Android pageview无限轮播及带动画指示器

下载链接;https://download.csdn.net/download/ink_s/13721036

https://github.com/inksnow/ViewPageIndicator

效果图

 

 

指示器:

参考 https://github.com/yanyiqun001/bannerDot

package com.ink.viewpageindicator;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Interpolator;

import androidx.annotation.Nullable;

/**
 * ProjectName:    test
 * Package:        com.ink.viewpageindicator
 * ClassName:      IndicatorGroup
 * Description:     java类作用描述
 * CreateDate:     2020-12-16 16:47
 * UpdateDate:     2020-12-16 16:47
 * UpdateRemark:   更新说明
 * Version:        1.0
 * 参考
 * https://github.com/yanyiqun001/bannerDot
 */
public class IndicatorGroup extends View {
    //第一阶段运动
    private int MOVE_STEP_ONE = 1;
    //第二阶段运动
    private int MOVE_STEP_TWO = 2;
    //向左滚动
    public static int DIRECTION_LEFT = 1;
    //向右滚动
    public static int DIRECTION_RIGHT = 2;

    //间隔距离(两圆之间的空白距离)
    private float distance = 100;
    //起始圆初始半径,未选中
    private float mDefaultRadius = 50;
    //起始圆初始半径,选中
    private float mSelectRadius = 60;

    //选中的画笔
    private Paint mSelectPaint;
    //选中颜色
    private int mSelectColor;
    //未选中的画笔
    private Paint mDefaultPaint;
    //未选中颜色
    private int mDefaultColor;


    //当前选中的路径
    private Path mSelectPath = new Path();
    //将要选中的路径
    private Path mDefaultPath = new Path();

    //圆点总个数
    private int count = 1;
    //当前选中的下标
    private int mSelectIndex = 0;

    //整体运动进度 也是原始进度
    private float mOriginProgress = 0;


    //进度前50%,起始圆由大到等于辅助圆
    private float mProgress1 = 0;
    //进度后50%
    private float mProgress2 = 0;
    //当前方向
    private int mDrection = 2;

    private Path mPath = new Path();
    private Path mPath2 = new Path();
    //起始圆变化半径(由大到小)
    private float mChangeRadius;
    //辅助圆变化半径(由小到大)
    private float mSupportChangeRadius;
    //起始圆圆心坐标(固定)
    float mCenterPointX;
    float mCenterPointY;
    //辅助圆圆心坐标(慢慢远离起始圆圆心,到达间隔距离)
    float mSupportCircleX;
    float mSupportCircleY;

    //起始圆变化半径(由大到小)
    private float mChangeRadius2;
    //辅助圆变化半径(由小到大)
    private float mSupportChangeRadius2;
    //起始圆圆心坐标(固定)
    float mCenterPointX2;
    float mCenterPointY2;
    //辅助圆圆心坐标(慢慢远离起始圆圆心,到达间隔距离)
    float mSupportCircleX2;
    float mSupportCircleY2;

    //插值器
    // AccelerateInterpolator 动画从开始到结束,变化率是一个加速的过程。
    //DecelerateInterpolator:动画从开始到结束,变化率是一个减速的过程。
    //CycleInterpolator:动画从开始到结束,变化率是循环给定次数的正弦曲线。
    //AccelerateDecelerateInterpolator:动画从开始到结束,变化率是先加速后减速的过程。
    Interpolator interpolator = new AccelerateDecelerateInterpolator();
    public IndicatorGroup(Context context) {
        this(context, (AttributeSet) null);
    }

