RxJava 实现ViewPager的轮播图

前言

在App中实现一个轮播图已经是很多产品的标配了,很多人都会想到使用ViewPager + Handler来完成轮播图的效果。但是在RxJava快速发展的情况下,已经可以使用RxJava来代替Handler完成这样任务了。
如果对RxJava还不熟悉的朋友可以看看以下的一些文章:
RxJava学习
给 Android 开发者的 RxJava 详解

效果图

ViewPager的操作

说到ViwePager应该大家都不陌生,它可以结合普通的View也可以结合Fragment一起使用。在此我也就不对它的使用方法进行过多的介绍了。直接开始介绍轮播的方法。

常见的轮播操作

private class ImageAdapter extends PagerAdapter{

    private ArrayList<ImageView> viewlist;

    public ImageAdapter(ArrayList<ImageView> viewlist) {
        this.viewlist = viewlist;
    }

    @Override
    public int getCount() {
        //设置成最大,使用户看不到边界
        return Integer.MAX_VALUE;
    }
    ....
}
private static class ImageHandler extends Handler{
    ...
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        //检查消息队列并移除未发送的消息,这主要是避免在复杂环境下消息出现重复等问题。
        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);
                //准备下次播放
                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;
        }
    }
    ...
}

以上就是比较常见的轮播图的代码,我只是在网上随便找的。首先它的代码中将PagerAdaptergetCount()返回了一个Integer.MAX_VALUE;它的目的是为了让图片一直的播放下去,但是在一些极限情况下还是会crash的,并且它返回的数量太大了在一定程度上对内存也造成了较大的消耗。其次我们可以看到handler的代码极其的冗杂,不仅多而且逻辑也比较麻烦。 现在我们针对刚才的问题来进行优化

更好的轮播操作

更好的无限播放:设置页卡视图列表时,在前后额外各加一个页卡。最前面加最后一张图片,最后面加第1张图片。然后每当切换到最前的页卡时,就替换成倒数第2个页卡;每当切换到最后的页卡时,就替换成第2个页卡。这样一来就形成了连贯,自然实现了无限滑动的功能。
1)设置ViewPager的视图列表时,在前后各加一个页卡。

for (int i = 0; i < count + 2; i++) {
    if (i == 0) {// 将最前面一页设置成本来最后的那页
        Glide.with(context).
                load(imageTitleBeanList.get(count - 1).getImageUrl()).into(ivImage);
        tvTitle.setText(imageTitleBeanList.get(count - 1).getTitle());
    } else if (i == count + 1) {// 将最后面一页设置成本来最前的那页
        Glide.with(context).
                load(imageTitleBeanList.get(0).getImageUrl()).into(ivImage);
        tvTitle.setText(imageTitleBeanList.get(0).getTitle());
    } else {
        Glide.with(context).
                load(imageTitleBeanList.get(i - 1).getImageUrl()).into(ivImage);
        tvTitle.setText(imageTitleBeanList.get(i - 1).getTitle());
    }
    // 将设置好的View添加到View列表中
    viewList.add(view);
}

2)在监听ViewPager的页卡状态改变中,当滑动到第1个页卡时替换成倒数第2个页卡;当滑动到最后一个页卡时替换成第2个页卡。

@Override
public void onPageScrollStateChanged(int state) {
    switch (state) {
        // 闲置中
        case ViewPager.SCROLL_STATE_IDLE:
            // “偷梁换柱”
            if (vpImageTitle.getCurrentItem() == 0) {
                vpImageTitle.setCurrentItem(count, false);
            } else if (vpImageTitle.getCurrentItem() == count + 1) {
                vpImageTitle.setCurrentItem(1, false);
            }
            currentItem = vpImageTitle.getCurrentItem();
            break;
    }
}

此部分参考自:使用ViewPager实现图片轮播。Handler现在就该由RxJava来替代了。

Interval 操作符

创建一个按固定时间间隔发射整数序列的Observable


Interval
Interval

Interval操作符返回一个Observable,它按固定的时间间隔发射一个无限递增的整数序列。


Interval
Interval

RxJava将这个操作符实现为interval方法。它接受一个表示时间间隔的参数和一个表示时间单位的参数。

Javadoc: interval(long,TimeUnit)
Javadoc: interval(long,TimeUnit,Scheduler)

interval默认在computation调度器上执行。你也可以传递一个可选的Scheduler参数来指定调度器。

用RxJava取代Handler

public void start()  {
    mViewPagerSubscribe = Observable.interval(5, 5, TimeUnit.SECONDS)  // 5s的延迟,5s的循环时间
        .subscribeOn(AndroidSchedulers.mainThread())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(new Action1<Long>() {
            @Override
            public void call(Long aLong) {
                // 进行轮播操作
                if (mWeeklyMovieInfos != null && mWeeklyMovieInfos.size() > 0 && isAutoPlay) {
                    mCurrentPage++;
                    mWeeklyViewPager.setCurrentItem(mCurrentPage);
                }
            }
        });
}

为了更好的用户体验,在用户进行滑动操作的时候,应该停止自动轮播

mPager.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
  //监听ViewPager的触摸事件,当用户按下的时候取消注册,当用户手抬起的时候再注册
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                stop();
                break;
            case MotionEvent.ACTION_UP:
                start();
                break; 
       }
        return false;
    }});

public void stop() {
    if(mViewPagerSubscribe.isUnsubscribed()) {    
        mViewPagerSubscribe.unsubscribe();
    }
}

总结

本文主要是对ViewPager实现轮播图的一种总结。首先提出更好的轮播图的方法,其实讲解了RxJava中interval操作符的使用,最后用该操作符替换掉Handler完美实现轮播图。
代码点我下载
AndroidDemos

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,444评论 25 707
  • 轮播图在Android开发中是非常常见的控件,一般App的首页广告和电商类App的商品详情图片都会用轮播图来实现。...
    donkingliang阅读 11,448评论 0 30
  • 背景 说到图片轮播,之前写过一篇文章《造轮子:android自定义专属广告轮播控件》,不过当时是采用ViewFli...
    码无止境阅读 3,314评论 5 41
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,596评论 18 139
  • 为什么会有文字呢?说话不就行了吗?我觉得应该很多人问过这个问题无数遍。所以今天来瞎说一下吧。 之所...
    Richard1015阅读 145评论 0 0