图片滑动验证Demo

首先说一下思路,首先绘制一个闭合路径来作为可移动的图片块的形状,在对应的背景图片位置绘制该路径并填充为灰色,接着在新建一个同样大小的图片,背景设为透明,以及一个对应新的Canvas,利用该路径切割Canvas,并绘制背景图片,此时该图片只有路径区域显示背景图片对应位置其他区域为透明,接着重写onDraw(),接着绘制这两张图片,并通过监听move事件移动背景透明的图片。效果如图:
图片滑动验证Demo_第1张图片

下面是源码:
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.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

/**
* Created by lc on 16-12-26.
*/

public class MySlideImage extends View {
private int backgroundId;//验证图片资源ID
private int drawX;//随机生成的x坐标
private int drawY;//随机生成的Y坐标
private boolean backgroundHasDraw = false;//是否时第一次绘制
private int width;//资源图片宽度
private int height;//资源图片高度
private int targetWidth;//图片块宽度
private int targetHeight;//图片块高度
private MoveFinishBack mMoveFinishBack = null;//验证完成后回调接口
private Path mPath;//图片块路径
Bitmap mBitmap;//验证图片
private float lastX = 0;//监听滑动事件上一次坐标
private int targetLength;//可移动图片块所在图片距离左边缘距离,范围为(-drawX,0)
private int moveLength = 12;//底部圆心距离左边缘距离

public MySlideImage(Context context) {
    super(context);
}

public MySlideImage(Context context, AttributeSet attrs) {
    super(context, attrs);
    TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MySlideImage);
    backgroundId = array.getResourceId(R.styleable.MySlideImage_imageSrc, 0);
    mBitmap = getBitmapById(backgroundId);
    width = mBitmap.getWidth();
    height = mBitmap.getHeight();
    Log.d("lc", "backgroundId = " + backgroundId + "height  = " + height + " width = " + width);
    array.recycle();
}

/*
    创建可移动图块,只有路径所围成的区域可见,其他为透明
 */
public Bitmap createMoveDrawable() {
    Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);//创建可移动图片
    Canvas canvas = new Canvas(bitmap);
    canvas.clipPath(getPath());//截取画布,目的只显示需要的图片区域
    Paint paint = new Paint();
    paint.setAntiAlias(true);
    if (mBitmap != null) {
        // canvas.drawBitmap(mBitmap, new Rect(drawX,drawY,drawX+targetWidth,drawY+targetHeight), new RectF(drawX,drawY,drawX+targetWidth,drawY+targetHeight), paint);
        canvas.drawBitmap(mBitmap, 0, 0, paint);
    } else {
        Log.d("lc", "createTargetDrawable :bitmap is null");
    }
    return bitmap;
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(width, height + 30);
}

/*
    创建底部背景
 */
public void createTargetDrawable(Canvas canvas) {

    Paint paint = new Paint();
    paint.setColor(Color.GRAY);
    paint.setAntiAlias(true);
    paint.setStyle(Paint.Style.FILL_AND_STROKE);

    if (mBitmap != null) {
        canvas.drawBitmap(mBitmap, 0, 0, paint);
    } else {
        Log.d("lc", "createTargetDrawable :bitmap is null");
    }
    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));
    paint.setColor(Color.GREEN);
    canvas.drawPath(getPath(), paint);
    backgroundHasDraw = true;
}

public void getRandomXY() {
    double random = Math.random();
    random = random > (5 * 1.00 / 6) ? (5 * 1.00 / 6) : (random > 1.00 / 6 ? random : 1.00 / 6);
    Log.d("lc", " random = " + random);
    drawX = (int) (width * random);
    drawY = (int) (height * random);
    Log.d("lc", " drawX = " + drawX + " drawY = " + drawY);
    targetHeight = height / 6;
    targetWidth = width / 6;
    Log.d("lc", " targetHeight = " + targetHeight + " targetWidth = " + targetWidth);
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    Paint paint = new Paint();
    paint.setAntiAlias(true);
    if (!backgroundHasDraw) {
        getRandomXY();
        targetLength = -drawX;
        moveLength = 12;
    }

    drawSeekBar(canvas, paint);
    createTargetDrawable(canvas);
    canvas.drawBitmap(createMoveDrawable(), targetLength, 0, paint);
    drawSeekButton(canvas, paint);
}

