下载链接;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;
}
}