Android|Android 图片滑块解锁 and 组件拖动解锁的简单实现

效果图
这里写图片描述 实现方式
滑块解锁

自定义ImageView在原图的基础上再画两个 滑块 一个目标位 一个移动位。 通过一个对外方法可以更改进度 然后 调用postInvalidate() 实现该效果 大致原理就是这样下面上代码

package project.com.verifyproject; import android.app.Activity; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Point; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.support.annotation.Nullable; import android.support.v7.widget.AppCompatImageView; import android.util.AttributeSet; /** * Created by 于德海 on 2018/6/7. * * email : yudehai0204@163.com * * @describe */public class SlideValidateView extends AppCompatImageView { //Image背景图 private Bitmap mBitmap; //滑块对应的画笔 private Paint mPaint; //滑块的宽度占比高度自动对比 private float mSlideWidthScale; //滑块的源图 private Bitmap mResourseBitmap; //滑块图片 private Bitmap mSlideBitmap; //滑块的宽度 private int mSlide_width = 0; //滑块高度 private int mSlide_height=0; //是否重新绘制图片 private boolean isReset = true; //偏移值最小2 最大100 private int deviation; //阴影颜色 private int shade_color= Color.GRAY; //阴影图片 private Bitmap mShadeBitmap; //图片的最大宽度,最大高度 private int max_width,max_height; //滑块移动距离 private int mSlideMoveDistance =0; //随机生成的目标阴影xy初始坐标 private int mShadeRandom_x,mShadeRandom_y; private OnSlideListener mListener; public interface OnSlideListener{ void success(); void error(); }public SlideValidateView(Context context) { this(context,null); }public SlideValidateView(Context context, @Nullable AttributeSet attrs) { this(context, attrs,0); }public SlideValidateView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray ta = context.obtainStyledAttributes(R.styleable.SlideValidateView); max_width = ta.getDimensionPixelOffset(R.styleable.SlideValidateView_max_width,0); max_height = ta.getDimensionPixelOffset(R.styleable.SlideValidateView_max_height,0); Drawable drawable = ta.getDrawable(R.styleable.SlideValidateView_mSlideBitmap); mResourseBitmap = initResourseBitmap(drawable); shade_color = ta.getColor(R.styleable.SlideValidateView_shade_color, Color.GRAY); mSlideWidthScale = ta.getFloat(R.styleable.SlideValidateView_slideWidthScale,0.2f); deviation = ta.getInteger(R.styleable.SlideValidateView_deviation,10); ta.recycle(); if(max_height==0||max_width==0){ Point point = new Point(); //x为宽,y为高 ((Activity)context).getWindowManager().getDefaultDisplay().getSize(point); max_width = point.x; max_height =point.y/2; } if(deviation<2){ deviation=2; }else if(deviation>100){ deviation=100; } mPaint = new Paint(); mPaint.setAntiAlias(true); //抗锯齿 }public void setOnSlideListener(OnSlideListener listener){ this.mListener = listener; }public void reset(){ isReset = true; mSlideMoveDistance=0; postInvalidate(); }@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if(isReset){ mBitmap = getViewBitmap(); if(mSlide_width==0&&mBitmap!=null&&mBitmap.getWidth()!=0){ initSlideWH(); } initSlideRandomXY(); mShadeBitmap = Bitmap.createBitmap(mSlide_width,mSlide_height, Bitmap.Config.ARGB_8888); mShadeBitmap.eraseColor(shade_color); mSlideBitmap = Bitmap.createBitmap(mBitmap,mShadeRandom_x,mShadeRandom_y,mSlide_width,mSlide_height); } isReset = false; canvas.drawBitmap(drawImage(mShadeBitmap),mShadeRandom_x,mShadeRandom_y,mPaint); canvas.drawBitmap(drawImage(mSlideBitmap),mSlideMoveDistance,mShadeRandom_y,mPaint); }/*** * * @param progress 1-100 */ public void setSlide_X(int progress){ mSlideMoveDistance = (mBitmap.getWidth()-mSlide_width)/100*progress; if (mSlideMoveDistance > mBitmap.getWidth() - mSlide_width) { mSlideMoveDistance = mBitmap.getWidth() - mSlide_width; } postInvalidate(); }/*** * 检测 */ public void checkSlidePoint(){ if( mListener !=null ){ if (Math.abs(mSlideMoveDistance - mShadeRandom_x) <= deviation) { mListener.success(); } else { mListener.error(); } }}/**** * 重构图片 * @return */ private Bitmap drawImage(Bitmap bitmap) { // 绘制图片 Bitmap showB; if (null != mResourseBitmap) { showB = handleBitmap(mResourseBitmap, mSlide_width, mSlide_height); } else { showB = handleBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.star_shade), mSlide_width, mSlide_height); } Bitmap resultBmp = Bitmap.createBitmap(mSlide_width, mSlide_height, Bitmap.Config.ARGB_8888); Paint paint = new Paint(); paint.setAntiAlias(true); Canvas canvas = new Canvas(resultBmp); canvas.drawBitmap(showB, new Rect(0, 0, mSlide_width, mSlide_height), new Rect(0, 0, mSlide_width, mSlide_height), paint); // 选择交集去上层图片 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY)); canvas.drawBitmap(bitmap, new Rect(0, 0, mSlide_width, mSlide_height), new Rect(0, 0, mSlide_width, mSlide_height), paint); return resultBmp; } /** * 缩放图片 * * @param bp * @param x * @param y * @return */ public static Bitmap handleBitmap(Bitmap bp, float x, float y) { int w = bp.getWidth(); int h = bp.getHeight(); float sx = (float) x / w; float sy = (float) y / h; Matrix matrix = new Matrix(); matrix.postScale(sx, sy); Bitmap resizeBmp = Bitmap.createBitmap(bp, 0, 0, w, h, matrix, true); return resizeBmp; } /*** * 初始化滑块的宽高 */ private void initSlideWH() { mSlide_width = (int) (mBitmap.getWidth()*mSlideWidthScale); float scale = ((float) mSlide_width)/mResourseBitmap.getWidth(); mSlide_height = (int) (mResourseBitmap.getHeight()*scale); }/*** * 初始化滑块起始位置 */ private void initSlideRandomXY() { mShadeRandom_x = (int) (mBitmap.getWidth()/2+(Math.random()*(mBitmap.getWidth()/2))-mSlide_width); mShadeRandom_y = (int) (Math.random() * (mBitmap.getHeight()-mSlide_height)); if(mShadeRandom_x+mSlide_width>mBitmap.getWidth()||mShadeRandom_y+mSlide_height>mBitmap.getHeight()){ initSlideRandomXY(); return; }}private Bitmap initResourseBitmap(Drawable drawable){ Bitmap bitmap; if(drawable ==null){ bitmap = BitmapFactory.decodeResource(getContext().getResources(),R.drawable.star_shade); }else { bitmap = drawable2bitmap(drawable); } return bitmap; } /*** * drawable 转 bitmap * @param drawable * @return */ private Bitmap drawable2bitmap(Drawable drawable){ if(drawable==null){ return null; }else if(drawable instanceof BitmapDrawable){ BitmapDrawable bd = (BitmapDrawable) drawable; return bd.getBitmap(); } int w = drawable.getIntrinsicWidth(); int h = drawable.getIntrinsicHeight(); Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); drawable.setBounds(0, 0, w, h); drawable.draw(canvas); return bitmap; }public Bitmap getViewBitmap() { Bitmap b = drawable2bitmap(getDrawable()); float scaleX = 1.0f; float scaleY = 1.0f; // 如果图片的宽或者高与view的宽高不匹配,计算出需要缩放的比例;缩放后的图片的宽高,一定要大于我们view的宽高;所以我们这里取大值; scaleX = getWidth() * 1.0f / b.getWidth(); scaleY = getHeight() * 1.0f / b.getHeight(); Matrix matrix = new Matrix(); matrix.setScale(scaleX, scaleY); Bitmap bd = Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), matrix, true); return bd; } }

该原理参考于 该文章:https://blog.csdn.net/u013904672/article/details/51279520
对代码进行了部分优化 并设置了 seekbar只可拖动 不可点击。
拖动验证 这个就是一个捕获一个View的ontouch事件来自己处理。然后不断更改 margin值 没什么难点。
项目中的按钮大小是写死的 不然需延迟获取view.getwidth() 在onCreate中 获取为0 因为在这里该View并没有绘制完成
核心代码也就ontouch里的方法
img_start.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent event) { switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: start_x = img_start.getLeft(); start_y = img_start.getTop(); end_y = img_end.getTop(); lastX = event.getRawX(); lastY = event.getRawY(); break; case MotionEvent.ACTION_MOVE: //不要直接用getX和getY,这两个获取的数据已经是经过处理的,容易出现图片抖动的情况 move_x = (int) (event.getRawX()-lastX); move_y = (int) (event.getRawY()-lastY); int distancex = start_x + move_x; int distancey = start_y + move_y; layoutParams.setMargins(distancex, distancey, 0, 0); img_start.setLayoutParams(layoutParams); if(distancex>end_x&&distancex<(end_x+img_end.getWidth())&&distancey>end_y&&distancey<(end_y+img_end.getHeight())){ Log.e("check","true"); if(!isCheck){ isCheck = true; img_end.setImageResource(R.drawable.img_aty_verify_end_check); } }else if(isCheck){ Log.e("check","false"); isCheck = false; img_end.setImageResource(R.drawable.img_aty_verify_end); } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: if(isCheck){ img_start.setEnabled(false); Toast.makeText(MainActivity.this,"success",Toast.LENGTH_SHORT).show(); }else { img_end.setImageResource(R.drawable.img_aty_verify_end); layoutParams.setMargins(start_x, start_y, 0, 0); img_start.setLayoutParams(layoutParams); }break; }return true; } });

是不是很简单,只需要不断的更改params的margin就可以实现。
项目地址
该项目源码已上传至github
下载地址:点击前往Github
【Android|Android 图片滑块解锁 and 组件拖动解锁的简单实现】Over!!!

    推荐阅读