自己造輪子--一款實(shí)用的Android廣告欄實(shí)現(xiàn)過程(一)

開源界有一句很有名的話叫“不要重復(fù)發(fā)明輪子”,當(dāng)然,我今天的觀點(diǎn)不是要反駁這句話,輪子理論給我們的開發(fā)帶來了極大的便利,項(xiàng)目中要實(shí)現(xiàn)一些功能,便去網(wǎng)上找找,一般推薦使用一些有名的庫(kù),我本身也是這么做的,但我想說的是,既要會(huì)用輪子,也要知道輪子怎么造,必要的時(shí)候,自己也要造輪子(想要找到一個(gè)完全滿意的輪子還是不大容易的)。

由來

之前項(xiàng)目里面都是用的daimajia的AndroidImageSlider,一開始被驚艷的動(dòng)畫切換效果吸引了,還有各種自定義屬性動(dòng)畫啥的,感覺很棒,但隨著項(xiàng)目的進(jìn)展和時(shí)間的推移,我慢慢發(fā)現(xiàn)它也不是無所不能的,甚至我發(fā)現(xiàn)它只是好看,卻不怎么實(shí)用,我列舉一些我發(fā)現(xiàn)的問題:

  • 庫(kù)中集成了Picasso,Picasso是極其耗內(nèi)存的,這點(diǎn)我在之前的一篇為什么圖片加載我首先Glide 提到過,當(dāng)一個(gè)頁(yè)面既有廣告又有列表(列表中也一般有圖片)會(huì)造成頁(yè)面嚴(yán)重卡頓。
  • 動(dòng)畫效果太炫,實(shí)際項(xiàng)目中一個(gè)沒用到,一般用標(biāo)準(zhǔn)模式。
  • 太多的依賴包,現(xiàn)在的許多項(xiàng)目都是基于android4.0以上版本開發(fā)的,nineoldandroid已經(jīng)不需要了
  • 頁(yè)面點(diǎn)擊事件不直觀,如果有那種像ListView的OnItemClickListener就好了

總而言之,我感覺它現(xiàn)在已經(jīng)有些臃腫。當(dāng)然,我也在網(wǎng)上尋找更簡(jiǎn)潔實(shí)用的替代庫(kù),比如BGABanner-Android,但事實(shí)也是殘酷的,不支持網(wǎng)絡(luò)圖片,還有一個(gè)坑等著我跳,看代碼片段

if (mAutoPlayAble && views.size() < 3) { 
       throw new IllegalArgumentException("開啟指定輪播時(shí)至少有三個(gè)頁(yè)面"); 
} 

低于3張圖片會(huì)直接拋異常,這叫我怎么用,需求也不是我能控制的。
當(dāng)然以上兩個(gè)庫(kù)的作者我都很喜歡,這兩個(gè)庫(kù)也非常不錯(cuò),不然也不值得我花時(shí)間研究。

我對(duì)輪子的要求

結(jié)合自己的理解,我認(rèn)為兩個(gè)庫(kù)中都有可取之處,也有不足之處,我就取長(zhǎng)補(bǔ)短,造自己的輪子,我對(duì)這個(gè)輪子的要求:

  • 無限輪播
  • 自動(dòng)加手動(dòng)滑動(dòng)
  • 簡(jiǎn)單的自定義指示器樣式及位置
  • 支持本地圖片及網(wǎng)絡(luò)圖片
  • 滑動(dòng)流暢,無卡頓,無閃爍
  • 廣告頁(yè)面不限制個(gè)數(shù)
  • 頁(yè)面點(diǎn)擊監(jiān)聽事件
  • 簡(jiǎn)單易用,高配置,無明顯bug
    動(dòng)畫效果我先打算拋棄了,默認(rèn)的就好,以實(shí)用為主。

開始動(dòng)手,step by step

系統(tǒng)可以滑動(dòng)翻頁(yè)的控件就只有ViewPager和ViewFliper網(wǎng)上還有大神實(shí)現(xiàn)用RecyclerView實(shí)現(xiàn)了類似ViewPager的效果,這里暫不做過多研究,這里就選擇使用最多ViewPager作為滑動(dòng)翻頁(yè)控件,使用ViewPager+PagerAdapter可以很容易的實(shí)現(xiàn)翻頁(yè)切換效果,但存在幾個(gè)弊端:

  • 不能無限輪回的翻頁(yè)(滑到第一個(gè)或者最后一個(gè)就不能繼續(xù)滑)
  • 切換速度太快,系統(tǒng)默認(rèn)250毫秒,用做廣告欄切換會(huì)存在明顯閃爍
  • 僅支持手動(dòng)滑動(dòng),不支持自動(dòng)切換
  • 沒有提供直接的類似ListView的OnItemClickListener監(jiān)聽,使用起來很不方便
    當(dāng)然還會(huì)有其他的坑,遇到了再解決,先解決上面的問題

無限輪播效果

