Android 輪播圖的實現
環境準備
開發環境
-
Android Studio 2.2.1
-
JDK1.7
-
API 24
-
Gradle 2.2.1
開發開始
先上效果預覽
效果預覽
案例分析
這個案例網上也很多, 質量參差不齊, 我也就根據自己的理解來分析分析需要實現的幾個功能點:
-
輪播圖有n張圖片和相對應的n個小圓點(指示器 indicator) 實現聯動
-
除了可以手動滑動外, 也可以自動滾動(輪播) 可以考慮使用Handler實現
-
實現無限輪回滾動
-
當手指按下圖片后不再自動滾動
根據上述分析進行開發
接下來搭建布局
activity_main.xml
public static final int MAX_SCROLL_VALUE = 10000;
private List<ImageView> mItems;
private Context mContext;
private LayoutInflater mInflater;
public MyPagerAdapter(List<ImageView> items, Context context) {
mContext = context;
mInflater = LayoutInflater.from(context);
mItems = items;
}
/**
* @param container
* @param position
* @return 對position進行求模操作
* 因為當用戶向左滑時position可能出現負值,所以必須進行處理
*/
@Override
public Object instantiateItem(ViewGroup container, int position) {
View ret = null;
//對ViewPager頁號求摸取出View列表中要顯示的項
position %= mItems.size();
Log.d("Adapter", "instantiateItem: position: " + position);
ret = mItems.get(position);
//如果View已經在之前添加到了一個父組件,則必須先remove,否則會拋出IllegalStateException。
ViewParent viewParent = ret.getParent();
if (viewParent != null) {
ViewGroup parent = (ViewGroup) viewParent;
parent.removeView(ret);
}
container.addView(ret);
return ret;
}
/**
* 由于我們在instantiateItem()方法中已經處理了remove的邏輯,
* 因此這里并不需要處理。實際上,實驗表明這里如果加上了remove的調用,
* 則會出現ViewPager的內容為空的情況。
*
* @param container
* @param position
* @param object
*/
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
//警告:不要在這里調用removeView, 已經在instantiateItem中處理了
}
@Override
public int getCount() {
int ret = 0;
if (mItems.size() > 0) {
ret = MAX_SCROLL_VALUE;
}
return ret;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == (View) object;
}
}</code></pre>
Note: 一定不要在destroyItem中再調用removeView了, 因為咱們已經instantiateItem中做了處理
在MainActivity.java中給ViewPager設置Adapter
mItems = new ArrayList<>();
mViewPager.setAdapter(mAdapter);
addImageView();
mAdapter.notifyDataSetChanged();</code></pre>
private void addImageView(){
ImageView view0 = new ImageView(this);
view0.setImageResource(R.mipmap.pic0);
ImageView view1 = new ImageView(this);
view1.setImageResource(R.mipmap.pic1);
ImageView view2 = new ImageView(this);
view2.setImageResource(R.mipmap.pic2);
view0.setScaleType(ImageView.ScaleType.CENTER_CROP);
view1.setScaleType(ImageView.ScaleType.CENTER_CROP);
view2.setScaleType(ImageView.ScaleType.CENTER_CROP);
mItems.add(view0);
mItems.add(view1);
mItems.add(view2);
}</code></pre>
Note: 因為咱們做的是Demo, 所以我們傳入的是一個ImageView的集合, 真正開發時, 需要傳入含有圖片url的實體類, 在Adapter中可以使用加載圖片的類庫加載
實現右下角指示器
添加指示器
在onCreate中添加
//獲取指示器(下面三個小點)
mBottomLiner = (LinearLayout) findViewById(R.id.live_indicator);
//右下方小圓點
mBottomImages = new ImageView[mItems.size()];
for (int i = 0; i < mBottomImages.length; i++) {
ImageView imageView = new ImageView(this);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(20, 20);
params.setMargins(5, 0, 5, 0);
imageView.setLayoutParams(params);
//如果當前是第一個 設置為選中狀態
if (i == 0) {
imageView.setImageResource(R.drawable.indicator_select);
} else {
imageView.setImageResource(R.drawable.indicator_no_select);
}
mBottomImages[i] = imageView;
//添加到父容器
mBottomLiner.addView(imageView);
}
實現聯動
添加ViewPager的監聽事件, 實現ViewPager.OnPageChangeListener接口
mViewPager.addOnPageChangeListener(this);
回調事件
///////////////////////////////////////////////////////////////////////////
// ViewPager的監聽事件
///////////////////////////////////////////////////////////////////////////
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
currentViewPagerItem = position;
if (mItems != null) {
position %= mBottomImages.length;
int total = mBottomImages.length;
for (int i = 0; i < total; i++) {
if (i == position) {
mBottomImages[i].setImageResource(R.drawable.indicator_select);
} else {
mBottomImages[i].setImageResource(R.drawable.indicator_no_select);
}
}
}
}
@Override
public void onPageScrollStateChanged(int state) {
}</code></pre>
實現自動滾動
在mBottomImages初始化之后 開啟一個線程 進行定時發送一個空消息給Handler處理, 由Handler決定切換到下一頁
//讓其在最大值的中間開始滑動, 一定要在 mBottomImages初始化之前完成
int mid = MyPagerAdapter.MAX_SCROLL_VALUE / 2;
mViewPager.setCurrentItem(mid);
currentViewPagerItem = mid;
//定時發送消息
mThread = new Thread(){
@Override
public void run() {
super.run();
while (true) {
mHandler.sendEmptyMessage(0);
try {
Thread.sleep(MainActivity.VIEW_PAGER_DELAY);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
mThread.start();</code></pre>
自定義Handler
///////////////////////////////////////////////////////////////////////////
// 為防止內存泄漏, 聲明自己的Handler并弱引用Activity
///////////////////////////////////////////////////////////////////////////
private static class MyHandler extends Handler {
private WeakReference<MainActivity> mWeakReference;
public MyHandler(MainActivity activity) {
mWeakReference = new WeakReference<MainActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 0:
MainActivity activity = mWeakReference.get();
if (activity.isAutoPlay) {
activity.mViewPager.setCurrentItem(++activity.currentViewPagerItem);
}
break;
}
}
}</code></pre>
Note: 其中isAutoPlay是一個用來判斷當前是否是自動輪播的boolean值變量, 主要用于實現我們接下來說的 當手指按下圖片后不再滾動
實現當手指按下圖片后不再滾動
思路: 我們可以考慮對ViewPager的觸摸事件進行監聽, 然后設置一個上節說到的isAutoPlay的boolean變量用來讓Handler判斷是否進行輪播滾動
代碼實現:
ViewPager設置監聽
mViewPager.setOnTouchListener(this);
事件回調
@Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
isAutoPlay = false;
break;
case MotionEvent.ACTION_UP:
isAutoPlay = true;
break;
}
return false;
}
注:細心的同學可能會看出來, 我們沒有單獨說 無限循環 如何實現, 其實, 它的實現已經隱藏在了代碼中, 在這里我簡單的說一下思路:
給ViewPager的條目個數設置個較大值, 該案例中為10000, 然后我們剛進入時選中的位置為 10000/2=5000, 也就是說我們可以向左或向右滑動約5000多張圖片, 但這是不現實的, 所以就給用戶造成了無限循環的假象
來自:http://www.jianshu.com/p/24b30a3d052f