    public IndicatorGroup(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public IndicatorGroup(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray typedArray = this.getContext().obtainStyledAttributes(attrs, R.styleable.IndicatorGroup);
        this.distance = typedArray.getDimension(R.styleable.IndicatorGroup_distance, 100);
        this.mDefaultRadius = typedArray.getDimension(R.styleable.IndicatorGroup_defaultRadius, 20);
        this.mSelectRadius = typedArray.getDimension(R.styleable.IndicatorGroup_selectRadius, 25);
        this.mDefaultColor = typedArray.getColor(R.styleable.IndicatorGroup_defaultColor, 0XFF555555);
        this.mSelectColor = typedArray.getColor(R.styleable.IndicatorGroup_selectColor, 0XFFFF0000);
        typedArray.recycle();
        initPaint();
        moveToRight();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //总高度,最大圆的直径
        int h = (int) Math.ceil(mSelectRadius * 2);
        int w = (int) Math.ceil(count * mSelectRadius * 2 + (count - 1) * distance);
        if (w < 0) {
            w = 0;
        }
       // Log.e("onMeasure", "h:   " + h + "    w :" + w);
        setMeasuredDimension(w, h);

    }


    private void initPaint() {
        //选中的画笔
        mSelectPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mSelectPaint.setColor(mSelectColor);
        mSelectPaint.setStyle(Paint.Style.FILL);
        mSelectPaint.setAntiAlias(true);//抗锯齿
        mSelectPaint.setDither(true);//防抖动
        //未选中的画笔
        mDefaultPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mDefaultPaint.setColor(mDefaultColor);
        mDefaultPaint.setStyle(Paint.Style.FILL);
        mDefaultPaint.setAntiAlias(true);//抗锯齿
        mDefaultPaint.setDither(true);//防抖动
    }


    public void reset() {
        initPaint();

        //整体运动进度 也是原始进度
        mOriginProgress = 0;
        //进度前50%,起始圆由大到等于辅助圆
        mProgress1 = 0;
        //进度后50%
        mProgress2 = 0;
        //当前方向,1左2右
        mDrection = 2;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //canvas.drawColor(0, PorterDuff.Mode.CLEAR);
        //canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
        canvas.save();
        canvas.translate((float) this.getPaddingLeft(), (float) this.getPaddingTop());

        //先画出未选中的(不包括未选中将要选中的),即画n-2个未选中的圆
        for (int i = 0; i < count; i++) {
            if (mDrection == DIRECTION_RIGHT) {
                //向右,不画当前选中的及下一个的
                if (i != mSelectIndex && i != mSelectIndex + 1) {
                    canvas.drawCircle(this.getCenterPointAt(i), mSelectRadius, mDefaultRadius, mDefaultPaint);
                }
            } else {
                //向左,不画当前选中的及前一个的
                if (i != mSelectIndex && i != mSelectIndex - 1) {
                    canvas.drawCircle(this.getCenterPointAt(i), mSelectRadius, mDefaultRadius, mDefaultPaint);
                }
            }

        }



        canvas.drawCircle(this.mSupportCircleX2, this.mSupportCircleY2, this.mSupportChangeRadius2, this.mDefaultPaint);
        canvas.drawCircle(this.mCenterPointX2, this.mCenterPointY2, this.mChangeRadius2, this.mDefaultPaint);
        canvas.drawPath(this.mPath2, this.mDefaultPaint);

        canvas.drawCircle(this.mSupportCircleX, this.mSupportCircleY, this.mSupportChangeRadius, this.mSelectPaint);
        canvas.drawCircle(this.mCenterPointX, this.mCenterPointY, this.mChangeRadius, this.mSelectPaint);
        canvas.drawPath(this.mPath, this.mSelectPaint);

        canvas.restore();
    }


    public void setCount(int count){
        this.count = count;
    }

    public void setSelectIndex(int index){
        this.mSelectIndex = index;
        reset();
        moveToRight();
    }

    /**
    * 进度百分比,方向,当前选中的下标
    * */
    public void setProgress(float progress,int direction,int index) {
        //Log.e("progress","progress:" + progress);
        mOriginProgress=progress;
        mDrection = direction;
        mSelectIndex = index;
//        if(progress ==0.0f ){
//            mOriginProgress = 0;
//            //进度前50%,起始圆由大到等于辅助圆
//            mProgress1 = 0;
//            //进度后50%
//            mProgress2 = 0;
//            moveToRight();
//        }else
            if(progress<=0.5){
            mProgress1 = progress/0.5f;
            mProgress2=0;
        }else{
            mProgress2=(progress-0.5f)/0.5f;
            mProgress1=1;
        }
        if(mDrection==DIRECTION_RIGHT) {
            moveToRight();
        }else{
            moveToLeft();
        }
        invalidate();
    }



    private void moveToRight() {
        mPath.reset();
        mPath2.reset();
        float mRadiusProgress = interpolator.getInterpolation(mOriginProgress);

        //当前选中的变化

        //起始圆半径
        mChangeRadius = getValue(mSelectRadius, 0, mRadiusProgress);
        //起始圆圆心
        mCenterPointX = getValue(getCenterPointAt(mSelectIndex), getCenterPointAt(mSelectIndex+1) - mSelectRadius, MOVE_STEP_TWO);
        mCenterPointY = mSelectRadius;

        //起点与起始圆圆心间的角度,由45度变为0度
        double radian = Math.toRadians(getValue(45, 0, MOVE_STEP_ONE));
        //X轴距离圆心距离
        float mX = (float) (Math.sin(radian) * mChangeRadius);
        //Y轴距离圆心距离
        float mY = (float) (Math.cos(radian) * mChangeRadius);


        //辅助圆圆心
        mSupportCircleX = getValue(getCenterPointAt(mSelectIndex) + mSelectRadius, getCenterPointAt(mSelectIndex+1), MOVE_STEP_ONE);
        mSupportCircleY = mSelectRadius;

        //辅助圆半径
        mSupportChangeRadius = getValue(0, mSelectRadius, mRadiusProgress);
        //终点与辅助圆圆心间的角度,由0度变为45度
        double supportRadian = Math.toRadians(getValue(0, 45, MOVE_STEP_TWO));
        //X轴距离圆心距离
        float mSupportRadianX = (float) (Math.sin(supportRadian) * mSupportChangeRadius);
        //Y轴距离圆心距离
        float mSupportRadianY = (float) (Math.cos(supportRadian) * mSupportChangeRadius);
        //起点
        float mStartX = mCenterPointX + mX;
        float mStartY = mCenterPointY - mY;

        //终点
        float endPointX = mSupportCircleX - mSupportRadianX;
        float endPointY = mSelectRadius - mSupportRadianY;
        //控制点
        float controlPointX = getValueForAll(getCenterPointAt(mSelectIndex) + mSelectRadius, getCenterPointAt(mSelectIndex+1));
        float controlPointY = mSelectRadius;
        //移动至起点
        mPath.moveTo(mStartX, mStartY);
        //形成闭合区域
        mPath.quadTo(controlPointX, controlPointY, endPointX, endPointY);
        mPath.lineTo(endPointX, mSelectRadius + mSupportRadianY);
        mPath.quadTo(controlPointX, controlPointY, mStartX, mStartY + 2 * mY);
        mPath.lineTo(mStartX, mStartY);


        //将要选中的变化
        //index * mSelectRadius * 2 + mSelectRadius + index * distance

        //起始圆半径
        mChangeRadius2 = getValue(mDefaultRadius, 0, mRadiusProgress);
        //起始圆圆心
        mCenterPointX2 = getValue(getCenterPointAt(mSelectIndex+1), getCenterPointAt(mSelectIndex) + mDefaultRadius, MOVE_STEP_TWO);
        mCenterPointY2 = mSelectRadius;

        //起点与起始圆圆心间的角度,由45度变为0度
         radian = Math.toRadians(getValue(45, 0, MOVE_STEP_ONE));
        //X轴距离圆心距离
         mX = (float) (Math.sin(radian) * mChangeRadius2);
        //Y轴距离圆心距离
         mY = (float) (Math.cos(radian) * mChangeRadius2);


        //辅助圆圆心
        mSupportCircleX2 = getValue(getCenterPointAt(mSelectIndex+1) - mDefaultRadius, getCenterPointAt(mSelectIndex), MOVE_STEP_ONE);
        mSupportCircleY2 = mSelectRadius;

        //辅助圆半径
        mSupportChangeRadius2 = getValue(0, mDefaultRadius, mRadiusProgress);
        //终点与辅助圆圆心间的角度,由0度变为45度
         supportRadian = Math.toRadians(getValue(0, 45, MOVE_STEP_TWO));
        //X轴距离圆心距离
         mSupportRadianX = (float) (Math.sin(supportRadian) * mSupportChangeRadius2);
        //Y轴距离圆心距离
         mSupportRadianY = (float) (Math.cos(supportRadian) * mSupportChangeRadius2);
        //起点
         mStartX = mCenterPointX2 - mX;
         mStartY = mCenterPointY2 - mY;

        //终点
         endPointX = mSupportCircleX2 + mSupportRadianX;
         endPointY = mSelectRadius - mSupportRadianY;
        //控制点
         controlPointX = getValueForAll(getCenterPointAt(mSelectIndex+1)-mDefaultRadius, getCenterPointAt(mSelectIndex) );
         controlPointY = mSelectRadius;

        //移动至起点
        mPath2.moveTo(mStartX, mStartY);
        mPath2.quadTo(controlPointX, controlPointY, endPointX, endPointY);
        mPath2.lineTo(endPointX, mSelectRadius + mSupportRadianY);
        mPath2.quadTo(controlPointX, controlPointY, mStartX, mStartY + 2 * mY);
        mPath2.lineTo(mStartX, mStartY);

    }




    private void moveToLeft() {
        mPath.reset();
        mPath2.reset();
        float mRadiusProgress = interpolator.getInterpolation(mOriginProgress);

        //将要选中的变化

        //起始圆半径
        mChangeRadius2 = getValue(mDefaultRadius, 0, mRadiusProgress);
        //起始圆圆心
        mCenterPointX2 = getValue(getCenterPointAt(mSelectIndex-1), getCenterPointAt(mSelectIndex) - mDefaultRadius, MOVE_STEP_TWO);
        mCenterPointY2 = mSelectRadius;

        //起点与起始圆圆心间的角度,由45度变为0度
        double radian = Math.toRadians(getValue(45, 0, MOVE_STEP_ONE));
        //X轴距离圆心距离
        float mX = (float) (Math.sin(radian) * mChangeRadius2);
        //Y轴距离圆心距离
        float mY = (float) (Math.cos(radian) * mChangeRadius2);


        //辅助圆圆心
        mSupportCircleX2 = getValue(getCenterPointAt(mSelectIndex-1) + mDefaultRadius, getCenterPointAt(mSelectIndex), MOVE_STEP_ONE);
        mSupportCircleY2 = mSelectRadius;

        //辅助圆半径
        mSupportChangeRadius2 = getValue(0, mDefaultRadius, mRadiusProgress);
        //终点与辅助圆圆心间的角度,由0度变为45度
        double supportRadian = Math.toRadians(getValue(0, 45, MOVE_STEP_TWO));
        //X轴距离圆心距离
        float mSupportRadianX = (float) (Math.sin(supportRadian) * mSupportChangeRadius2);
        //Y轴距离圆心距离
        float mSupportRadianY = (float) (Math.cos(supportRadian) * mSupportChangeRadius2);
        //起点
        float mStartX = mCenterPointX2 + mX;
        float mStartY = mCenterPointY2 - mY;

        //终点
        float endPointX = mSupportCircleX2 - mSupportRadianX;
        float endPointY = mSelectRadius - mSupportRadianY;
        //控制点
        float controlPointX = getValueForAll(getCenterPointAt(mSelectIndex-1) + mDefaultRadius, getCenterPointAt(mSelectIndex));
        float controlPointY = mSelectRadius;
        //移动至起点
        mPath2.moveTo(mStartX, mStartY);
        //形成闭合区域
        mPath2.quadTo(controlPointX, controlPointY, endPointX, endPointY);
        mPath2.lineTo(endPointX, mSelectRadius + mSupportRadianY);
        mPath2.quadTo(controlPointX, controlPointY, mStartX, mStartY + 2 * mY);
        mPath2.lineTo(mStartX, mStartY);





        //当前选中的变化
        //起始圆半径
        mChangeRadius = getValue(mSelectRadius, 0, mRadiusProgress);
        //起始圆圆心
        mCenterPointX = getValue(getCenterPointAt(mSelectIndex), getCenterPointAt(mSelectIndex-1) + mSelectRadius, MOVE_STEP_TWO);
        mCenterPointY = mSelectRadius;

        //起点与起始圆圆心间的角度,由45度变为0度
        radian = Math.toRadians(getValue(45, 0, MOVE_STEP_ONE));
        //X轴距离圆心距离
        mX = (float) (Math.sin(radian) * mChangeRadius);
        //Y轴距离圆心距离
        mY = (float) (Math.cos(radian) * mChangeRadius);


        //辅助圆圆心
        mSupportCircleX = getValue(getCenterPointAt(mSelectIndex) - mSelectRadius, getCenterPointAt(mSelectIndex-1), MOVE_STEP_ONE);
        mSupportCircleY = mSelectRadius;

        //辅助圆半径
        mSupportChangeRadius = getValue(0, mSelectRadius, mRadiusProgress);
        //终点与辅助圆圆心间的角度,由0度变为45度
        supportRadian = Math.toRadians(getValue(0, 45, MOVE_STEP_TWO));
        //X轴距离圆心距离
        mSupportRadianX = (float) (Math.sin(supportRadian) * mSupportChangeRadius);
        //Y轴距离圆心距离
        mSupportRadianY = (float) (Math.cos(supportRadian) * mSupportChangeRadius);
        //起点
        mStartX = mCenterPointX - mX;
        mStartY = mCenterPointY - mY;

        //终点
        endPointX = mSupportCircleX + mSupportRadianX;
        endPointY = mSelectRadius - mSupportRadianY;
        //控制点
        controlPointX = getValueForAll(getCenterPointAt(mSelectIndex), getCenterPointAt(mSelectIndex-1) + mSelectRadius);
        controlPointY = mSelectRadius;

        //移动至起点
        mPath.moveTo(mStartX, mStartY);
        mPath.quadTo(controlPointX, controlPointY, endPointX, endPointY);
        mPath.lineTo(endPointX, mSelectRadius + mSupportRadianY);
        mPath.quadTo(controlPointX, controlPointY, mStartX, mStartY + 2 * mY);
        mPath.lineTo(mStartX, mStartY);


    }


    /**
     * 获取当前值(适用分阶段变化的值)
     * @param start 初始值
     * @param end  终值
     * @param step  第几活动阶段
     * @return
     */
    public float getValue(float start, float end, int step) {

        //当第一阶段的时候,mProgress2 = 0,第二阶段的时候,mProgress1 = 1
        //第一阶段的时候mProgress2 = 0 起始点圆心不变

        if(step==MOVE_STEP_ONE) {
            return start + (end - start) * mProgress1;
        }else{
            return start + (end - start) * mProgress2;
        }
    }
    /**
     * 获取当前值(适用全过程变化的值)
     * @param start 初始值
     * @param end  终值
     * @return
     */
    public float getValueForAll(float start, float end){
        return start + (end - start) * mOriginProgress;
    }

    /**
     * 通过进度获取当前值
     * @param start 初始值
     * @param end 终值
     * @param progress 当前进度
     * @return
     */
    public float getValue(float start, float end, float progress) {
        return start + (end - start) * progress;
    }

    private float getCenterPointAt(int index) {

        return index * mSelectRadius * 2 + mSelectRadius + index * distance;
    }


}

自定义属性attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="IndicatorGroup">
        <attr name="distance" format="dimension" />
        <attr name="defaultRadius" format="dimension" />
        <attr name="selectRadius" format="dimension" />
        <attr name="selectColor" format="color" />
        <attr name="defaultColor" format="color" />
    </declare-styleable>
</resources>

 

无限循环adapter1,首尾各多加一条数据

package com.ink.viewpageindicator;

import android.content.Context;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
import android.widget.Scroller;

import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;

import java.lang.reflect.Field;
import java.util.List;

//无限循环滚动 ViewPager

//参考
//https://blog.csdn.net/xia236326/article/details/84072247

class ViewAdapter extends PagerAdapter {
    private List<View> datas;

