使用Viewpager Indicator实现图片无限轮播



自定义Indicator控件设置XML属性介绍:

Android Viewpager Indicator是android开发中最常用的控件之一,几乎所有的新闻类APP中都有使用,下面介绍其基本使用方法。

1. 首先一个indicator必须要与一个ViewPager关联在一起,所以它提供了一个setViewPager方法。
2 .它扩展了ViewPager.OnPageChangeListener接口,表示接管了ViewPager的Pager改变时的监听处理,这也是为什么为ViewPager设置OnPageChangeListener监听器时不能设置在ViewPager上而必须设置在indicator上的原因。   

                             

            最终实现效果:

                                                                      

Java代码:

实现步骤:

首先我们创建一个集合,为了装ViewPager加载的图片控件,在定义一个Indicator的成员变量.

创建一个Handler设置为全局,通过Handler实现图片无限轮播,下面我会给大家详细的说明图片无限轮播的实现方法

  1. //创建一个集合装Viewpager加载的图片控件  
  2.    private List<View> mViews = new ArrayList<View>();  
  3.    private Indicator mIn;  
  4.    private Handler mHandler = new Handler();  
  5.    private ViewPager mViewPager;  
 //创建一个集合装Viewpager加载的图片控件
    private List<View> mViews = new ArrayList<View>();
    private Indicator mIn;
    private Handler mHandler = new Handler();
    private ViewPager mViewPager;
然后在 onCreate()里实现:

  1. @Override  
  2.   protected void onCreate(Bundle savedInstanceState) {  
  3.       super.onCreate(savedInstanceState);  
  4.       setContentView(R.layout.activity_main);  
  5.       //初始化viewpager的item数据,往集合里面放数据,方便ViewPager拿数据  
  6.       initData();  
  7.       //初始化控件  
  8.       ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);  
  9.       //初始化我的自定义控件  
  10.       mIn = (Indicator) findViewById(R.id.indicator);  
  11.       //设置ViewPager的监听器  
  12.       viewPager.setOnPageChangeListener(new MyPagerListner());  
  13.       //设置ViewPager适配器  
  14.       viewPager.setAdapter(new MyPagerAdapter());  
  15.   }  
  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //初始化viewpager的item数据,往集合里面放数据,方便ViewPager拿数据
        initData();
        //初始化控件
        ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
        //初始化我的自定义控件
        mIn = (Indicator) findViewById(R.id.indicator);
        //设置ViewPager的监听器
        viewPager.setOnPageChangeListener(new MyPagerListner());
        //设置ViewPager适配器
        viewPager.setAdapter(new MyPagerAdapter());
    }
然后初始化ViewPager的 item数据,最后别忘记调用initData方法
  1. private void initData() {  
  2.     for (int x = 0; x < 4; x++) {  
  3.         //通过布局填充器,把一个布局XML文件转换为View对象  
  4.         View inflate = getLayoutInflater().inflate(R.layout.pager_item, null);  
  5.         //找到布局View里的ImageView控件对象  
  6.         ImageView imageview = (ImageView) inflate.findViewById(R.id.iv);  
  7.         //往ImageView控件里放图片  
  8.         imageview.setImageResource(R.mipmap.ic_launcher);  
  9.         //最后把布局View控件放到集合里  
  10.         mViews.add(inflate);  
  11.     }  
  12. }  
    private void initData() {
        for (int x = 0; x < 4; x++) {
            //通过布局填充器,把一个布局XML文件转换为View对象
            View inflate = getLayoutInflater().inflate(R.layout.pager_item, null);
            //找到布局View里的ImageView控件对象
            ImageView imageview = (ImageView) inflate.findViewById(R.id.iv);
            //往ImageView控件里放图片
            imageview.setImageResource(R.mipmap.ic_launcher);
            //最后把布局View控件放到集合里
            mViews.add(inflate);
        }
    }

