使用ViewPager实现图片轮播

前言

首先我们来看一下运行的效果,如下所示:



  这是在我们的项目中经常会用到的图片轮播效果,一般用于广告图片的展示。

它要求主要实现以下功能:

1)自动播放;
  2)无限滑动;
  3)手指拖拽图片时暂停自动轮播,松开后继续自动轮播;
  4)含动画效果的小圆点指示器。

本文将介绍利用ViewPager实现无限轮播图片,图片下方加上小圆点指示器标记当前位置,并利用Handler实现自动轮播图片。

正文

1、实现自动播放
思路:没隔一段时间让ViewPager更换一次页卡,使用Handler来实现。

1)标记是否自动播放

private boolean isAutoPlay;

2)每隔一段时间使用Handler发送一次更换页卡的任务

// 如果少于2张就不用自动播放了
if (count < 2) {
    isAutoPlay = false;
} else {
    isAutoPlay = true;
    handler = new Handler();
    handler.postDelayed(task, delay);
}

3)在任务中每隔一段时间再次发送任务,这样循环发送就实现了自动播放的效果。

private Runnable task = new Runnable() {
    @Override
    public void run() {
        if (isAutoPlay) {
            // 位置循环
            currentItem = currentItem % (count + 1) + 1;
            // 正常每隔3秒播放一张图片
            vpImageTitle.setCurrentItem(currentItem);
            handler.postDelayed(task, delay);
        } else {
            // 如果处于拖拽状态停止自动播放,会每隔5秒检查一次是否可以正常自动播放。
            handler.postDelayed(task, 5000);
        }
    }
};

2、实现无限滑动
思路:设置页卡视图列表时,在前后额外各加一个页卡。最前面加最后一张图片,最后面加第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;
    }
}

3、手指滑动图片时停止自动播放
思路:使用一个标记来控制是否自动播放。

1)声明一个boolean变量,用来标记是否播放。

private boolean isAutoPlay;

2)默认是自动播放,但当图片少于2张时不自动播放。

private void starPlay() {
    // 如果少于2张就不用自动播放了
    if (count < 2) {
        isAutoPlay = false;
    } else {
        isAutoPlay = true;
        handler.postDelayed(task, delay);
    }
}

3)根据标记判断是否切换页卡

private Runnable task = new Runnable() {
    @Override
    public void run() {
        if (isAutoPlay) {
            // 正常每隔3秒播放一张图片
            vpImageTitle.setCurrentItem(currentItem);
            handler.postDelayed(task, delay);
        } else {
            // 如果处于拖拽状态停止自动播放,会每隔5秒检查一次是否可以正常自动播放。
            handler.postDelayed(task, 5000);
        }
    }
};

4)在监听ViewPager的页卡状态改变中,如果是拖动状态就不切换页卡。

@Override
public void onPageScrollStateChanged(int state) {
    switch (state) {
        // 闲置中

        case ViewPager.SCROLL_STATE_IDLE:
            isAutoPlay = true;
            break;
        // 拖动中
        case ViewPager.SCROLL_STATE_DRAGGING:
            isAutoPlay = false;
            break;
        // 设置中
        case ViewPager.SCROLL_STATE_SETTLING:
            isAutoPlay = true;
            break;
    }
}

4、自定义指示器
思路:使用一个LinearLayout作为容器,然后根据图片的数量向容器中不断添加绘制的小圆点,另外再设置变大变小的属性动画用于动画效果。监听ViewPager的页卡,每当切换到一个页卡时就将切换对应状态的小圆点,并且设置相应的动画效果。

1)绘制小圆点
未选中状态,灰色的圆。

<shape xmlns:android="http://schemas.android.com/apk/res/android"
      android:shape="oval">
    <size
        android:width="32dp"
        android:height="32dp"/>
    <solid android:color="#9e9e9e"/>
</shape>

选中状态,白色的圆。

<shape xmlns:android="http://schemas.android.com/apk/res/android"
      android:shape="oval">
    <size
        android:width="32dp"
        android:height="32dp"/>
    <solid android:color="#ecf0f1"/>
</shape>

2)属性动画
变大

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:duration="150"
        android:pivotX="50%"
        android:pivotY="50%"
        android:propertyName="scaleX"
        android:valueFrom="1.0"
        android:valueTo="1.5"
        android:valueType="floatType"/>
    <objectAnimator
        android:duration="150"
        android:pivotX="50%"
        android:pivotY="50%"
        android:propertyName="scaleY"
        android:valueFrom="1.0"
        android:valueTo="1.5"
        android:valueType="floatType"/>
</set>

变小

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:duration="150"
        android:pivotX="50%"
        android:pivotY="50%"
        android:propertyName="scaleX"
        android:valueFrom="1.5"
        android:valueTo="1.0"
        android:valueType="floatType"/>
    <objectAnimator
        android:duration="150"
        android:pivotX="50%"
        android:pivotY="50%"
        android:propertyName="scaleY"
        android:valueFrom="1.5"
        android:valueTo="1.0"
        android:valueType="floatType"/>
</set>

3)设置指示器
先是统一设置属性并添加到容器中,然后默认第1个小圆点为选中状态。选中状态的小圆点颜色由灰色变成白色,并且变大。

private void setIndicator() {
    isLarge = new SparseBooleanArray();
    // 记得创建前先清空数据,否则会受遗留数据的影响。
    llDot.removeAllViews();
    for (int i = 0; i < count; i++) {
        View view = new View(context);
        view.setBackgroundResource(R.drawable.dot_unselected);
        LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(dotSize, dotSize);
        layoutParams.leftMargin = dotSpace / 2;
        layoutParams.rightMargin = dotSpace / 2;
        layoutParams.topMargin = dotSpace / 2;
        layoutParams.bottomMargin = dotSpace / 2;
        llDot.addView(view, layoutParams);
        isLarge.put(i, false);
    }
    llDot.getChildAt(0).setBackgroundResource(R.drawable.dot_selected);
    animatorToLarge.setTarget(llDot.getChildAt(0));
    animatorToLarge.start();
    isLarge.put(0, true);
}

4)监听页卡
当页卡被选中时,相应的小圆点颜色由灰色变成白色,并且变大;之前的小圆点颜色由白色变成灰色,并且变小。

@Override
public void onPageSelected(int position) {
    // 遍历一遍子View,设置相应的背景。
    for (int i = 0; i < llDot.getChildCount(); i++) {
        if (i == position - 1) {// 被选中
            llDot.getChildAt(i).setBackgroundResource(R.drawable.dot_selected);
            if (!isLarge.get(i)) {
                animatorToLarge.setTarget(llDot.getChildAt(i));
                animatorToLarge.start();
                isLarge.put(i, true);
            }
        } else {// 未被选中
            llDot.getChildAt(i).setBackgroundResource(R.drawable.dot_unselected);
            if (isLarge.get(i)) {
                animatorToSmall.setTarget(llDot.getChildAt(i));
                animatorToSmall.start();
                isLarge.put(i, false);
            }
        }
    }
}

源码地址
ImageSlideshow

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

推荐阅读更多精彩内容