效果图
实现方式
滑块解锁
自定义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
Over!!!
网友评论