Android viewPager實(shí)現(xiàn)banner輪播

用什么來(lái)實(shí)現(xiàn)輪播:

在比較常見(jiàn)的主流控件里面煌寇,其實(shí) ViewPager 和 RecyclerView 已經(jīng)實(shí)現(xiàn)了類(lèi)似的功能淹朋,尤其是 ViewPager,可以說(shuō)是已經(jīng)實(shí)現(xiàn)了我們這個(gè)控件的大部分功能邻耕,所以如果我們基于 ViewPager 來(lái)進(jìn)行改造的話(huà)嗦哆,也能讓我們的輪播控件更加穩(wěn)定
那 ViewPager 跟我們需要的自動(dòng)輪播控件有多少差距呢,主要有兩個(gè):

1.不支持自動(dòng)播放
2.無(wú)法從最后一張滑動(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)該是通過(guò) Timer 或者 ScheduledExecutorService 來(lái)實(shí)現(xiàn)計(jì)時(shí)器的功能畏腕,然后讓 ViewPager 通過(guò) serCurrentItem(int position) 方法缴川,將當(dāng)前的 Item 設(shè)置為下一個(gè) position 的數(shù)據(jù),但是如果通過(guò)定時(shí)器來(lái)實(shí)現(xiàn)的話(huà)描馅,會(huì)有一個(gè)問(wèn)題把夸,那就是我們?cè)谛枰?banner 進(jìn)行停止播放的時(shí)候就比較麻煩,所以通過(guò) Handler 用 sendMessage 的形式铭污,進(jìn)行事件的發(fā)送實(shí)現(xiàn) ViewPager 的自動(dòng)輪播恋日,以及某些場(chǎng)景的停止是比較合理的。

代碼實(shí)現(xiàn):

private static class BannerHander extends Handler {

    private WeakReference<AutoScrollViewPager> mBannerRef;

    private static final int MSG_CHANGE_SELECTION = 1;

    BannerHander(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岂膳,然后通過(guò)弱引用的形式防止內(nèi)存泄露,通過(guò)在 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)的無(wú)限輪播簸喂。

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

我們知道,ViewPager 是無(wú)法從最后一頁(yè)滑動(dòng)到第一頁(yè)的燎潮,但我們可以換一個(gè)思路喻鳄,如果我們?cè)?ViewPager 的 Adapter 里面,通過(guò) getCount() 方法將 ViewPager 的大小設(shè)置為無(wú)限大跟啤,然后通過(guò)取余的方式來(lái)保證滑動(dòng)的頁(yè)面一直對(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); // 通過(guò)取余的方式
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)化袄简,例如:我們是通過(guò)將 ViewPager 的大小設(shè)置為無(wú)限大的方式腥放,來(lái)實(shí)現(xiàn)從最后一張滑動(dòng)到第一張的,但這時(shí)候如果不進(jìn)行緩存的話(huà)绿语,我們?cè)?Adapter 的 instantiateItem(ViewGroup container, final int position) 方法里面秃症,便需要返回很多新 new 出來(lái)的 View候址,這樣會(huì)造成不必要的內(nèi)存浪費(fèi),只有對(duì)這些細(xì)節(jié)進(jìn)行優(yōu)化种柑,才能讓我們的控件更加的好用岗仑,穩(wěn)定性和性能方面也會(huì)更加優(yōu)異。

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 或者離開(kāi)當(dāng)前展示 Banner 的頁(yè)面時(shí)驶赏,如果 banner 還在不停的進(jìn)行無(wú)線輪播的話(huà)炸卑,會(huì)造成沒(méi)必要的性能損失,所以我們需要在觸摸 Banner 以及當(dāng)前的 Activity 為不可見(jiàn)狀態(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ì)給人一種很突兀的感覺(jué),而且 ViewPager 也沒(méi)有提供接口給我們對(duì) ViewPager 進(jìn)行切換速度的設(shè)置龄恋,所以我們需要通過(guò)反射的方式疙驾,使用 Scroller 來(lái)進(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ú)論是性能上還是穩(wěn)定性上都已經(jīng)很不錯(cuò)了铣卡。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末链韭,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子煮落,更是在濱河造成了極大的恐慌敞峭,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蝉仇,死亡現(xiàn)場(chǎng)離奇詭異旋讹,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)轿衔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)沉迹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人害驹,你說(shuō)我怎么就攤上這事鞭呕。” “怎么了宛官?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵葫松,是天一觀的道長(zhǎng)瓦糕。 經(jīng)常有香客問(wèn)我,道長(zhǎng)腋么,這世上最難降的妖魔是什么咕娄? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮珊擂,結(jié)果婚禮上圣勒,老公的妹妹穿的比我還像新娘。我一直安慰自己未玻,他們只是感情好灾而,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著扳剿,像睡著了一般旁趟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上庇绽,一...
    開(kāi)封第一講書(shū)人閱讀 49,784評(píng)論 1 290
  • 那天锡搜,我揣著相機(jī)與錄音,去河邊找鬼瞧掺。 笑死耕餐,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的辟狈。 我是一名探鬼主播肠缔,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼哼转!你這毒婦竟也來(lái)了明未?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤壹蔓,失蹤者是張志新(化名)和其女友劉穎趟妥,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體佣蓉,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡披摄,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了勇凭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片疚膊。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖虾标,靈堂內(nèi)的尸體忽然破棺而出酿联,到底是詐尸還是另有隱情,我是刑警寧澤夺巩,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布贞让,位于F島的核電站,受9級(jí)特大地震影響柳譬,放射性物質(zhì)發(fā)生泄漏喳张。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一美澳、第九天 我趴在偏房一處隱蔽的房頂上張望销部。 院中可真熱鬧,春花似錦制跟、人聲如沸舅桩。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)擂涛。三九已至,卻和暖如春聊记,著一層夾襖步出監(jiān)牢的瞬間撒妈,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工排监, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留狰右,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓舆床,卻偏偏與公主長(zhǎng)得像棋蚌,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子挨队,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容