    private ViewPager viewPager;
    private Context context;

    public ViewAdapter(List<View> list, ViewPager viewPager, Context context) {
        datas=list;
        this.viewPager = viewPager;
        this.context = context;
    }


    @Override
    public int getCount() {
        return datas.size();
    }

    //删除指定位置的页面;适配器负责从view容器中删除view,然而它只保证在finishUpdate(ViewGroup)返回时才完成。
    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        // 把ImageView从ViewPager中移除掉
        viewPager.removeView(datas.get(position));
        //super.destroyItem(container, position, object);
    }

    //是否获取缓存
    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == object;
    }


    //实例化Item
    //在指定的位置创建页面;适配器负责添加view到这个容器中,然而它只保证在finishUpdate(ViewGroup)返回时才完成。
    @Override
    public Object instantiateItem(ViewGroup container, int position) {



      View  view = datas.get(position);
        viewPager.addView(view);
        return view;
    }

    @Override
    public int getItemPosition(Object object) {
        return POSITION_NONE;
    }

    //无论是创建view添加到容器中  还是 销毁view 都是在此方法结束之后执行的
    @Override
    public void finishUpdate(ViewGroup container) {
        super.finishUpdate(container);

//        int position = viewPager.getCurrentItem();
//        Log.e("position",position+"");
//
//        if (position == 0) {
//            position = datas.size() - 2;
//            viewPager.setCurrentItem(position,false);
//        } else if (position == datas.size() - 1) {
//            position = 1;
//            viewPager.setCurrentItem(position,false);
//        }
    }



}

