国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Android > 正文

Android實(shí)現(xiàn)一個(gè)絲滑的自動(dòng)輪播控件實(shí)例代碼

2019-10-21 21:41:50
字體:
供稿:網(wǎng)友

前言

現(xiàn)在很多的 App 都有自動(dòng)輪播的 banner 界面,用于展示廣告圖片或者顯示當(dāng)前比較熱門的一些活動(dòng),除了具備比較酷炫的效果之外,通過輪播的方式來減少對(duì)界面的占用,也是很贊的一個(gè)設(shè)計(jì)點(diǎn)。本文主要是總結(jié)自動(dòng)輪播控件的實(shí)現(xiàn)過程,以及對(duì)這類控件的一些優(yōu)化的技巧。

一、如何實(shí)現(xiàn)

在開始進(jìn)行我們的代碼編程之前,我們先要思考一下,在 Google 提供的官方 Api 里面,有沒有類似的控件實(shí)現(xiàn)了相似的功能,畢竟官方的控件大都經(jīng)過了時(shí)間的考驗(yàn),無論是穩(wěn)定性還是性能方面都是非常不錯(cuò)的,如果我們能夠基于官方的控件進(jìn)行相應(yīng)的改造,控件的穩(wěn)定性也會(huì)有相對(duì)的保障。

在比較常見的主流控件里面,其實(shí) ViewPager 和 RecyclerView 已經(jīng)實(shí)現(xiàn)了類似的功能,尤其是 ViewPager,可以說是已經(jīng)實(shí)現(xiàn)了我們這個(gè)控件的大部分功能,所以如果我們基于 ViewPager 來進(jìn)行改造的話,也能讓我們的輪播控件更加穩(wěn)定。

那 ViewPager 跟我們需要的自動(dòng)輪播控件有多少差距呢,主要有兩個(gè):

  • 不支持自動(dòng)播放
  • 無法從最后一張滑動(dòng)到第一張

所以我們主要是針對(duì)這兩部分進(jìn)行相應(yīng)的改造,從而實(shí)現(xiàn)我們自己的自動(dòng)輪播控件。

1.1 實(shí)現(xiàn)自動(dòng)輪播功能

要想實(shí)現(xiàn)自動(dòng)輪播功能,我們最先想到的應(yīng)該是通過 Timer 或者 ScheduledExecutorService 來實(shí)現(xiàn)計(jì)時(shí)器的功能,然后讓 ViewPager 通過 serCurrentItem(int position) 方法,將當(dāng)前的 Item 設(shè)置為下一個(gè) position 的數(shù)據(jù),但是如果通過定時(shí)器來實(shí)現(xiàn)的話,會(huì)有一個(gè)問題,那就是我們?cè)谛枰?banner 進(jìn)行停止播放的時(shí)候就比較麻煩,所以通過 Handler 用 sendMessage 的形式,進(jìn)行事件的發(fā)送實(shí)現(xiàn) ViewPager 的自動(dòng)輪播,以及某些場(chǎng)景的停止是比較合理的。

我們來看下代碼:

 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,然后通過弱引用的形式防止內(nèi)存泄露,通過在 handlerMessage() 方法里面,調(diào)用 setCurrentItem() 方法,將當(dāng)前 ViewPager 的 Item 設(shè)置為對(duì)應(yīng)的 position + 1 的數(shù)據(jù),所以我們只要在外部調(diào)用 Handler 的 sendMessage() 方法,就能使 ViewPager 進(jìn)行自動(dòng)的無限輪播。

1.2 讓 ViewPager 從最后一張滑動(dòng)到第一張

我們知道,ViewPager 是無法從最后一頁滑動(dòng)到第一頁的,但我們可以換一個(gè)思路,如果我們?cè)?ViewPager 的 Adapter 里面,通過 getCount() 方法將 ViewPager 的大小設(shè)置為無限大,然后通過取余的方式來保證滑動(dòng)的頁面一直對(duì)應(yīng)數(shù)據(jù)源的那幾個(gè)數(shù)據(jù),這樣便能讓 ViewPager 實(shí)現(xiàn)從最后一張滑動(dòng)到第一張的效果。

 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; }

