您好,登錄后才能下訂單哦!
前言
現在很多的 App 都有自動輪播的 banner 界面,用于展示廣告圖片或者顯示當前比較熱門的一些活動,除了具備比較酷炫的效果之外,通過輪播的方式來減少對界面的占用,也是很贊的一個設計點。本文主要是總結自動輪播控件的實現過程,以及對這類控件的一些優化的技巧。
一、如何實現
在開始進行我們的代碼編程之前,我們先要思考一下,在 Google 提供的官方 Api 里面,有沒有類似的控件實現了相似的功能,畢竟官方的控件大都經過了時間的考驗,無論是穩定性還是性能方面都是非常不錯的,如果我們能夠基于官方的控件進行相應的改造,控件的穩定性也會有相對的保障。
在比較常見的主流控件里面,其實 ViewPager 和 RecyclerView 已經實現了類似的功能,尤其是 ViewPager,可以說是已經實現了我們這個控件的大部分功能,所以如果我們基于 ViewPager 來進行改造的話,也能讓我們的輪播控件更加穩定。
那 ViewPager 跟我們需要的自動輪播控件有多少差距呢,主要有兩個:
所以我們主要是針對這兩部分進行相應的改造,從而實現我們自己的自動輪播控件。
1.1 實現自動輪播功能
要想實現自動輪播功能,我們最先想到的應該是通過 Timer 或者 ScheduledExecutorService 來實現計時器的功能,然后讓 ViewPager 通過 serCurrentItem(int position) 方法,將當前的 Item 設置為下一個 position 的數據,但是如果通過定時器來實現的話,會有一個問題,那就是我們在需要讓 banner 進行停止播放的時候就比較麻煩,所以通過 Handler 用 sendMessage 的形式,進行事件的發送實現 ViewPager 的自動輪播,以及某些場景的停止是比較合理的。
我們來看下代碼:
private static class AutoScrollHandler extends Handler { private WeakReference<AutoScrollViewPager> mBannerRef; private static final int MSG_CHANGE_SELECTION = 1; AutoScrollHandler(AutoScrollViewPager autoScrollViewPager) { mBannerRef = new WeakReference<>(autoScrollViewPager); } private void start() { removeMessages(MSG_CHANGE_SELECTION); sendEmptyMessageDelayed(MSG_CHANGE_SELECTION, AUTO_SCROLL_TIME); } private void stop() { removeMessages(MSG_CHANGE_SELECTION); } @Override public void handleMessage(Message msg) { if (msg.what == MSG_CHANGE_SELECTION) { if (mBannerRef == null || mBannerRef.get() == null) { return; } AutoScrollViewPager banner = mBannerRef.get(); if (banner.mSelectedIndex == Integer.MAX_VALUE) { int rightPos = banner.mSelectedIndex % banner.mBannerList.size(); banner.setCurrentItem(banner.getInitPosition() + rightPos + 1, true); } else { if (!hasMessages(MSG_CHANGE_SELECTION)) { banner.setCurrentItem(banner.mSelectedIndex + 1, true); sendEmptyMessageDelayed(MSG_CHANGE_SELECTION, AUTO_SCROLL_TIME); } } } } }
可以看到,我們先傳入外部的 ViewPager,然后通過弱引用的形式防止內存泄露,通過在 handlerMessage() 方法里面,調用 setCurrentItem() 方法,將當前 ViewPager 的 Item 設置為對應的 position + 1 的數據,所以我們只要在外部調用 Handler 的 sendMessage() 方法,就能使 ViewPager 進行自動的無限輪播。
1.2 讓 ViewPager 從最后一張滑動到第一張
我們知道,ViewPager 是無法從最后一頁滑動到第一頁的,但我們可以換一個思路,如果我們在 ViewPager 的 Adapter 里面,通過 getCount() 方法將 ViewPager 的大小設置為無限大,然后通過取余的方式來保證滑動的頁面一直對應數據源的那幾個數據,這樣便能讓 ViewPager 實現從最后一張滑動到第一張的效果。
public int getCount() { if (mBannerList == null) { return 0; } if (mBannerList.size() == 1) { return 1; } else { return Integer.MAX_VALUE; } }
public Object instantiateItem(ViewGroup container, final int position) { if (mBannerList != null && mBannerList.size() > 0) { View imageView = null; Uri uri = Uri.parse(mBannerList.get(position % mBannerList.size()).cover_url); // 通過取余的方式 imageView = new SimpleDraweeView(mContext); ((SimpleDraweeView) imageView).setImageURI(uri); container.addView(imageView); return imageView; } return null; }
二、如何進行優化
在上面我們只是簡單的實現了 ViewPager 的自動輪播功能,但其實還有很多的細節需要我們進行優化,例如:我們是通過將 ViewPager 的大小設置為無限大的方式,來實現從最后一張滑動到第一張的,但這時候如果不進行緩存的話,我們在 Adapter 的 instantiateItem(ViewGroup container, final int position) 方法里面,便需要返回很多新 new 出來的 View,這樣會造成不必要的內存浪費,只有對這些細節進行優化,才能讓我們的控件更加的好用,穩定性和性能方面也會更加優異。
2.1 通過緩存減少內存浪費
為了讓 ViewPager 能實現無線輪播的功能,我們是使用了通過將 getCount() 的大小設置為無限大的方式來實現的,但這會產生一個問題,這樣會使我們在 Adapter 的 instantiateItem() 方法中返回很多新 new 出來的 View,而造成不必要的內存浪費。
所以我們可以通過一個 List 作為緩存池,在 Adapter 中的 destroyItem() 方法中將廢棄的 object 存到緩存池中,重復利用,這樣便能避免內存浪費。
private final ArrayList<View> mViewCaches = new ArrayList<>(); @Override public void destroyItem(ViewGroup container, int position, Object object) { ImageView imageView = (ImageView) object; container.removeView(imageView); mViewCaches.add(imageView); }
然后在 Adapter 的 instantiateItem() 方法中,從 List 中取出已經被緩存的 View,進行重復利用
public Object instantiateItem(ViewGroup container, final int position) { if (mBannerList != null && mBannerList.size() > 0) { View imageView = null; Uri uri = Uri.parse(mBannerList.get(position % mBannerList.size()).cover_url); if (mViewCaches.isEmpty()) { imageView = new SimpleDraweeView(GlobalContext.getContext()); } else { // 當緩存集合有數據時,進行復用 imageView = (ImageView) mViewCaches.remove(0); } }
2.2 適當的停止自動輪播
當我們觸摸 Banner 或者離開當前展示 Banner 的頁面時,如果 banner 還在不停的進行無線輪播的話,會造成沒必要的性能損失,所以我們需要在觸摸 Banner 以及當前的 Activity 為不可見狀態的時候,停止 Banner 的輪播,從而提升性能。
public boolean dispatchTouchEvent(MotionEvent ev) { int action = ev.getAction(); if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_OUTSIDE) { startAutoPlay(); } else if (action == MotionEvent.ACTION_DOWN) { stopAutoPlay(); } return super.dispatchTouchEvent(ev); }
2.3 改變 ViewPager 切換速度
原生的 ViewPager 在進行自動輪播的時候,切換速度是特別快的,會給人一種很突兀的感覺,而且 ViewPager 也沒有提供接口給我們對 ViewPager 進行切換速度的設置,所以我們需要通過反射的方式,使用 Scroller 來進行切換速度的設置,從而讓我們的 Banner 更加的絲滑。
public AutoScrollViewPager(Context context) { this(context, null); initViewPagerScroll(); } private void initViewPagerScroll() { try { Field mField = ViewPager.class.getDeclaredField("mScroller"); mField.setAccessible(true); BannerScroller scroller = new BannerScroller(getContext()); mField.set(this, scroller); } catch (Exception e) { Log.d(TAG, e.getMessage()); } }
public class BannerScroller extends Scroller { private static final int BANNER_DURATION = 1000; private int mDuration = BANNER_DURATION; public BannerScroller(Context context) { super(context); } @Override public void startScroll(int startX, int startY, int dx, int dy, int duration) { super.startScroll(startX, startY, dx, dy, mDuration); } }
至此,我們的自動輪播控件,無論是性能上還是穩定性上都已經很不錯了。
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對億速云的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。