无限循环adapter2,返回最大值条目个数

package com.ink.viewpageindicator;

import android.content.Context;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;

import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;

import java.util.List;



//2张图时右滑会出现空白,滑动结束后才显示,因为2张图片时左右两边用的是同一个view,parent.removeView(v)后其中一个就没有显示了
//解决办法,判断是否小于3张,是的话就复制扩充数据


class ViewAdapter2 extends PagerAdapter {
    private List<View> datas;

    private ViewPager viewPager;
    private Context context;

    public ViewAdapter2(List<View> list, ViewPager viewPager, Context context) {
        datas=list;
        this.viewPager = viewPager;
        this.context = context;

        //2张图时右滑会出现空白,滑动结束后才显示,因为2张图片时左右两边用的是同一个view,parent.removeView(v)后其中一个就没有显示了
        //解决办法,判断是否小于3张,是的话就复制扩充数据
        //view 不能克隆,只能在创建数据是如果是小于三个的数据是多创建数据
//        if(datas.size()==1){
//            datas.add(1,datas.get(0));
//            datas.add(2,datas.get(0));
//        }else if(datas.size()==2){
//            datas.add(2,datas.get(0));
//            datas.add(3,datas.get(1));
//        }

    }


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

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

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        View v = datas.get(position % datas.size());
        ViewGroup parent = (ViewGroup) v.getParent();
        if (parent != null) {
            parent.removeView(v);
        }
        container.addView(v);
        return v;
    }
    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
