上次更新的一個Banner的輪播循環,經過測試,發現存在一定的問題,就是當用戶手動切換banner圖片的時候,banner的計時沒有重新開始。
具體說一下問題所在,設置3s更換一張banner,如果我在2s的時候手動滑動banner,到下一張banner的圖片,那麼只需要1s就對當前的圖片進行切換了,對於追求完美的程序員來說,是不可以忍受的,所以我查閱了多方資料,進行了修正更改。
具體分析原因的話就是因爲Thread線程,當你手動的時候,這個線程並沒有暫停結束,還在繼續運行着,所以當用戶手動滑動的話,線程還在繼續,3s的時間跑完繼續currentItem+1,導致問題的所在,我們現在要解決的問題就是當用戶滑動的時候,要將線程暫停,然後重新計時3s,重新開始運行這個線程。
原因分析完了,最新的這部分代碼,加了一個進度條來進行監控,可以讓我們的進度條跟隨着圖片一起進行切換,圖片切換,進度條走滿,手動切換圖片,進度條重新開始走,並且圖片計時重新開始3s的計時。
廢話了這麼多了,上ImageHandler的代碼吧:
import android.os.Handler; import android.os.Message; import java.lang.ref.WeakReference; /** * Created by Administrator on 2016/8/12 0012. */ public class ImageHandler extends Handler { /** * 請求更新顯示的View */ protected static final int MSG_UPDATE_IMAGE = 1; /** * 請求暫停輪播 */ protected static final int MSG_KEEP_SILENT = 2; /** * 請求恢復輪播 */ protected static final int MSG_BREAK_SILENT = 3; /** * 記錄最新的頁號,當用戶手動滑動時需要記錄新頁號,否則會使輪播的頁面出錯。 * 例如當前如果在第一頁,本來準備播放的是第二頁,而這時候用戶滑動到了末頁, * 則應該播放的是第一頁,如果繼續按照原來的第二頁播放,則邏輯上有問題。 */ protected static final int MSG_PAGE_CHANGED = 4; //輪播間隔時間 protected static final long MSG_DELAY = 6000; //使用弱引用避免Handler泄露.這裏的泛型參數可以不是Activity,也可以是Fragment等 private WeakReference<IndexActivity> weakReference; private int currentItem = 0; protected ImageHandler(WeakReference<IndexActivity> wk) { weakReference = wk; } @Override public void handleMessage(Message msg) { super.handleMessage(msg); final IndexActivity activity = weakReference.get(); if (activity == null) { //Activity已經回收,無需再處理UI了 return; } //檢查消息隊列並移除未發送的消息,這主要是避免在複雜環境下消息出現重複等問題。 if (activity.handler.hasMessages(MSG_UPDATE_IMAGE)) { activity.handler.removeMessages(MSG_UPDATE_IMAGE); } switch (msg.what) { case MSG_UPDATE_IMAGE: currentItem++; activity.viewPager.setCurrentItem(currentItem); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i <= 100; i++) { if (Onn.isOff) { activity.progressBar.setProgress(0); i=0; Onn.isOff = false; continue; } activity.progressBar.setProgress(i); try { Thread.sleep(60); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); //準備下次播放 activity.handler.sendEmptyMessageDelayed(MSG_UPDATE_IMAGE, MSG_DELAY); break; case MSG_KEEP_SILENT: //只要不發送消息就暫停了 break; case MSG_BREAK_SILENT: activity.handler.sendEmptyMessageDelayed(MSG_UPDATE_IMAGE, MSG_DELAY); break; case MSG_PAGE_CHANGED: //記錄當前的頁號,避免播放的時候頁面顯示不正確。 currentItem = msg.arg1; break; default: break; } } }個人重點記錄一下線程中的東西,其他地方的代碼很好理解。以後可以參考使用。
線程中的Onn.isOff,是我們自己定義的一個全局變量。
/** * Created by Administrator on 2016/8/12 0012. */ public class Onn { public static boolean isOff = false; }這個全局變量是判斷我們的線程狀態。默認狀態是false。for循環中,走的就是我們的進度條的一個動態效果。但是手動切換圖片的話,我們的進度條要歸零,並且從0開始重新走。這是我們的需求。實現的邏輯就是,當我們手動切換圖片的時候,isOff設爲true,然後將進度條歸零,再將isOff設爲false,continue,就繼續開始走我們的進度條了。如果正常狀態,沒有切換,那我們就不走if,直接走我們的進度條啦。
具體的activity中的操作代碼如下:
ImageHandler handler = new ImageHandler(new WeakReference<IndexActivity>(this));
LayoutInflater inflater = LayoutInflater.from(this); ImageView view1 = (ImageView) inflater.inflate(R.layout.item_viewpager, null); ImageView view2 = (ImageView) inflater.inflate(R.layout.item_viewpager, null); ImageView view3 = (ImageView) inflater.inflate(R.layout.item_viewpager, null); view1.setImageResource(R.mipmap.banner_test); view2.setImageResource(R.mipmap.banner_test); view3.setImageResource(R.mipmap.banner_test); ArrayList<ImageView> views = new ArrayList<ImageView>(); views.add(view1); views.add(view2); views.add(view3); viewPager.setAdapter(new ImageAdapter(views)); viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { //配合Adapter的currentItem字段進行設置。 @Override public void onPageSelected(int arg0) { handler.sendMessage(Message.obtain(handler, ImageHandler.MSG_PAGE_CHANGED, arg0, 0)); } @Override public void onPageScrolled(int arg0, float arg1, int arg2) { } //覆寫該方法實現輪播效果的暫停和恢復 @Override public void onPageScrollStateChanged(int arg0) { switch (arg0) { case ViewPager.SCROLL_STATE_DRAGGING: handler.sendEmptyMessage(ImageHandler.MSG_KEEP_SILENT); Onn.isOff = true; break; case ViewPager.SCROLL_STATE_IDLE: handler.sendEmptyMessageDelayed(ImageHandler.MSG_UPDATE_IMAGE, ImageHandler.MSG_DELAY); break; default: break; } } }); viewPager.setCurrentItem(0);//默認在中間,使用戶看不到邊界 new Thread(new Runnable() { @Override public void run() { for (int i = 0; i <= 100; i++) { progressBar.setProgress(i); try { Thread.sleep(60); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); //開始輪播效果 handler.sendEmptyMessageDelayed(ImageHandler.MSG_UPDATE_IMAGE, ImageHandler.MSG_DELAY);
這裏爲什麼又單獨寫了一個Thread線程來走我們的進度條呢,如果按照上述的操作完,你會發現,進來以後的第一張圖片是沒有進度條的,所以剛進到這個頁面我們就需要單獨跑一起這個進度條。
還有我們的ImageViewAdapter:
import android.support.v4.view.PagerAdapter; import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; import android.widget.ImageView; import java.util.ArrayList; /** * Created by Administrator on 2016/8/10 0010. */ public class ImageAdapter extends PagerAdapter { private ArrayList<ImageView> viewlist; public ImageAdapter(ArrayList<ImageView> viewlist) { this.viewlist = viewlist; } @Override public int getCount() { //設置成最大,使用戶看不到邊界 return Integer.MAX_VALUE; } @Override public boolean isViewFromObject(View arg0, Object arg1) { return arg0==arg1; } @Override public void destroyItem(ViewGroup container, int position, Object object) { //Warning:不要在這裏調用removeView } @Override public Object instantiateItem(ViewGroup container, int position) { //對ViewPager頁號求模取出View列表中要顯示的項 position %= viewlist.size(); if (position<0){ position = viewlist.size()+position; } ImageView view = viewlist.get(position); //如果View已經在之前添加到了一個父組件,則必須先remove,否則會拋出IllegalStateException。 ViewParent vp =view.getParent(); if (vp!=null){ ViewGroup parent = (ViewGroup)vp; parent.removeView(view); } container.addView(view); //add listeners here if necessary return view; } }
這樣就實現了我們的循環輪播加進度條監控的效果了。大家可以根據自己的需求進行修改,如果不需要進度條的可以自己將Thread的代碼刪除就可以單獨的實現輪播循環的banner。我主要還是爲了記錄一下自己,所以加上了這個進度條的效果狀態。
如果轉載,請標明出處。謝謝。