PS:以前也写过关于图片轮播这一块的博客.不过写的很烂,并且很多情况没有考虑到(没有支持无线轮播,和手势点击事件).因此这里写一篇补上.也是当时太年轻了.

注:图片请放大后再看.否则看不清楚.

学习内容:

1.自定义ViewPager

2.图片无限轮播由于ViewPager的预加载机制所导致的问题.

  以前也写过关于图片轮播的相关博客,不过总体写的非常的烂,并且不能够无线轮播,而且也无法对手势事件进行相关的处理,因此在这里补上,也是属于自定义View的一篇内容吧.并且通过这个过程发现了无限轮播由于ViewPager的预加载机制所导致的问题.也正赶上项目要上一个新的版本,发现了这个bug.我的同事想到了一个比较不错的方案解决了这个问题.总体还是很不错的.因此在这里记录一下.

  总体实现无限轮播的思想,其实和网上大部分的思路都是相同的,设置一个Integer的最大值的一半,然后根据position和图片的数量去计算,来实现向左向右无限滑动这个功能.总体不是特别的难.自定义ViewPager之后,把相关的图片和跟随图片滑动时的小圆点传递到ViewPager当中,然后设置相关的滑动监听,Adapter就可以完美的实现图片的无限轮播了.

i.初始化轮播.

  初始化轮播需要线程和Handler配合来完成,设置一个无线循环的线程,让这个线程按照一定的周期一直向Activity发送Message,Handler在接收到消息后会不断的对消息进行处理.改变ViewPager当前显示的CurrentItem就可以实现无限轮播了.

/**
   * 初始化轮播的线程
   */
public void initLoop() {

    new Thread(new Runnable() {
        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(3000);
                    if (!stopLoopTag) {
                        Message message = Message.obtain();
                        message.what = 10;
                        message.arg1 = getCurrentItem() + 1;
                        mHandler.sendMessage(message);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }).start();
}

/**
   * 处理轮播的Handler
   */
private Handler mHandler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        if (msg.what == 10 && !stopLoopTag) {
            setCurrentItem(msg.arg1);
        } else {
            mHandler.removeMessages(10);
        }
        return true;
    }
});

ii.图片轮播时小圆点随之变动

  有了一个子线程和一个Handler就可以实现无线循环的一个过程,我们知道一般图片轮播都需要伴随小圆点的移动,小圆点一般是直接布局到ViewPager当中的,因为需要给用户一种更好的体验性,因此在图片轮播的同时伴随着小圆点也随之变动.那么小圆点如何在ViewPager的Item改变的时候也随之变动呢?这就需要addOnPageChangeListener()来实现了.

private OnPageChangeListener onPageChangeListener = new OnPageChangeListener() {
    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

    }

    @Override
    public void onPageSelected(int position) {

        /**
           * 改变小圆点的状态
           * 三种情况:
           *         1.在初始位置
           *         2.向左滑动
           *         3.向右滑动
           * 不要放在上一个方法中,会有明显的延迟现象出现
           * lastPointPosition 上一个点的位置,初始化为0
           * lastPointIndex 上一个点在集合中的位置
           * currentPointIndex 当前的点在集合中的位置
           * */
        if (lastPointPosition == 0) {

            currentPointIndex = 0;
            lastPointIndex = 0;

        } else if (lastPointPosition < position) {

            if (currentPointIndex == getImages().size() - 1) {
                currentPointIndex = 0;
            } else {
                currentPointIndex += 1;
            }

        } else if (lastPointPosition > position) {

            if (currentPointIndex == 0) {
                currentPointIndex = getImages().size() - 1;
            } else {
                currentPointIndex -= 1;
            }

        }

        dots.get(lastPointIndex).setImageResource(R.drawable.dot_normal);
        dots.get(currentPointIndex).setImageResource(R.drawable.dot_focus);
        lastPointPosition = position;
        lastPointIndex = currentPointIndex;
    }

    @Override
    public void onPageScrollStateChanged(int state) {

    }
};

这里我们通过addOnPageChangeListener()绑定Page的改变监听来改变小圆点随着Page改变的同时随之改变.图片的改变需要在适配器里去设置,适配器我留到最后说,因为其中有很多的细节.这样有了无限循环,原点移动,那么就需要说一下当我们手指停留在ViewPager的时候,如何使ViewPager停止播放.因为涉及到了手势事件,因此就要重写相关的方法.