這里采用網(wǎng)上通用的解決辦法,偽輪播(讓用戶看到輪播的假象,實(shí)際上用了很多頁(yè)面在不斷重復(fù)出現(xiàn),如果用戶滑動(dòng)幾十億下是可以滑到頭,實(shí)際幾乎不可能有人這么做)看具體實(shí)現(xiàn)代碼

  public class LoopPagerAdapter extends PagerAdapter {
        private List<View> views;

        public LoopPagerAdapter(List<View> views) {
            this.views = views;
        }

        @Override
        public int getCount() {
            //Integer.MAX_VALUE = 2147483647
            return Integer.MAX_VALUE;
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            if (views.size() > 0) {
                //position % view.size()是指虛擬的position會(huì)在[0,view.size())之間循環(huán)
                View view = views.get(position % views.size());
                if (container.equals(view.getParent())) {
                    container.removeView(view);
                }
                container.addView(view);
                return view;
            }
            return null;
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
        }
    }~~~  
還需要做一件事情,就是設(shè)置當(dāng)前position為中間的一個(gè)較大的值,如果不設(shè)置或者設(shè)置的比較小,往左滑動(dòng)容易滑倒頭

pager.setCurrentItem(Integer.MAX_VALUE / 2 - Integer.MAX_VALUE / 2 % views.size());

####改變?cè)鶹iewPager切換速度
通過反射拿到ViewPager的滑動(dòng)器mScroller,改變duration參數(shù),看代碼:

public void setSliderTransformDuration(int duration) {
try {
Field mScroller = ViewPager.class.getDeclaredField("mScroller");
mScroller.setAccessible(true);
FixedSpeedScroller scroller = new FixedSpeedScroller(pager.getContext(), null, duration);
mScroller.set(pager, scroller);
} catch (Exception e) {

    }
}

//FixedSpeedScroller.java
public class FixedSpeedScroller extends Scroller {
//默認(rèn)1秒,可以通過上面的方法控制
private int mDuration = 1000;

public FixedSpeedScroller(Context context) {
    super(context);
}

public FixedSpeedScroller(Context context, Interpolator interpolator) {
    super(context, interpolator);
}

public FixedSpeedScroller(Context context, Interpolator interpolator, int duration){
    this(context,interpolator);
    mDuration = duration;
}

@Override
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
    // Ignore received duration, use fixed one instead
    super.startScroll(startX, startY, dx, dy, mDuration);
}

@Override
public void startScroll(int startX, int startY, int dx, int dy) {
    // Ignore received duration, use fixed one instead
    super.startScroll(startX, startY, dx, dy, mDuration);
}

}

####自動(dòng)切換實(shí)現(xiàn)
這里可以有多種方式,使用Handler或者Timer都可以的,這里采用handler實(shí)現(xiàn),isAutoPlay可以控制是否禁止控件自動(dòng)輪播,autoPlayDuration是輪播間隔時(shí)間,還需注意觸摸時(shí)應(yīng)當(dāng)停止輪播,放開恢復(fù)正常

/**
* 開始自動(dòng)輪播
*/
public void startAutoPlay() {
if (isAutoPlay) {
handler.sendEmptyMessageDelayed(WHAT_AUTO_PLAY, autoPlayDuration);
}
}

/**
 * 停止自動(dòng)輪播
 */
public void stopAutoPlay() {
    if (isAutoPlay) {
        handler.removeMessages(WHAT_AUTO_PLAY);
    }
}

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            stopAutoPlay();
            break;
        case MotionEvent.ACTION_CANCEL:
        case MotionEvent.ACTION_UP:
            startAutoPlay();
            break;
    }
    return super.dispatchTouchEvent(ev);
}
先附上一張預(yù)覽圖:

![bannerLayoutDemo.gif](http://upload-images.jianshu.io/upload_images/697635-6e241cb677253652.gif?imageMogr2/auto-orient/strip)


先寫這么多吧,后續(xù)完整代碼我會(huì)上傳到github,如果大家有興趣,我會(huì)抽時(shí)間寫剩下的內(nèi)容,這個(gè)控件的代碼也是借鑒了很多優(yōu)秀的開源庫(kù),并結(jié)合自己的理解寫的。
> 炫麗的效果固然吸引人眼球,平凡實(shí)用的東西才愈久彌香

完整代碼已上傳到github:[BannerLayoutDemo](https://github.com/dongjunkun/BannerLayoutDemo)

下一篇[自己造輪子--一款實(shí)用的Android廣告欄實(shí)現(xiàn)過程(二)](http://m.itdecent.cn/p/12d45f01e99e)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,355評(píng)論 25 708
  • 內(nèi)容抽屜菜單ListViewWebViewSwitchButton按鈕點(diǎn)贊按鈕進(jìn)度條TabLayout圖標(biāo)下拉刷新...
    皇小弟閱讀 47,183評(píng)論 22 665
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,704評(píng)論 4 61
  • 今天去醫(yī)院看胃病,醫(yī)生二話不說開了胃鏡檢查給我。 兩個(gè)星期前我研究了一上午,最后想,如果做胃鏡,我一定要全麻。 結(jié)...
    三腳貓捉老鼠閱讀 844評(píng)論 0 0
  • 今天我去買花了,這些花是送給老師的,我一定覺得老師喜歡,最后就吃飯了。
    軒仔1111兮寶閱讀 289評(píng)論 0 1

友情鏈接更多精彩內(nèi)容