//        Log.e(this.getClass().getName(),"destroyItem。position="+position);
//        position = position % datas.size();
//        container.removeView(datas.get(position));
//        Log.e(this.getClass().getName(),"destroyItem.view_size="+container.getChildCount());
    }




}

 

 

正常adapter,不能循环

package com.ink.viewpageindicator;

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;

import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;

import java.util.List;


class ViewAdapter3 extends PagerAdapter {
    private List<View> datas;

    private ViewPager viewPager;
    private Context context;

    public ViewAdapter3(List<View> list, ViewPager viewPager, Context context) {
        datas=list;
        this.viewPager = viewPager;
        this.context = context;
    }


    @Override
    public int getCount() {
        return datas.size();
    }

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

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        View view=datas.get(position);
        container.addView(view);
        return view;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView(datas.get(position));
    }


}

 

 

activity 

package com.ink.viewpageindicator;

import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager.widget.ViewPager;

import android.annotation.SuppressLint;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.ImageView;
import android.widget.Scroller;
import android.widget.TextView;

import java.lang.reflect.Field;
import java.util.ArrayList;


public class MainActivity extends AppCompatActivity {

    private ViewPager viewPager1, viewPager2, viewPager3;
    private IndicatorGroup indicatorGroup1, indicatorGroup2,indicatorGroup3;
    private ViewAdapter viewAdapter1;
    private ViewAdapter2 viewAdapter2;
    private ViewAdapter3 viewAdapter3;
    private int mViewPagerIndex1;
    private int mViewPagerIndex2;
    private int mViewPagerIndex3;
    //自动滑动
    private boolean isAuto = true;

    ArrayList<View> views1 = new ArrayList<>();
    ArrayList<View> views2 = new ArrayList<>();
    ArrayList<View> views3 = new ArrayList<>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        viewPager1 = findViewById(R.id.ViewPager1);
        viewPager2 = findViewById(R.id.ViewPager2);
        viewPager3 = findViewById(R.id.ViewPager3);
        indicatorGroup1 = findViewById(R.id.IndicatorGroup1);
        indicatorGroup2 = findViewById(R.id.IndicatorGroup2);
        indicatorGroup3 = findViewById(R.id.IndicatorGroup3);

        //设置滑动速度,默认的切换太快了,
        // (滑动速度影响手动滑动,手动滑动到一半后,后面的会自动滑动到下一页或重置到当前页,这个速度影响这部分的自动滑动速度)
        try {
            Field field = ViewPager.class.getDeclaredField("mScroller");
            field.setAccessible(true);
            FixedSpeedScroller scroller = new FixedSpeedScroller(viewPager1.getContext(),
                    new AccelerateInterpolator());
            field.set(viewPager1, scroller);
            scroller.setmDuration(600);
        } catch (Exception e) {

        }

        try {
            Field field = ViewPager.class.getDeclaredField("mScroller");
            field.setAccessible(true);
            FixedSpeedScroller scroller = new FixedSpeedScroller(viewPager2.getContext(),
                    new AccelerateInterpolator());
            field.set(viewPager2, scroller);
            scroller.setmDuration(600);
        } catch (Exception e) {

        }


        //无限循环
        initPagerData1();
        viewAdapter1 = new ViewAdapter(views1, viewPager1, this);
        viewPager1.setAdapter(viewAdapter1);
        viewPager1.setCurrentItem(1);
        mViewPagerIndex1 = 1;
        indicatorGroup1.setCount(views1.size() - 2);
        indicatorGroup1.setSelectIndex(mViewPagerIndex1 - 1);
        viewPager1.setOnPageChangeListener(pageChangeListener1);
        mHandler.removeMessages(111);
        mHandler.sendEmptyMessageDelayed(111, 2000);


        initPagerData2();
        //2张图时右滑会出现空白,滑动结束后才显示,因为2张图片时左右两边用的是同一个view,parent.removeView(v)后其中一个就没有显示了
        //解决办法,判断是否小于3张,是的话就复制扩充数据
        viewAdapter2 = new ViewAdapter2(views2, viewPager2, this);
        viewPager2.setAdapter(viewAdapter2);
        viewPager2.setCurrentItem(Integer.MAX_VALUE / 2 -(Integer.MAX_VALUE / 2 % views2.size()));
        indicatorGroup2.setCount(views2.size());
        indicatorGroup2.setSelectIndex(0);
        viewPager2.setOnPageChangeListener(pageChangeListener2);
        mHandler.removeMessages(333);
        mHandler.sendEmptyMessageDelayed(333, 2000);