iii.重写手势事件

/**
    * 手势事件的重写
    */
@Override
public boolean onTouchEvent(MotionEvent event) {

    switch (event.getAction()) {

        case MotionEvent.ACTION_DOWN:
            /**
              * 按下的时候停止轮播
              * */
            stopLoop();
            xDown = (int) event.getX();
            yDown = (int) event.getY();
            break;

        case MotionEvent.ACTION_MOVE:
            /**
              * 这里不用做任何处理,移动的时候ViewPager内的图片会自动滑动
              * */
            break;

        case MotionEvent.ACTION_UP:

            /**
              * 记录按下时间
              * */
            if (timeThread == null) {
                touchTime = new Date().getTime();
                timeTag = true;
                timeThread = new TimeThread();
                timeThread.start();
            } else {
                touchTime = new Date().getTime();
            }

            /**
              * 判断是否是点击事件
              * */
            int xUp = (int) event.getX();
            int yUp = (int) event.getY();
            if (Math.abs(xDown - xUp) < 20 && Math.abs(yDown - yUp) < 20) {

                if (onImageItemClickListener != null) {
                        onImageItemClickListener.onItemClick(currentPointIndex);
                }
            }
            break;
    }
    return super.onTouchEvent(event);
}

手势事件的重写就非常的简单了,当点击的时候停止轮播,移动的时候我们不需要做任何的处理,在手指离开之后,我们需要计算离开的时间,离开的时间超过两秒,就再次开启轮播.关闭轮播只需要停止最开始的那个子线程,让标记位StopLoopTag为true,再将消息队列中的消息移除队列.这样Handler也接收不到消息了.这样就停止了轮播的效果.计算手指按下和离开的时间也需要一个子线程去处理.

/**
   * 时间线程,用于记录手指离开点击ViewPager的时间
   * 如果离开的时间 >= 2000毫秒,那么ViewPager继续轮播
   */
class TimeThread extends Thread {

    @Override
    public void run() {
        while (timeTag) {
            long currentTime = new Date().getTime();
            if (currentTime - touchTime >= 2000) {
                openLoop();
                timeTag = false;
                timeThread = null;
            }
        }
    }
}

iv.PagerAdapter.

  PagerAdapter也是最蛋疼的一块.instantiateItem()方法里的那一大堆代码是关键.很多人都用这样的代码来实现图片的加载过程.

@Override
public Object instantiateItem(ViewGroup container, int position) {

        position %= images.size();
        if (position < 0) {
            position = position + images.size();
        }
        ImageView imageView = images.get(initPosition);
        ViewParent parent = imageView.getParent();
        if (parent != null) {
            ViewGroup viewGroup = (ViewGroup) parent;
            viewGroup.removeView(imageView);
        }
        container.addView(imageView);
        return imageView;
}
     

 其实这样写是有很大的问题的,因为这里没有考虑到ViewPager的预加载机制的问题,这也是我们项目出现的一个bug,我们是从服务器上获取相关的图片数据,然后保存在集合当中,如果我们在将图片加载到ViewPager中的时候,仅调用image.get(position),position为上面代码计算出的position,这样的话实际上会导致图片错位.虽然显示的是5张图片,但是实际上他们在ViewPager中显示的顺序是不对的.比如说我们在后台定义了这样的顺序, 1,2,3,4,5的顺序发送给我们,但是如果我们按照上面的方法从集合中拿数据的时候,ViewPager显示的不是1,2,3,4,5这样的顺序,这也是我们在项目中发现的问题.因为在点击图片的时候,是需要走不同的链接的,也正是这个原因我们发现了这个bug.因此这里做了很多的处理.

public class PictureAdapter extends PagerAdapter {

    private List<ImageView> images;

    /**
     * initPosition -1为初始化的位置,后面是当前的图片索引位置
     * topPosition 记录上一次初始化的索引位,用于计算上次的position和本次position的偏移量
     *
     * */
    private int initPosition = -1;
    private int topPosition = -1;

    public PictureAdapter(List<ImageView> images) {
        this.images = images;
    }

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

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

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {

    }