创建ViewPager适配器,重写4个方法,下面给大家介绍这4个方法的作用.

 1:getCount()是设置ViewPager的item数量

 2:isViewFromObject()固定的格式

 3:destroyItem()防止内存溢出,溢出ImageView对象

 4:instantiateItem()此方法类似于ListView中的getView,第一个参数是ViewPager的化身,第二个参数是item的位置

  1. class MyPagerAdapter extends PagerAdapter {  
  2.         
  3.        @Override  
  4.        public int getCount() {  
  5.            return Integer.MAX_VALUE;  
  6.        }  
  7.   
  8.          
  9.        @Override  
  10.        public boolean isViewFromObject(View view, Object object) {  
  11.            return view == object;  
  12.        }  
  13.   
  14.         
  15.        @Override  
  16.        public void destroyItem(ViewGroup container, int position, Object object) {  
  17.            position %= 4;//防止角标越界  
  18.            container.removeView(mViews.get(position));  
  19.   
  20.        }  
  21.   
  22.         
  23.        @Override  
  24.        public Object instantiateItem(ViewGroup container, int position) {  
  25.            position %= 4;//防止角标越界  
  26.            //从集合里拿对应位置的图片  
  27.            View view = mViews.get(position);  
  28.            //把ImageView的对象添加到Viewpager  
  29.            container.addView(view);  
  30.            //返回View对象  
  31.            return view;  
  32.        }  
  33.    }  
 class MyPagerAdapter extends PagerAdapter {
       
        @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) {
            position %= 4;//防止角标越界
            container.removeView(mViews.get(position));

        }

       
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            position %= 4;//防止角标越界
            //从集合里拿对应位置的图片
            View view = mViews.get(position);
            //把ImageView的对象添加到Viewpager
            container.addView(view);
            //返回View对象
            return view;
        }
    }
创建ViewPager的监听事件:

1:onPageScrolled()是滑动时调用

2:onPageScrollStateChanged()是选中时调用

3:onPageScrollStateChanged()是滑动状态改变时回调的方法

  1. class MyPagerListner implements ViewPager.OnPageChangeListener {  
  2.       /** 
  3.        * @param position             item位置 
  4.        * @param positionOffset       //偏移的百分比这个百分比永远接近于1 
  5.        * @param positionOffsetPixels //偏移量 
  6.        */  
  7.       @Override  
  8.       public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {  
  9.           Log.d("DS""onPageScrolled回调");  
  10.           Log.i("ds""position :" + position + "  positionOffset :" + positionOffset + "  positionOffsetPixels : " + positionOffsetPixels);  
  11.           //往自定义控件里放入item位置及偏移的百分比,是小点可以动态的跟着ViewPager滑动  
  12.           mIn.setoffest(position, positionOffset);  
  13.   
  14.       }  
  15.   
  16.       @Override  
  17.       public void onPageSelected(int position) {  
  18.           Log.d("DS""onPageScrolled    回调");  
  19.       }  
  20.   
  21.       @Override  
  22.       public void onPageScrollStateChanged(int state) {  
  23.           Log.d("DS""onPageScrollStateChanged     回调    hhh");  
  24.       }  
  25.   }  
  class MyPagerListner implements ViewPager.OnPageChangeListener {
        /**
         * @param position             item位置
         * @param positionOffset       //偏移的百分比这个百分比永远接近于1
         * @param positionOffsetPixels //偏移量
         */
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            Log.d("DS", "onPageScrolled回调");
            Log.i("ds", "position :" + position + "  positionOffset :" + positionOffset + "  positionOffsetPixels : " + positionOffsetPixels);
            //往自定义控件里放入item位置及偏移的百分比,是小点可以动态的跟着ViewPager滑动
            mIn.setoffest(position, positionOffset);

        }

        @Override
        public void onPageSelected(int position) {
            Log.d("DS", "onPageScrolled    回调");
        }

        @Override
        public void onPageScrollStateChanged(int state) {
            Log.d("DS", "onPageScrollStateChanged     回调    hhh");
        }
    }
在这里给大家呈现关于实现ViewPager图片自动轮播
  1. private void autoScroll() {  
  2.   
  3.        mHandler.postDelayed(new Runnable() {  
  4.            @Override  
  5.            public void run() {  
  6.                //获取当前的轮播的位置  
  7.                int currentItem = mViewPager.getCurrentItem();  
  8.                //从当前的图切换到另一张通过(currentItem + 1)就可以实现  
  9.                mViewPager.setCurrentItem(currentItem + 1);  
  10.                //通过mHandler请求延迟2秒  
  11.                mHandler.postDelayed(this2000);  
  12.                //调用触摸滑动事件方法  
  13.                onTouch();  
  14.            }  
  15.   
  16.        }, 2000);  
  17.   
  18.    }  
 private void autoScroll() {

        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                //获取当前的轮播的位置
                int currentItem = mViewPager.getCurrentItem();
                //从当前的图切换到另一张通过(currentItem + 1)就可以实现
                mViewPager.setCurrentItem(currentItem + 1);
                //通过mHandler请求延迟2秒
                mHandler.postDelayed(this, 2000);
                //调用触摸滑动事件方法
                onTouch();
            }

        }, 2000);

    }
