Android自定View实现滑动验证效果的代码
时间:2022-06-16 09:48:45|栏目:Android代码|点击: 次
效果图
自定义属性代码
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="MyCheckView"> <attr name="m_blockBg" format="reference" /><!--滑块背景图片--> <attr name="m_blockColor" format="color" /><!--滑块颜色--> <attr name="m_blockShadowLayer" format="color" /><!--滑块阴影颜色--> <attr name="m_proColor" format="color" /><!--进度条颜色--> <attr name="m_recColor" format="color" /><!--矩形背景色--> <attr name="m_circleSize" format="integer" /><!--圆角角度值--> </declare-styleable> </resources>
自定义View代码
public class MyCheckView extends View { private boolean isBlockArea = false; private boolean isMove = false; private boolean isFinish = false; private boolean isDown = false; private int mRight; private int startX = 0; /** * 滑块边距 */ private final int blockSize = SizeUtils.dp2px(5); /** * 相关属性 */ private int m_blockColor = Color.WHITE;//默认滑块颜色 private int m_blockShadowLayer = Color.parseColor("#D8D8D8");//默认滑块阴影色 private int m_proColor = Color.parseColor("#ff3159");//默认进度条颜色 private int m_recColor = Color.parseColor("#D8D8D8");//默认矩形颜色 private int blockDrawableId;//默认滑块背景图 /** * 矩形画笔 */ private final Paint recPaint = new Paint(); /** * 进度条画笔 */ private final Paint proPaint = new Paint(); /** * 滑块画笔 */ private final Paint blockPaint = new Paint(); /** * 圆角角度 */ private int circleSize = SizeUtils.dp2px(20); /** * 记录父控件宽度 */ private float parentWidth = 0f; /** * 矩形高度 */ private int proHeight; /** * 默认高度 */ private final int DEFAULT_HEIGHT = SizeUtils.dp2px(45); /** * 滑块宽度 */ private final int blockWidth = SizeUtils.dp2px(60); /** * 手指落下位置 */ private int dX; /** * 偏移距离 */ private int mX; /** * 接口回调 */ private FinishListener finishListener; public void setFinishListener(FinishListener finishListener) { this.finishListener = finishListener; } public MyCheckView(@NonNull Context context) { super(context); init(); } public MyCheckView(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); initParams(context, attrs); init(); } public MyCheckView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initParams(context, attrs); init(); } /** * 初始化自定义属性 * * @param context 上下文 * @param attrs 属性参数 */ private void initParams(Context context, AttributeSet attrs) { TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyCheckView); if (typedArray != null) { //获取滑块背景图片 blockDrawableId = typedArray.getResourceId(R.styleable.MyCheckView_m_blockBg, -1); //获取滑块颜色 m_blockColor = typedArray.getColor(R.styleable.MyCheckView_m_blockColor, m_blockColor); //滑块阴影色 m_blockShadowLayer = typedArray.getColor(R.styleable.MyCheckView_m_blockColor, m_blockShadowLayer); //进度条颜色 m_proColor = typedArray.getColor(R.styleable.MyCheckView_m_blockColor, m_proColor); //矩形颜色 m_recColor = typedArray.getColor(R.styleable.MyCheckView_m_blockColor, m_recColor); //圆角角度值 circleSize = typedArray.getInt(R.styleable.MyCheckView_m_blockColor, circleSize); typedArray.recycle(); } } /** * 初始化画笔 */ private void init() { //设置矩形背景色 recPaint.setColor(m_recColor); recPaint.setStyle(Paint.Style.FILL); recPaint.setAntiAlias(true); //设置进度条背景色 proPaint.setColor(m_proColor); proPaint.setStyle(Paint.Style.FILL); recPaint.setAntiAlias(true); //判断是否使用了背景图 if (blockDrawableId != -1) { //设置滑块背景色 blockPaint.setColor(m_blockColor); blockPaint.setStyle(Paint.Style.FILL_AND_STROKE); blockPaint.setAntiAlias(true); //给滑块添加阴影 blockPaint.setShadowLayer(35, 1, 1, m_blockShadowLayer); } else { blockPaint.setStyle(Paint.Style.FILL_AND_STROKE); blockPaint.setAntiAlias(true); } } public void blockReset() { mX = 0; reset(startX); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); parentWidth = getMyWSize(widthMeasureSpec); proHeight = getMyHSize(heightMeasureSpec); setMeasuredDimension((int) parentWidth, proHeight); } @SuppressLint("DrawAllocation") @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //绘制矩形 RectF rectF = new RectF(); rectF.left = 1; rectF.right = parentWidth - 1; rectF.top = 1; rectF.bottom = proHeight - 1; //绘制圆角矩形 canvas.drawRoundRect(rectF, circleSize, circleSize, recPaint); if (isMove || isDown) { //绘制进度条 RectF rectP = new RectF(); rectP.left = 1; rectP.right = blockWidth + blockSize + mX; rectP.top = 1; rectP.bottom = proHeight - 1; canvas.drawRoundRect(rectP, circleSize, circleSize, proPaint); } //绘制滑块 RectF rectB = new RectF(); rectB.left = blockSize + mX; rectB.right = blockWidth + mX; rectB.top = blockSize; rectB.bottom = proHeight - blockSize; mRight = (int) rectB.right; //判断是否使用了背景图 if (blockDrawableId != -1) { //绘制背景图 Bitmap bitmap = BitmapFactory.decodeResource(getResources(), blockDrawableId); Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); canvas.drawBitmap(bitmap, rect, rectB, blockPaint); } else { //绘制滑块 canvas.drawRoundRect(rectB, circleSize, circleSize, blockPaint); } } @SuppressLint("ClickableViewAccessibility") @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: dX = (int) event.getX(); int dY = (int) event.getY(); int top = getTop(); int bottom = getBottom(); //判断区域是否为滑块 if (dX > blockSize && dX < blockWidth && dY > blockSize && dY < (bottom - top)) { isBlockArea = true; } return true; case MotionEvent.ACTION_MOVE: if (isBlockArea) { mX = (int) event.getX() - dX; //设置范围 if ((blockWidth + blockSize + mX) < parentWidth && (blockSize + mX) >= blockSize) { //计算偏移量 invalidate(); startX = (int) event.getX() - blockWidth / 2; } else if ((blockSize + mX) >= blockSize) { //超出复位 mX = (int) parentWidth - blockWidth - blockSize; invalidate(); } isMove = true; } return true; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: isBlockArea = false; isFinish = mRight == parentWidth - blockSize; if (isFinish) { //监听回调 if (finishListener != null) { finishListener.finish(); } } if (!isFinish && isMove) { reset(startX); } break; } return super.onTouchEvent(event); } /** * 松手回弹动画效果 */ private void reset(int start) { ValueAnimator valueAnimator = ValueAnimator.ofInt(start, 0); valueAnimator.setDuration(500); valueAnimator.start(); valueAnimator.addUpdateListener(animation -> { mX = (int) animation.getAnimatedValue(); //刷新 invalidate(); }); valueAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { isMove = false; isFinish = false; startX = 0; } }); } /** * 获取测量大小 */ private int getMyWSize(int measureSpec) { int result; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) { result = specSize;//确切大小,所以将得到的尺寸给view } else if (specMode == MeasureSpec.AT_MOST) { result = Math.min(getScreenWidth() - 20, specSize); } else { result = getScreenWidth() - 20; } return result; } /** * 获取测量大小 */ private int getMyHSize(int measureSpec) { int result; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) { result = specSize;//确切大小,所以将得到的尺寸给view } else if (specMode == MeasureSpec.AT_MOST) { result = Math.min(DEFAULT_HEIGHT, specSize); } else { result = DEFAULT_HEIGHT - 20; } return result; } /** * 获取屏幕宽度 */ private int getScreenWidth() { WindowManager windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); DisplayMetrics displayMetrics = new DisplayMetrics(); windowManager.getDefaultDisplay().getMetrics(displayMetrics); return displayMetrics.widthPixels; } /** * 接口回调方法 */ public interface FinishListener { void finish(); } }
使用方法
<com.guanwei.globe.view.MyCheckView android:id="@+id/checkView" android:layout_width="200dp" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:layout_marginRight="20dp" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintTop_toTopOf="parent" app:m_blockBg="@mipmap/block" />