    /**
     * 实例化Item
     */
    @Override
    public Object instantiateItem(ViewGroup container, int position) {

        if(images.size() <=1){
            ImageView imageView = images.get(topPosition);
            container.addView(imageView);
            return imageView;
        }else{
            /**
             * 初始化状态
             * 向左滑动
             * 向右滑动
             * 由于ViewPager有预加载机制,默认加载一页,因此在第一次初始化的时候,会调用三次这个方法.
             * (第一次: position=1073741823 第二次: position=1073741822 第三次: position=1073741824)
             *
             * 而后续,这个方法仅被执行一次,并且执行的是预加载下一页的请求.
             * */
            Log.e("TAG","position="+position);
            if (initPosition == -1) {
                /**
                 * 初始化状态
                 * topPosition 记录第一次初始化的索引位.用于后续作比较判断下次是向右滑动还是向左滑动
                 * initPosition 初始化图片集合的索引值
                 * */
                topPosition = position;
                initPosition = 0;
            } else if (topPosition < position) {
                /**
                 * 向左滑动
                 * 得出偏移量后比较是否超过图片集合的大小
                 * */
                int value = position - topPosition;
                initPosition += value;
                if (initPosition == images.size()) {
                    /**
                     * 滑动到了最后一页
                     * */
                    initPosition = 0;
                } else if (initPosition > images.size()) {
                    /**
                     * 如果超出了图片集合的大小,则 initPosition = 超过的数值
                     * */
                    initPosition = (initPosition - images.size());
                }
                topPosition = position;
            } else if (topPosition > position) {
                int value = topPosition - position;
                initPosition -= value;
                if (initPosition == -1) {
                    /**
                     * 滑动到了第一页
                     * */
                    initPosition = images.size() - 1;
                } else if (initPosition < -1) {
                    /**
                     * 当计算后的值小于了集合大小,则用集合大小减去小于的这部分
                     * */
                    initPosition = (images.size() - (Math.abs(initPosition)));
                }
                topPosition = position;
            }
            Log.e("TAG","topPosition="+topPosition);
            Log.e("TAG","initPosition="+initPosition);
            /**
             * 只用这句话应该会出现问题
             * */
//        position %= images.size();
//        if (position < 0) {
//            position = position + images.size();
//        }
            ImageView imageView = images.get(initPosition);
            ViewParent parent = imageView.getParent();
            if (parent != null) {
                ViewGroup viewGroup = (ViewGroup) parent;
                viewGroup.removeView(imageView);
            }
            container.addView(imageView);
            return imageView;
        }
    }
} 

这就是我们定义的PagerAdapter.里面做了很多的逻辑处理.一张图解释一下其中的原理.

  这张图说明了一种的道理,上面代码不难发现.会出现 (initPosition > images.size())和initPosition < -1这两种情况.这种原因的导致就是因为position会由于这种预加载机制出现数值跳跃问题.这里大家可以去根据Log信息结合我说的原理,好好的研究一下.相信大家会研究明白的.

  最后就是点击事件了.对Activity暴露接口,让Activity去实现就可以了.

/**
     * 对Activity暴露接口
     */
    private OnImageItemClickListener onImageItemClickListener;

    public interface OnImageItemClickListener {
        void onItemClick(int itemPosition);
    }

    public void setOnImageItemClickListener(OnImageItemClickListener onImageItemClickListener) {
        this.onImageItemClickListener = onImageItemClickListener;
    }

  剩下的就是MainActivity了.这里就不粘贴代码了.里面的内容比较的简单.这样就实现了图片的无线轮播,并且支持点击停止,松开继续播放的功能,还有的图片点击事件.最后放一个源代码:

  源代码地址:http://pan.baidu.com/s/1dFqig17