关于实现触摸滑动的监听事件,它的作用是当你用手触摸ViewPager时它会监听你的手势,当你触碰到时就暂停轮播.
  1. private void onTouch() {  
  2.         //通过mViewPager去设置触摸滑动的点击事件  
  3.         mViewPager.setOnTouchListener(new View.OnTouchListener() {  
  4.             @Override  
  5.             public boolean onTouch(View view, MotionEvent event) {  
  6.                 switch (event.getAction()) {  
  7.                     case MotionEvent.ACTION_MOVE:  
  8.                         mHandler.removeMessages(0);  
  9.                     //移除回调函数和消息  
  10.                     case MotionEvent.ACTION_DOWN:  
  11.                         mHandler.removeCallbacksAndMessages(null);  
  12.                         break;  
  13.                     //当你触摸时停止自动滑动  
  14.                     case MotionEvent.ACTION_UP:  
  15.                         autoScroll();  
  16.                         break;  
  17.                 }  
  18.                 return false;  
  19.             }  
  20.         });  
  21.     }  
  22.   
  23. }  
private void onTouch() {
        //通过mViewPager去设置触摸滑动的点击事件
        mViewPager.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_MOVE:
                        mHandler.removeMessages(0);
                    //移除回调函数和消息
                    case MotionEvent.ACTION_DOWN:
                        mHandler.removeCallbacksAndMessages(null);
                        break;
                    //当你触摸时停止自动滑动
                    case MotionEvent.ACTION_UP:
                        autoScroll();
                        break;
                }
                return false;
            }
        });
    }

}
Java布局:
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <RelativeLayout  
  3.     xmlns:android="http://schemas.android.com/apk/res/android"  
  4.     xmlns:tools="http://schemas.android.com/tools"  
  5.     xmlns:app="http://schemas.android.com/apk/res-auto"  
  6.     android:id="@+id/activity_main"  
  7.     android:layout_width="match_parent"  
  8.     android:layout_height="match_parent"  
  9.     tools:context="com.example.android.indicatorapp.MainActivity">  
  10.   
  11.     <android.support.v4.view.ViewPager  
  12.         android:id="@+id/viewpager"  
  13.         android:layout_width="match_parent"  
  14.         android:layout_height="200dp">  
  15.   
  16.     </android.support.v4.view.ViewPager>  
  17.   
  18.     <!--使用的自定义控件-->  
  19.     <!--setNumber 我们自己设置的属性,决定空心圆的多少,一开始不要加上-->  
  20.     <com.example.liuy.indicatorapp.Indicator  
  21.         app:setNumber="6"  
  22.         android:id="@+id/indicator"  
  23.         android:layout_width="200dp"  
  24.         android:layout_height="60dp"  
  25.         android:layout_alignBottom="@+id/viewpager"  
  26.         android:layout_centerHorizontal="true"/>  
  27.   
  28.   
  29. </RelativeLayout>  
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.android.indicatorapp.MainActivity">

    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="200dp">

    </android.support.v4.view.ViewPager>

    <!--使用的自定义控件-->
    <!--setNumber 我们自己设置的属性,决定空心圆的多少,一开始不要加上-->
    <com.example.liuy.indicatorapp.Indicator
        app:setNumber="6"
        android:id="@+id/indicator"
        android:layout_width="200dp"
        android:layout_height="60dp"
        android:layout_alignBottom="@+id/viewpager"
        android:layout_centerHorizontal="true"/>


</RelativeLayout>

接下我们创建一个 Indicator这个类的实现以下步骤:

1.首先继承View,复写构造方法

  1. public class Indicator extends View {  
  2.     //实心圆的画笔;  
  3.     private Paint mForePaint;  
  4.     //空心圆的画笔;  
  5.     private Paint mBgPaint;  
  6.   
  7.   
  8.     //规定圆的数量,默认是4个,如果有XML指定的数量,使用指定的  
  9.     private int mNumber = 4;  
  10.     //圆的半径,规定默认值为10,如果有XML指定的数量,使用指定的  
  11.     private int mRadius = 10;  
  12.     //定义圆(空心圆)的背景颜色,默认红色,如果有XML指定的数量,使用指定的  
  13.     private int mBgColor = Color.RED;  
  14.     //定义圆(实心圆)的背景颜色,默认蓝色,如果有XML指定的数量,使用指定的  
  15.     private int mkForeColor = Color.BLUE;  
public class Indicator extends View {
    //实心圆的画笔;
    private Paint mForePaint;
    //空心圆的画笔;
    private Paint mBgPaint;


    //规定圆的数量,默认是4个,如果有XML指定的数量,使用指定的
    private int mNumber = 4;
    //圆的半径,规定默认值为10,如果有XML指定的数量,使用指定的
    private int mRadius = 10;
    //定义圆(空心圆)的背景颜色,默认红色,如果有XML指定的数量,使用指定的
    private int mBgColor = Color.RED;
    //定义圆(实心圆)的背景颜色,默认蓝色,如果有XML指定的数量,使用指定的
    private int mkForeColor = Color.BLUE;
2.初始化画笔,该方法在Java代码添加控件时回调

  1. public Indicator(Context context, AttributeSet attrs) {  
  2.   
  3.      super(context, attrs);  
  4.      //初始化画笔对象  
  5.      initPaint();  
  6.   
  7.      //引用atts文件下,给自己定义控件设置属性,得到TypdeArray对象.  
  8.      //参数1:attrs固定    参数2:在values文件下XML里R.styleable.下写的name名字  
  9.      TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.Indicator);  
  10.      //使用typedArray对象,把在自定义控件设置的属性和XML文件里的属性进行关联,才算完成  
  11.      //参数1:R.styleable.Indicator 你在attrs定义的名字   参数2:你要管理的成员变量名(最后=也是成员变量名)  
  12.      //注意你在XML文件里设置的类型属性获取时也要是对应的类型.(最后同步Gradle文件,否则在XML布局文件里依然没有办法引用)  
  13.      mNumber = typedArray.getInteger(R.styleable.Indicator_setNumber, mNumber);  
  14.      mRadius = typedArray.getInteger(R.styleable.Indicator_setRadius, mRadius);  
  15.  }  
   public Indicator(Context context, AttributeSet attrs) {

        super(context, attrs);
        //初始化画笔对象
        initPaint();

        //引用atts文件下,给自己定义控件设置属性,得到TypdeArray对象.
        //参数1:attrs固定    参数2:在values文件下XML里R.styleable.下写的name名字
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.Indicator);
        //使用typedArray对象,把在自定义控件设置的属性和XML文件里的属性进行关联,才算完成
        //参数1:R.styleable.Indicator 你在attrs定义的名字   参数2:你要管理的成员变量名(最后=也是成员变量名)
        //注意你在XML文件里设置的类型属性获取时也要是对应的类型.(最后同步Gradle文件,否则在XML布局文件里依然没有办法引用)
        mNumber = typedArray.getInteger(R.styleable.Indicator_setNumber, mNumber);
        mRadius = typedArray.getInteger(R.styleable.Indicator_setRadius, mRadius);
    }
3.复写onMeasure,方法里调用 initPaint()方法

  1. @Override  
  2.    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  3.        super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  4.        initPaint();  
  5.    }  
 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        initPaint();
    }
4.将 mForePaintmBgPaint抽取成员变量

initPaint此代码是实现画圆:

  1. //初始化画笔对象  
  2.    private void initPaint() {  
  3.        //创建画笔的对象  
  4.        mForePaint = new Paint();  
  5.        //设置抗锯齿(如果不设置抗锯齿画出来的图会模糊)  
  6.        mForePaint.setAntiAlias(true);  
  7.        //设置画笔的样式,为实心  
  8.        mForePaint.setStyle(Paint.Style.FILL);  
  9.        //设置画笔的颜色  
  10.        mForePaint.setColor(mkForeColor);  
  11.        //设置画笔的宽度  
  12.        mForePaint.setStrokeWidth(2);  
  13.   
  14.        //创建画笔的对象,用于画空心圆  
  15.        mBgPaint = new Paint();  
  16.        mBgPaint.setAntiAlias(true);  
  17.        mBgPaint.setStyle(Paint.Style.STROKE);  
  18.        mBgPaint.setColor(mBgColor);  
  19.        mBgPaint.setStrokeWidth(2);  
  20.   
  21.    }  
 //初始化画笔对象
    private void initPaint() {
        //创建画笔的对象
        mForePaint = new Paint();
        //设置抗锯齿(如果不设置抗锯齿画出来的图会模糊)
        mForePaint.setAntiAlias(true);
        //设置画笔的样式,为实心
        mForePaint.setStyle(Paint.Style.FILL);
        //设置画笔的颜色
        mForePaint.setColor(mkForeColor);
        //设置画笔的宽度
        mForePaint.setStrokeWidth(2);

        //创建画笔的对象,用于画空心圆
        mBgPaint = new Paint();
        mBgPaint.setAntiAlias(true);
        mBgPaint.setStyle(Paint.Style.STROKE);
        mBgPaint.setColor(mBgColor);
        mBgPaint.setStrokeWidth(2);

    }
