前言
首先我們來看一下運行的效果,如下所示:
這是在我們的項目中經常會用到的圖片輪播效果,一般用于廣告圖片的展示。
它要求主要實現以下功能:
1)自動播放;
2)無限滑動;
3)手指拖拽圖片時暫停自動輪播,松開后繼續自動輪播;
4)含動畫效果的小圓點指示器。
本文將介紹利用ViewPager實現無限輪播圖片,圖片下方加上小圓點指示器標記當前位置,并利用Handler實現自動輪播圖片。
正文
1、實現自動播放
思路:沒隔一段時間讓ViewPager更換一次頁卡,使用Handler來實現。
1)標記是否自動播放
private boolean isAutoPlay;
2)每隔一段時間使用Handler發送一次更換頁卡的任務
// 如果少于2張就不用自動播放了
if (count < 2) {
isAutoPlay = false;
} else {
isAutoPlay = true;
handler = new Handler();
handler.postDelayed(task, delay);
}
3)在任務中每隔一段時間再次發送任務,這樣循環發送就實現了自動播放的效果。
private Runnable task = new Runnable() {
@Override
public void run() {
if (isAutoPlay) {
// 位置循環
currentItem = currentItem % (count + 1) + 1;
// 正常每隔3秒播放一張圖片
vpImageTitle.setCurrentItem(currentItem);
handler.postDelayed(task, delay);
} else {
// 如果處于拖拽狀態停止自動播放,會每隔5秒檢查一次是否可以正常自動播放。
handler.postDelayed(task, 5000);
}
}
};
2、實現無限滑動
思路:設置頁卡視圖列表時,在前后額外各加一個頁卡。最前面加最后一張圖片,最后面加第1張圖片。然后每當切換到最前的頁卡時,就替換成倒數第2個頁卡;每當切換到最后的頁卡時,就替換成第2個頁卡。這樣一來就形成了連貫,自然實現了無限滑動的功能。
1)設置ViewPager的視圖列表時,在前后各加一個頁卡。
for (int i = 0; i < count + 2; i++) {
if (i == 0) {// 將最前面一頁設置成本來最后的那頁
Glide.with(context).
load(imageTitleBeanList.get(count - 1).getImageUrl()).into(ivImage);
tvTitle.setText(imageTitleBeanList.get(count - 1).getTitle());
} else if (i == count + 1) {// 將最后面一頁設置成本來最前的那頁
Glide.with(context).
load(imageTitleBeanList.get(0).getImageUrl()).into(ivImage);
tvTitle.setText(imageTitleBeanList.get(0).getTitle());
} else {
Glide.with(context).
load(imageTitleBeanList.get(i - 1).getImageUrl()).into(ivImage);
tvTitle.setText(imageTitleBeanList.get(i - 1).getTitle());
}
// 將設置好的View添加到View列表中
viewList.add(view);
}
2)在監聽ViewPager的頁卡狀態改變中,當滑動到第1個頁卡時替換成倒數第2個頁卡;當滑動到最后一個頁卡時替換成第2個頁卡。
@Override
public void onPageScrollStateChanged(int state) {
switch (state) {
// 閑置中
case ViewPager.SCROLL_STATE_IDLE:
// “偷梁換柱”
if (vpImageTitle.getCurrentItem() == 0) {
vpImageTitle.setCurrentItem(count, false);
} else if (vpImageTitle.getCurrentItem() == count + 1) {
vpImageTitle.setCurrentItem(1, false);
}
currentItem = vpImageTitle.getCurrentItem();
break;
}
}
3、手指滑動圖片時停止自動播放
思路:使用一個標記來控制是否自動播放。
1)聲明一個boolean變量,用來標記是否播放。
private boolean isAutoPlay;
2)默認是自動播放,但當圖片少于2張時不自動播放。
private void starPlay() {
// 如果少于2張就不用自動播放了
if (count < 2) {
isAutoPlay = false;
} else {
isAutoPlay = true;
handler.postDelayed(task, delay);
}
}
3)根據標記判斷是否切換頁卡
private Runnable task = new Runnable() {
@Override
public void run() {
if (isAutoPlay) {
// 正常每隔3秒播放一張圖片
vpImageTitle.setCurrentItem(currentItem);
handler.postDelayed(task, delay);
} else {
// 如果處于拖拽狀態停止自動播放,會每隔5秒檢查一次是否可以正常自動播放。
handler.postDelayed(task, 5000);
}
}
};
4)在監聽ViewPager的頁卡狀態改變中,如果是拖動狀態就不切換頁卡。
@Override
public void onPageScrollStateChanged(int state) {
switch (state) {
// 閑置中
case ViewPager.SCROLL_STATE_IDLE:
isAutoPlay = true;
break;
// 拖動中
case ViewPager.SCROLL_STATE_DRAGGING:
isAutoPlay = false;
break;
// 設置中
case ViewPager.SCROLL_STATE_SETTLING:
isAutoPlay = true;
break;
}
}
4、自定義指示器
思路:使用一個LinearLayout作為容器,然后根據圖片的數量向容器中不斷添加繪制的小圓點,另外再設置變大變小的屬性動畫用于動畫效果。監聽ViewPager的頁卡,每當切換到一個頁卡時就將切換對應狀態的小圓點,并且設置相應的動畫效果。
1)繪制小圓點
未選中狀態,灰色的圓。
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<size
android:width="32dp"
android:height="32dp"/>
<solid android:color="#9e9e9e"/>
</shape>
選中狀態,白色的圓。
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<size
android:width="32dp"
android:height="32dp"/>
<solid android:color="#ecf0f1"/>
</shape>
2)屬性動畫
變大
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="150"
android:pivotX="50%"
android:pivotY="50%"
android:propertyName="scaleX"
android:valueFrom="1.0"
android:valueTo="1.5"
android:valueType="floatType"/>
<objectAnimator
android:duration="150"
android:pivotX="50%"
android:pivotY="50%"
android:propertyName="scaleY"
android:valueFrom="1.0"
android:valueTo="1.5"
android:valueType="floatType"/>
</set>
變小
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="150"
android:pivotX="50%"
android:pivotY="50%"
android:propertyName="scaleX"
android:valueFrom="1.5"
android:valueTo="1.0"
android:valueType="floatType"/>
<objectAnimator
android:duration="150"
android:pivotX="50%"
android:pivotY="50%"
android:propertyName="scaleY"
android:valueFrom="1.5"
android:valueTo="1.0"
android:valueType="floatType"/>
</set>
3)設置指示器
先是統一設置屬性并添加到容器中,然后默認第1個小圓點為選中狀態。選中狀態的小圓點顏色由灰色變成白色,并且變大。
private void setIndicator() {
isLarge = new SparseBooleanArray();
// 記得創建前先清空數據,否則會受遺留數據的影響。
llDot.removeAllViews();
for (int i = 0; i < count; i++) {
View view = new View(context);
view.setBackgroundResource(R.drawable.dot_unselected);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(dotSize, dotSize);
layoutParams.leftMargin = dotSpace / 2;
layoutParams.rightMargin = dotSpace / 2;
layoutParams.topMargin = dotSpace / 2;
layoutParams.bottomMargin = dotSpace / 2;
llDot.addView(view, layoutParams);
isLarge.put(i, false);
}
llDot.getChildAt(0).setBackgroundResource(R.drawable.dot_selected);
animatorToLarge.setTarget(llDot.getChildAt(0));
animatorToLarge.start();
isLarge.put(0, true);
}
4)監聽頁卡
當頁卡被選中時,相應的小圓點顏色由灰色變成白色,并且變大;之前的小圓點顏色由白色變成灰色,并且變小。
@Override
public void onPageSelected(int position) {
// 遍歷一遍子View,設置相應的背景。
for (int i = 0; i < llDot.getChildCount(); i++) {
if (i == position - 1) {// 被選中
llDot.getChildAt(i).setBackgroundResource(R.drawable.dot_selected);
if (!isLarge.get(i)) {
animatorToLarge.setTarget(llDot.getChildAt(i));
animatorToLarge.start();
isLarge.put(i, true);
}
} else {// 未被選中
llDot.getChildAt(i).setBackgroundResource(R.drawable.dot_unselected);
if (isLarge.get(i)) {
animatorToSmall.setTarget(llDot.getChildAt(i));
animatorToSmall.start();
isLarge.put(i, false);
}
}
}
}
源碼地址
ImageSlideshow