public Bitmap getBitmapById(int resourceId) {
    return BitmapFactory.decodeResource(getContext().getResources(), resourceId);
}

public Path getPath() {
    if (mPath == null) {
        mPath = new Path();
        mPath.moveTo(drawX, drawY);
        mPath.lineTo(drawX + 10, drawY);
        mPath.lineTo(drawX + 10, drawY + 15);
        mPath.lineTo(drawX + targetWidth, drawY + 15);
        mPath.lineTo(drawX + targetWidth, drawY + targetHeight);
        mPath.lineTo(drawX, drawY + targetHeight);
        mPath.lineTo(drawX, drawY);
    }
    return mPath;
}

/*
    绘制底部绿色bar
 */
public void drawSeekBar(Canvas canvas, Paint paint) {
    paint.setColor(Color.GREEN);
    paint.setStyle(Paint.Style.FILL_AND_STROKE);
    canvas.drawRoundRect(new RectF(5, height + 10, width - 5, height + 20), 0.5f, 0.5f, paint);
}

/*
    绘制底部红色圆
 */
public void drawSeekButton(Canvas canvas, Paint paint) {
    paint.setColor(Color.RED);
    paint.setStyle(Paint.Style.FILL_AND_STROKE);
    canvas.drawCircle(moveLength, height + 12, 12, paint);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    int action = event.getAction();
    switch (action) {
        case MotionEvent.ACTION_DOWN:
            lastX = event.getX();
            Log.d("lc", "action down");
            break;
        case MotionEvent.ACTION_MOVE:
            float currentX = event.getX();
            int length = (int) (currentX - lastX);
            Log.d("lc", "action move  length = " + length);
            moveLength = Math.max(12, Math.min(width - 12, moveLength + length));
            targetLength = Math.max(-drawX, Math.min(width - targetWidth, targetLength + length));
            invalidate();
            lastX = currentX;
            break;
        case MotionEvent.ACTION_UP:
            boolean finished = false;
            if (Math.abs(targetLength) < 6) {
                finished = true;
                Log.d("lc", " action up " + finished);
                targetLength = 0;
                postInvalidateDelayed(300);
            } else {
                Log.d("lc", " action up " + finished);
                moveLength = 12;
                targetLength = -drawX;
                postInvalidateDelayed(300);
            }
            if (mMoveFinishBack != null) {
                if (finished) {
                    mMoveFinishBack.MoveFinish(true);
                } else {
                    mMoveFinishBack.MoveFinish(false);
                }
            }
            break;
    }
    return true;
}

public void setCallBack(MoveFinishBack moveFinishBack) {
    this.mMoveFinishBack = moveFinishBack;
}
/*
    自定义路径以及设置图片块宽高
 */
public void setPathAndRect(Path path,int height,int width) {
    mPath = path;
    targetWidth = width;
    targetHeight = height;
}
/*
    刷新图片
 */
public void refreshImage(){
    backgroundHasDraw = false;
    mPath = null;
    invalidate();
}
interface MoveFinishBack {
    void MoveFinish(boolean correct);
}
}
说明:
    1.上面代码中未对图片大小进行判断,所以测试大的图片时需要先对其进行尺寸压缩。
    2.图片块的位置采用随机值
    3.上例中图片的资源Id是通过自定义属性获得
    4.部分手机(图片块只显示为一个矩形)需要在清单文件中关闭硬件加速才能看到完整效果

你可能感兴趣的:(android)