5.重写 onDraw方法.参数就是 canvas画板,直接使用,画两种类型的圆

  1. @Override  
  2. protected void onDraw(Canvas canvas) {  
  3.     super.onDraw(canvas);  
  4.   
  5.     //画多个空心圆,为了使圆不重叠,所以对X轴坐标进行动态的修改  
  6.     for (int i = 0; i < mNumber; i++) {  
  7.         //  
  8.         canvas.drawCircle(230 + i * mRadius * 320, mRadius, mBgPaint);  
  9.     }  
  10.     //画实心圆,为使实心圆能够进行X轴移动   参数1加上了偏移量  
  11.     canvas.drawCircle(230 + moffest, 20, mRadius, mForePaint);  
  12. }  
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //画多个空心圆,为了使圆不重叠,所以对X轴坐标进行动态的修改
        for (int i = 0; i < mNumber; i++) {
            //
            canvas.drawCircle(230 + i * mRadius * 3, 20, mRadius, mBgPaint);
        }
        //画实心圆,为使实心圆能够进行X轴移动   参数1加上了偏移量
        canvas.drawCircle(230 + moffest, 20, mRadius, mForePaint);
    }
6.接受外界ViewPager的 Item位置,及偏移的百分比转换为偏移量:
  1.  private float moffest;  
  2.     public void setoffest(int position, float positionOffset) {  
  3.         invalidate();  
  4.   
  5.         //为了防止角标越界,取余数  
  6.         position %= mNumber;  
  7.         //给成员变量设置偏移量具体数据  
  8.         //因为从一个圆到另一个圆,要经过3个半径+偏移量*3个半径,也可以看出点的移动过程  
  9.         moffest = position * 3 * mRadius + positionOffset * 3 * mRadius;  
  10.         if (position == mNumber - 1) {  
  11.             moffest = position * 3 * mRadius;  
  12.         }  
  13.         //关键的一点,从新绘制自定义View的方法,十分常用.(不绘制看不成自定义控件的动态效果)  
  14.         invalidate();  
  15.   
  16.     }  
  17. }  
 private float moffest;
    public void setoffest(int position, float positionOffset) {
        invalidate();

        //为了防止角标越界,取余数
        position %= mNumber;
        //给成员变量设置偏移量具体数据
        //因为从一个圆到另一个圆,要经过3个半径+偏移量*3个半径,也可以看出点的移动过程
        moffest = position * 3 * mRadius + positionOffset * 3 * mRadius;
        if (position == mNumber - 1) {
            moffest = position * 3 * mRadius;
        }
        //关键的一点,从新绘制自定义View的方法,十分常用.(不绘制看不成自定义控件的动态效果)
        invalidate();

    }
}


7.同步Gradle文件,否则在XML布局文件里依然没有办法引用


8.在values下创建一XML资源文件



9.在XML文件里,定义标头和属性

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.     <!--指定这些属性都是谁的的,注意这里添加完属性要在自己定义View类构造方法里应用-->  
  4.     <!--注意:写完这个属性后,布局XML文件要想应用的话,必须要同步Gradle文件-->  
  5.     <declare-styleable name="Indicator">  
  6.         <!--定义控件显示圆的数量  参数format是类型-->  
  7.         <attr name="setNumber" format="integer"></attr>  
  8.         <!--定义控件显示圆的半径  参数format是类型-->  
  9.         <attr name="setRadius" format="integer"></attr>  
  10.     </declare-styleable>  
  11. </resources>  



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值