        initPagerData3();
        viewAdapter3 = new ViewAdapter3(views3, viewPager3, this);
        viewPager3.setAdapter(viewAdapter3);
        viewPager3.setCurrentItem(0);
        indicatorGroup3.setCount(views3.size());
        indicatorGroup3.setSelectIndex(0);
        viewPager3.setOnPageChangeListener(pageChangeListener3);

    }




    ViewPager.OnPageChangeListener pageChangeListener1 = new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {


            //position等于当前选中的,向右滑(下一张是右边的)
            //position小于当前选中的,向左滑(下一张是左边的)
            if (position == mViewPagerIndex1) {
                if (position == views1.size() - 2) {
                    if (positionOffset == 0.0f) {
                        indicatorGroup1.setProgress(positionOffset, 2, 0);
                    }
                } else {
                    indicatorGroup1.setProgress(positionOffset, 2, mViewPagerIndex1 - 1);
                }

            } else if (position < mViewPagerIndex1) {
                if (mViewPagerIndex1 - position == 1) {
                    if (position == 0) {
                        if (positionOffset == 0.0f) {
                            indicatorGroup1.setProgress(positionOffset, 2, views1.size() - 2);
                        }
                    } else {
                        indicatorGroup1.setProgress(1 - positionOffset, 1, mViewPagerIndex1 - 1);
                    }
                } else {
                    indicatorGroup1.setProgress(1 - positionOffset, 1, position);
                }

            } else {
                indicatorGroup1.setProgress(1 - positionOffset, 1, position);
            }
           // Log.e("progress", "position:" + position + "mViewPagerIndex1:" + mViewPagerIndex1 + "    positionOffset: " + positionOffset);


        }

        @Override
        public void onPageSelected(int position) {
            //Log.e("onPageSelected", position + "");
            //mViewPagerIndex1 = viewPager1.getCurrentItem();
            //延时动画时长毫秒检测是否需要切换,不然自动切换时最后一张到第一张没有动画效果
            //可能原因是viewPager1.setCurrentItem后onPageSelected立马回调,但是此时动画还没有完成,如果直接在viewPager1.setCurrentItem到第一张或最后一张,则没有动画
            //如果手动滑动的话,滑动到多一半会自动滑动完最后一部分,但是用时没有全部用时那么多,可能会造成切换延时
            //只手动滑动的话,在adapter的finishUpdate中检测最好。
            //只自动滑动的话可以这样设置延时,
            //如果手动加自动结合的话,可以把滑动时间设小一点。
//            isAuto = true;
//            mHandler.removeMessages(222);
//            mHandler.sendEmptyMessageDelayed(222,600);
//            mHandler.removeMessages(111);
//            mHandler.sendEmptyMessageDelayed(111, 3000);
        }

        @Override
        public void onPageScrollStateChanged(int state) {
            //Log.e("onPageScrollState", state + "");
            //2自动滑动开始(手指滑动到一半再放开也会触发)
            //1手指滑动开始
            //0滑动结束
            //在手指滑到一半还有一半自动滑到下一页的情况下,此时getCurrentItem会立马变为下一页,但是动画还没有结束
            //或者setCurrentItem到下一页也会立马getCurrentItem变为下一页,但是动画还没结束
            //所以不要在state = 2时赋值mViewPagerIndex1


            if (state == 1) {
                mViewPagerIndex1 = viewPager1.getCurrentItem();
                mHandler.removeMessages(111);
                isAuto = false;

            } else if (state == 0) {
                mViewPagerIndex1 = viewPager1.getCurrentItem();
                //滑动结束
                isAuto = true;
                mHandler.sendEmptyMessage(222);
                mHandler.removeMessages(111);
                mHandler.sendEmptyMessageDelayed(111, 3000);

            }

        }
    };



    ViewPager.OnPageChangeListener pageChangeListener2= new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {


            //position等于当前选中的,向右滑(下一张是右边的)
            //position小于当前选中的,向左滑(下一张是左边的)
            if (position == mViewPagerIndex2) {
                if (position%views2.size() == views2.size()-1 ) {
                    //最后一个圆点,回到第一个圆点
                    if (positionOffset == 0.0f) {
                        indicatorGroup2.setProgress(positionOffset, 2, 0);
                    }
                } else {
                    indicatorGroup2.setProgress(positionOffset, 2, position%views2.size());
                }

            } else if (position < mViewPagerIndex2) {

                //第一个圆点左滑,跳到最后一个圆点
                    if (mViewPagerIndex2%views2.size() == 0) {
                        if (positionOffset == 0.0f) {
                            indicatorGroup2.setProgress(positionOffset, 2, views2.size()-1);
                        }
                    } else {
                        indicatorGroup2.setProgress(1 - positionOffset, 1, position%views2.size()+1);
                    }


            }else {
                indicatorGroup2.setProgress(positionOffset, 2, position%views2.size());
            }

//            Log.e("progress", "position:" + position+ "mViewPagerIndex2:" + mViewPagerIndex2);
//
//            Log.e("progress", "position:" + position%views2.size() + "mViewPagerIndex2:" + mViewPagerIndex2%views2.size() + "    positionOffset: " + positionOffset);


        }

        @Override
        public void onPageSelected(int position) {
        }

        @Override
        public void onPageScrollStateChanged(int state) {
            //Log.e("onPageScrollState", state + "");
            //2自动滑动开始(手指滑动到一半再放开也会触发)
            //1手指滑动开始
            //0滑动结束
            //在手指滑到一半还有一半自动滑到下一页的情况下,此时getCurrentItem会立马变为下一页,但是动画还没有结束
            //或者setCurrentItem到下一页也会立马getCurrentItem变为下一页,但是动画还没结束
            //所以不要在state = 2时赋值mViewPagerIndex1


            if (state == 1) {
                mViewPagerIndex2 = viewPager2.getCurrentItem();
                mHandler.removeMessages(333);

            } else if (state == 0) {
                mViewPagerIndex2 = viewPager2.getCurrentItem();
                //滑动结束
                mHandler.removeMessages(333);
                mHandler.sendEmptyMessageDelayed(333, 3000);

            }

        }
    };




    ViewPager.OnPageChangeListener pageChangeListener3= new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {


            //position等于当前选中的,向右滑(下一张是右边的)
            //position小于当前选中的,向左滑(下一张是左边的)
            if (position == mViewPagerIndex3) {
                indicatorGroup3.setProgress(positionOffset, 2, position);

//                if (position == views2.size()-1 ) {
//                    //最后一个圆点,回到第一个圆点
//                    if (positionOffset == 0.0f) {
//                        indicatorGroup2.setProgress(positionOffset, 2, 0);
//                    }
//                } else {
//                    indicatorGroup2.setProgress(positionOffset, 2, position%views2.size());
//                }

            } else if (position < mViewPagerIndex3) {
                indicatorGroup3.setProgress(1 - positionOffset, 1, position+1);
                //第一个圆点左滑,跳到最后一个圆点
//                if (mViewPagerIndex2%views2.size() == 0) {
//                    if (positionOffset == 0.0f) {
//                        indicatorGroup2.setProgress(positionOffset, 2, views2.size()-1);
//                    }
//                } else {
//                    indicatorGroup2.setProgress(1 - positionOffset, 1, position%views2.size()+1);
//                }

            }else {
                indicatorGroup3.setProgress(positionOffset, 2, position);
            }

//            Log.e("progress", "position:" + position+ "mViewPagerIndex2:" + mViewPagerIndex2);
//
//            Log.e("progress", "position:" + position%views2.size() + "mViewPagerIndex2:" + mViewPagerIndex2%views2.size() + "    positionOffset: " + positionOffset);


        }

        @Override
        public void onPageSelected(int position) {
        }

        @Override
        public void onPageScrollStateChanged(int state) {
            if (state == 1) {
                mViewPagerIndex3 = viewPager3.getCurrentItem();
            } else if (state == 0) {
                mViewPagerIndex3 = viewPager3.getCurrentItem();
            }

        }
    };



    @SuppressLint("HandlerLeak")
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 111:
                    //Log.e("viewPager1", viewPager1.getCurrentItem() + "");
                    viewPager1.setCurrentItem(viewPager1.getCurrentItem() + 1, true);
                    break;
                case 222:
                    int positions = viewPager1.getCurrentItem();
                    //Log.e("position", positions + "");

                    if (positions == 0) {
                        positions = views1.size() - 2;
                        viewPager1.setCurrentItem(positions, false);
                    } else if (positions == views1.size() - 1) {
                        positions = 1;
                        viewPager1.setCurrentItem(positions, false);
                    }

                    break;
                case 333:
                    viewPager2.setCurrentItem(viewPager2.getCurrentItem() + 1, true);
                    break;
            }
        }
    };


    private void initPagerData1() {
        TextView textView = new TextView(this);
        textView.setBackgroundColor(0XFF722ed1);
        textView.setText("5");
        textView.setTextSize(50);
        textView.setGravity(Gravity.CENTER);
        textView.setTextColor(0XFFFFFFFF);
        views1.add(textView);

        textView = new TextView(this);
        textView.setBackgroundColor(0XFFfa541c);
        textView.setText("1");
        textView.setTextSize(50);
        textView.setGravity(Gravity.CENTER);
        textView.setTextColor(0XFFFFFFFF);
        views1.add(textView);

        textView = new TextView(this);
        textView.setBackgroundColor(0XFF13c2c2);
        textView.setText("2");
        textView.setTextSize(50);
        textView.setGravity(Gravity.CENTER);
        textView.setTextColor(0XFFFFFFFF);
        views1.add(textView);

        textView = new TextView(this);
        textView.setBackgroundColor(0XFF1890ff);
        textView.setTextSize(50);
        textView.setGravity(Gravity.CENTER);
        textView.setTextColor(0XFFFFFFFF);
        textView.setText("3");
        views1.add(textView);

        textView = new TextView(this);
        textView.setBackgroundColor(0xffeb2f96);
        textView.setTextSize(50);
        textView.setGravity(Gravity.CENTER);
        textView.setTextColor(0XFFFFFFFF);
        textView.setText("4");
        views1.add(textView);

        textView = new TextView(this);
        textView.setBackgroundColor(0XFF722ed1);
        textView.setTextSize(50);
        textView.setGravity(Gravity.CENTER);
        textView.setTextColor(0XFFFFFFFF);
        textView.setText("5");
        views1.add(textView);

        textView = new TextView(this);
        textView.setBackgroundColor(0XFFfa541c);
        textView.setTextSize(50);
        textView.setGravity(Gravity.CENTER);
        textView.setTextColor(0XFFFFFFFF);
        textView.setText("1");
        views1.add(textView);


    }

    private void initPagerData2() {


        TextView textView = new TextView(this);
        textView.setBackgroundColor(0XFFfa541c);
        textView.setText("1");
        textView.setTextSize(50);
        textView.setGravity(Gravity.CENTER);
        textView.setTextColor(0XFFFFFFFF);
        views2.add(textView);

        textView = new TextView(this);
        textView.setBackgroundColor(0XFF13c2c2);
        textView.setText("2");
        textView.setTextSize(50);
        textView.setGravity(Gravity.CENTER);
        textView.setTextColor(0XFFFFFFFF);
        views2.add(textView);

        textView = new TextView(this);
        textView.setBackgroundColor(0XFF1890ff);
        textView.setTextSize(50);
        textView.setGravity(Gravity.CENTER);
        textView.setTextColor(0XFFFFFFFF);
        textView.setText("3");
        views2.add(textView);

        textView = new TextView(this);
        textView.setBackgroundColor(0xffeb2f96);
        textView.setTextSize(50);
        textView.setGravity(Gravity.CENTER);
        textView.setTextColor(0XFFFFFFFF);
        textView.setText("4");
        views2.add(textView);

        textView = new TextView(this);
        textView.setBackgroundColor(0XFF722ed1);
        textView.setTextSize(50);
        textView.setGravity(Gravity.CENTER);
        textView.setTextColor(0XFFFFFFFF);
        textView.setText("5");
        views2.add(textView);
    }

    private void initPagerData3() {


        TextView textView = new TextView(this);
        textView.setBackgroundColor(0XFFfa541c);
        textView.setText("1");
        textView.setTextSize(50);
        textView.setGravity(Gravity.CENTER);
        textView.setTextColor(0XFFFFFFFF);
        views3.add(textView);

        textView = new TextView(this);
        textView.setBackgroundColor(0XFF13c2c2);
        textView.setText("2");
        textView.setTextSize(50);
        textView.setGravity(Gravity.CENTER);
        textView.setTextColor(0XFFFFFFFF);
        views3.add(textView);

        textView = new TextView(this);
        textView.setBackgroundColor(0XFF1890ff);
        textView.setTextSize(50);
        textView.setGravity(Gravity.CENTER);
        textView.setTextColor(0XFFFFFFFF);
        textView.setText("3");
        views3.add(textView);

        textView = new TextView(this);
        textView.setBackgroundColor(0xffeb2f96);
        textView.setTextSize(50);
        textView.setGravity(Gravity.CENTER);
        textView.setTextColor(0XFFFFFFFF);
        textView.setText("4");
        views3.add(textView);

        textView = new TextView(this);
        textView.setBackgroundColor(0XFF722ed1);
        textView.setTextSize(50);
        textView.setGravity(Gravity.CENTER);
        textView.setTextColor(0XFFFFFFFF);
        textView.setText("5");
        views3.add(textView);
    }
    @Override
    protected void onDestroy() {
        mHandler.removeCallbacksAndMessages(null);
        super.onDestroy();
    }
}

 

 