二、如何進(jìn)行優(yōu)化

在上面我們只是簡(jiǎn)單的實(shí)現(xiàn)了 ViewPager 的自動(dòng)輪播功能,但其實(shí)還有很多的細(xì)節(jié)需要我們進(jìn)行優(yōu)化,例如:我們是通過將 ViewPager 的大小設(shè)置為無限大的方式,來實(shí)現(xiàn)從最后一張滑動(dòng)到第一張的,但這時(shí)候如果不進(jìn)行緩存的話,我們?cè)?Adapter 的 instantiateItem(ViewGroup container, final int position) 方法里面,便需要返回很多新 new 出來的 View,這樣會(huì)造成不必要的內(nèi)存浪費(fèi),只有對(duì)這些細(xì)節(jié)進(jìn)行優(yōu)化,才能讓我們的控件更加的好用,穩(wěn)定性和性能方面也會(huì)更加優(yōu)異。

2.1 通過緩存減少內(nèi)存浪費(fèi)

為了讓 ViewPager 能實(shí)現(xiàn)無線輪播的功能,我們是使用了通過將 getCount() 的大小設(shè)置為無限大的方式來實(shí)現(xiàn)的,但這會(huì)產(chǎn)生一個(gè)問題,這樣會(huì)使我們?cè)?Adapter 的 instantiateItem() 方法中返回很多新 new 出來的 View,而造成不必要的內(nèi)存浪費(fèi)。

所以我們可以通過一個(gè) List 作為緩存池,在 Adapter 中的 destroyItem() 方法中將廢棄的 object 存到緩存池中,重復(fù)利用,這樣便能避免內(nèi)存浪費(fèi)。

 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 中取出已經(jīng)被緩存的 View,進(jìn)行重復(fù)利用

 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 {    // 當(dāng)緩存集合有數(shù)據(jù)時(shí),進(jìn)行復(fù)用    imageView = (ImageView) mViewCaches.remove(0);   } }

2.2 適當(dāng)?shù)耐V棺詣?dòng)輪播

當(dāng)我們觸摸 Banner 或者離開當(dāng)前展示 Banner 的頁面時(shí),如果 banner 還在不停的進(jìn)行無線輪播的話,會(huì)造成沒必要的性能損失,所以我們需要在觸摸 Banner 以及當(dāng)前的 Activity 為不可見狀態(tài)的時(shí)候,停止 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 在進(jìn)行自動(dòng)輪播的時(shí)候,切換速度是特別快的,會(huì)給人一種很突兀的感覺,而且 ViewPager 也沒有提供接口給我們對(duì) ViewPager 進(jìn)行切換速度的設(shè)置,所以我們需要通過反射的方式,使用 Scroller 來進(jìn)行切換速度的設(shè)置,從而讓我們的 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); }}

至此,我們的自動(dòng)輪播控件,無論是性能上還是穩(wěn)定性上都已經(jīng)很不錯(cuò)了。

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)VEVB武林網(wǎng)的支持。 


注:相關(guān)教程知識(shí)閱讀請(qǐng)移步到Android開發(fā)頻道。
發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 台东县| 三明市| 卓尼县| 略阳县| 抚州市| 饶河县| 吴忠市| 木兰县| 云和县| 大足县| 油尖旺区| 秦安县| 鄂温| 仙游县| 锦屏县| 平江县| 海兴县| 东至县| 中阳县| 屏山县| 三门峡市| 北辰区| 铜陵市| 阿拉善左旗| 双峰县| 密云县| 布尔津县| 上饶县| 沙雅县| 莒南县| 皮山县| 廊坊市| 同德县| 历史| 云南省| 依安县| 望都县| 龙南县| 德清县| 赞皇县| 玛沁县|