Android之自定义ViewPager实现图片的无线轮播的更多相关文章

  1. Android RatingBar自定义替换系统图片

    1.自定义替换提醒☆图片,准备两个图片添加到系统中去:如下:        在drewable下定义一个图片资源ratingbar_drawable.xml 1 2 3 4 5 6 7 8 9 10 ...

  2. Android progressBar 自定义圆形旋转图片

    项目需要中需要更换progressbar的旋转背景,在网上找了几种办法,但是都有各自的问题 于是结合网上所讲,研究了一下终于ok了: 一 首相在drawable文件夹中建立如下旋转动画文件 <? ...

  3. 三行代码接入,社交软件打字时底下弹出的表情布局,自定义ViewPager+页面点标+各种功能的android小框架。

    (转载请声明出处:http://www.cnblogs.com/linguanh/) 前言: 接上次分享的 ListView 动态加载类,入口:http://www.cnblogs.com/lingu ...

  4. Android:使用ViewPager实现左右滑动切换图片 (简单版)

    ViewPager,它是google SDk中自带的一个附加包的一个类, 可以使视图滑动. 步骤: 1.引入android-support-v4.jar包,在主布局里加入 <android.su ...

  5. Android开发之ViewPager实现轮播图(轮播广告)效果的自定义View

    最近开发中需要做一个类似京东首页那样的广告轮播效果,于是采用ViewPager自己自定义了一个轮播图效果的View. 主要原理就是利用定时任务器定时切换ViewPager的页面. 效果图如下: 主页面 ...

  6. Android实现自定义带文字和图片的Button

    Android实现自定义带文字和图片的Button 在Android开发中经常会需要用到带文字和图片的button,下面来讲解一下常用的实现办法. 一.用系统自带的Button实现 最简单的一种办法就 ...

  7. Android随笔--使用ViewPager实现简单地图片的左右滑动切换

    Android中图片的左右切换随处可见,今天我也试着查阅资料试着做了一下,挺简单的一个小Demo,却也发现了一些问题,话不多说,上代码~: 使用了3个xml文件作为ViewPager的滑动page,布 ...

  8. Android:使用ViewPager实现左右滑动切换图片(图上有点点)

    在以下实例的基础上加上点点 Android:使用ViewPager实现左右滑动切换图片 (简单版) 效果预览: 因为要把点点放图片上,所以修改布局为相对布局: <?xml version=&qu ...

  9. Android ViewPager PagerAdapter 图片轮播

    ViewPager类直接继承了ViewGroup类,所有它是一个容器类,可以在其中添加其他的View类. ViewPager类需要一个PagerAdapter适配器类给它提供数据. ViewPager ...

随机推荐

  1. MyBatis中jdbcType和javaType的映射关系

    JDBC Type Java Type CHAR String VARCHAR String LONGVARCHAR String NUMERIC java.math.BigDecimal DECIM ...

  2. servlet的四个作用域

    作用域规定的是变量的有效期限,servlet有四个作用域对象,这里只说三个: 一. request作用域: 1.作用范围: 就是指从http请求发起,到服务器处理结束,返回响应的整个过程.在这个过程中 ...

  3. C++对象模型详解

    原文链接:吴秦大神的C++对象模型. 何为C++对象模型? C++对象模型可以概括为以下2部分: 1.语言中直接支持面向对象程序设计的部分: 2.对于各种支持的底层实现机制. 语言中直接支持面向对象程 ...

  4. Java Management extentsions(jmx)与tomcat

    1,概念:一个可以使用JMX管理器来管理的Java对象称为JMX管理资源(JMX manageable resource).事实上,一个JMX管理资源也可以是一个应用程序.一个实现或者一个服务.设备. ...

  5. GJM : 【C# 高性能服务器】完成端口、心跳的高性能Socket服务器 [转载]

    感谢您的阅读.喜欢的.有用的就请大哥大嫂们高抬贵手"推荐一下"吧!你的精神支持是博主强大的写作动力以及转载收藏动力.欢迎转载! 版权声明:本文原创发表于 [请点击连接前往] ,未经 ...

  6. UISrollView

    1.  contentOffset 默认CGPointZero,用来设置scrollView的滚动偏移量. // 设置scrollView的滚动偏移量 scrollView.contentOffset ...

  7. 销傲销售过程GSP管理系统功能概述

    1      公司介绍 西安海思威软件有限公司于2009年2月注册成立,海思威软件公司隶属于海思威集团,位于交通十分便利的西安经济技术开发区.公司致力于中国本土式销售管理的研究与管理软件产品的开发,是 ...

  8. .net core 使用Autofac依赖注入

    Startup中: public IContainer ApplicationContainer { get; private set; } // This method gets called by ...

  9. 缓存插件 EHCache 页面缓存CachingFilter

    Ehcache基本用法 CacheManager cacheManager = CacheManager.create(); // 或者 cacheManager = CacheManager.get ...

  10. Android 使用Okhttp/Retrofit持久化cookie的简便方式

    首先cookie是什么就不多说了,还是不知道的话推荐看看这篇文章 Cookie/Session机制详解 深入解析Cookie技术 为什么要持久化cookie也不多说了,你能看到这篇文章代表你有这个需求 ...