activity xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="无限自动循环ViewPager + 指示器\n(原理:头尾各多加一条,viewPager.setCurrentItem(position,false)切换)" />

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">

        <androidx.viewpager.widget.ViewPager
            android:id="@+id/ViewPager1"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

        <com.ink.viewpageindicator.IndicatorGroup
            android:id="@+id/IndicatorGroup1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:layout_marginBottom="10dp" />
    </RelativeLayout>


    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="无限自动循环ViewPager + 指示器\n(原理:return Integer.MAX_VALUE)" />

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">

        <androidx.viewpager.widget.ViewPager
            android:id="@+id/ViewPager2"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

        <com.ink.viewpageindicator.IndicatorGroup
            android:id="@+id/IndicatorGroup2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            app:selectColor="#090909"
            app:defaultColor="#ffffff"
            app:defaultRadius="10dp"
            app:selectRadius="13dp"
            android:layout_marginBottom="10dp" />
    </RelativeLayout>





    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="正常ViewPager + 指示器" />


    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">

        <androidx.viewpager.widget.ViewPager
            android:id="@+id/ViewPager3"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

        <com.ink.viewpageindicator.IndicatorGroup
            android:id="@+id/IndicatorGroup3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_alignParentEnd="true"
            android:layout_marginEnd="20dp"
            app:selectColor="#090909"
            app:defaultColor="#ffffff"
            app:defaultRadius="5dp"
            app:distance="10dp"
            app:selectRadius="6dp"
            android:layout_marginBottom="10dp" />
    </RelativeLayout>


</LinearLayout>

 

 

自动以滚动速度Scroller 

package com.ink.viewpageindicator;

import android.content.Context;
import android.view.animation.Interpolator;
import android.widget.Scroller;

public class FixedSpeedScroller extends Scroller {
    private int mDuration = 1500;
                                                                                                            
    public FixedSpeedScroller(Context context) {
        super(context);
    }
                                                                                                            
    public FixedSpeedScroller(Context context, Interpolator interpolator) {
        super(context, interpolator);
    }
                                                                                                            
    @Override
    public void startScroll(int startX, int startY, int dx, int dy, int duration) {
        // Ignore received duration, use fixed one instead
        super.startScroll(startX, startY, dx, dy, mDuration);
    }
                                                                                                            
    @Override
    public void startScroll(int startX, int startY, int dx, int dy) {
        // Ignore received duration, use fixed one instead
        super.startScroll(startX, startY, dx, dy, mDuration);
    }
                                                                                                            
    public void setmDuration(int time) {
        mDuration = time;
    }
                                                                                                            
    public int getmDuration() {
        return mDuration;
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值