android 自定义view实现刮刮卡效果

感觉好久没写博客了,最近也是很烦,现在可以要好好学习,把自己的心态调整好,

今天实现一个刮刮卡效果,主要用到的知识点还是canvas和paint,Path这几个类,不熟悉的可以先把这三个中用到什么方法,熟悉下,也就是把api熟悉了,知道它是干嘛用的,


其实刮刮卡和橡皮擦的功能很像,我之前博客也写过关于android涂鸦方面的,如果那个看懂了,刮刮卡就很容易看的懂,我还是先写一个简单的涂鸦功能,

package com.simple;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
 * Created by admin on 2017/5/3.
 */
public class ScratchCardView extends View {
    private int width;
    private int height;
    private String text = "猜猜她是谁";
    private Paint paint;
    private Path path;
    private float downX,downY;
    private float tempX,tempY;
    public ScratchCardView(Context context) {
        this(context,null);
    }

    public ScratchCardView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public ScratchCardView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaint();
    }

    private void initPaint() {
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(2);
        paint.setTextSize(24);
        path = new Path();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        width = w;
        height = h;
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawPath(path,paint);
    }
    private void drawText(Canvas canvas) {
        Rect rect = new Rect();
        paint.getTextBounds(text, 0, text.length(), rect);
        int textWidth = rect.width();
        int textHeight = rect.height();
        float x = width/2-textWidth/2;
        float y = height/2+textHeight/2;
        paint.setColor(Color.parseColor("#9370DB"));
        canvas.drawText(text,x,y,paint);
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                downX = event.getX();
                downY = event.getY();
                path.moveTo(downX,downY);
                invalidate();
                tempX = downX;
                tempY = downY;
                break;
            case MotionEvent.ACTION_MOVE:
                float moveX = event.getX();
                float moveY = event.getY();
                path.quadTo(tempX,tempY,moveX,moveY);
                invalidate();
                tempX = moveX;
                tempY = moveY;
                break;
        }
        return true;
    }
}
布局文件:


  

效果图:

android 自定义view实现刮刮卡效果_第1张图片
这个就是刮刮卡效果中的擦除功能,只是你给paint设置的颜色不同而已,

比如如何创建一个可绘制的空的画布canvas呢?

上面我们是在系统的canvas上去绘制一些文字的,但是要在自己创建的画布上绘制东西,怎么创建?其实很简单

 myBitmap = Bitmap.createBitmap(700,400, Bitmap.Config.ARGB_8888);
 myCanvas = new Canvas(myBitmap);

这就二行代码就可以了,然后通过系统的canvas,把这个mBitmap绘制上去就ok,比如怎么把几张图合并成一张图,就这么这么干了,下面四张图是我叫美工切的:

android 自定义view实现刮刮卡效果_第2张图片

然后通过代码把这四张图合并成一张图入下:

package com.simple;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
 * Created by admin on 2017/5/3.
 */
public class ScratchCardView extends View {
    private static final String TAG ="ScratchCardView" ;
    private int width;
    private int height;
    private String text = "猜猜她是谁";
    private Paint paint;
    private Path path;
    private float downX,downY;
    private float tempX,tempY;
    private Canvas myCanvas;
    private Bitmap myBitmap;
    private Bitmap bitmapa;
    private Bitmap bitmapb;
    private Bitmap bitmapc;
    private Bitmap bitmapd;
    public ScratchCardView(Context context) {
        this(context,null);
    }

    public ScratchCardView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public ScratchCardView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initBitmap();
        initPaint();
    }

    private void initBitmap() {
        bitmapa = BitmapFactory.decodeResource(getResources(),R.drawable.a);
        bitmapb = BitmapFactory.decodeResource(getResources(),R.drawable.b);
        bitmapc = BitmapFactory.decodeResource(getResources(),R.drawable.c);
        bitmapd = BitmapFactory.decodeResource(getResources(),R.drawable.d);
    }

    private void initPaint() {
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(2);
        paint.setTextSize(24);
        paint.setDither(true);
        path = new Path();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        width = w;
        height = h;
        myBitmap = Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888);
        myCanvas = new Canvas(myBitmap);
    }
    @Override
    protected void onDraw(Canvas canvas) {
        drawBitmap();
        drawPath();
        canvas.drawBitmap(myBitmap,0,0,null);
        super.onDraw(canvas);
    }
    private void drawPath() {
        myCanvas.drawPath(path,paint);
    }
    private void drawBitmap() {
        myCanvas.drawBitmap(bitmapa,0,0,null);
        myCanvas.drawBitmap(bitmapb,bitmapa.getWidth(),0,null);
        myCanvas.drawBitmap(bitmapc,0,bitmapa.getHeight(),null);
        myCanvas.drawBitmap(bitmapd,bitmapc.getWidth(),bitmapb.getHeight(),null);
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                downX = event.getX();
                downY = event.getY();
                path.moveTo(downX,downY);
                invalidate();
                tempX = downX;
                tempY = downY;
                break;
            case MotionEvent.ACTION_MOVE:
                float moveX = event.getX();
                float moveY = event.getY();
                path.quadTo(tempX,tempY,moveX,moveY);
                invalidate();
                tempX = moveX;
                tempY = moveY;
                break;
        }
        return true;
    }
}
布局文件:


  
我们看到布局文件中没有什么背景图片了,说明这是合并成的:

android 自定义view实现刮刮卡效果_第3张图片
合并的原理其实很简单:

android 自定义view实现刮刮卡效果_第4张图片

如果做过tv端的,对倒影会有点影响,其实就是根据这个原理做的,这也是拼图小游戏中的一个小功能。还有是不是类似明星给你在衣服上签名一样.

现在我们更进一步就是怎么在一张被盖住的图片上进行擦除,比如底层是一张图片,这图片上层有一层背景色,然后把上层的背景色擦除就可以看到下层的美女了,激动吧!

package com.simple;
import android.content.Context;
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.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
 * Created by admin on 2017/5/3.
 */
public class ScratchCardView extends View {
    private static final String TAG ="ScratchCardView" ;
    private int width;
    private int height;
    private String text = "猜猜她是谁";
    private Paint paint;
    private Path path;
    private float downX,downY;
    private float tempX,tempY;
    private Canvas myCanvas;
    private Bitmap myBitmap;
    private Bitmap bitmapa;
    private Bitmap bitmapb;
    private Bitmap bitmapc;
    private Bitmap bitmapd;
    private Bitmap bitmap;
    public ScratchCardView(Context context) {
        this(context,null);
    }

    public ScratchCardView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public ScratchCardView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initBitmap();
        initPaint();
    }
    private void initBitmap() {
        bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.bg);
        bitmapa = BitmapFactory.decodeResource(getResources(),R.drawable.a);
        bitmapb = BitmapFactory.decodeResource(getResources(),R.drawable.b);
        bitmapc = BitmapFactory.decodeResource(getResources(),R.drawable.c);
        bitmapd = BitmapFactory.decodeResource(getResources(),R.drawable.d);
    }
    private void initPaint() {
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(20);
        paint.setTextSize(24);
        paint.setDither(true);
        path = new Path();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        width = w;
        height = h;
        myBitmap = Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888);
        myCanvas = new Canvas(myBitmap);
        myCanvas.drawColor(Color.GREEN);
    }
    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawBitmap(bitmap,0,0,null);//底层图
        drawPath();//这个path是绘制在myvanvas画布上的
        canvas.drawBitmap(myBitmap,0,0,null);
        super.onDraw(canvas);
    }
    private void drawPath() {
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
        myCanvas.drawPath(path,paint);
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                downX = event.getX();
                downY = event.getY();
                path.moveTo(downX,downY);
                invalidate();
                tempX = downX;
                tempY = downY;
                break;
            case MotionEvent.ACTION_MOVE:
                float moveX = event.getX();
                float moveY = event.getY();
                path.quadTo(tempX,tempY,moveX,moveY);
                invalidate();
                tempX = moveX;
                tempY = moveY;
                break;
        }
        return true;
    }
}
效果图:

android 自定义view实现刮刮卡效果_第5张图片

这看起来是不是离呱呱卡效果近了,其实完全可以做一个最简单的刮刮卡,就是把文字和低层图片放在一起,

我现在的底图是:

android 自定义view实现刮刮卡效果_第6张图片

上面的代码一点都没动,然后效果是这样的:

android 自定义view实现刮刮卡效果_第7张图片

这样做很low,而且实际效果也不好,但却是一种实现方案.能实现这个效果最关键的一点是paint中的一个方法:

paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));

这是设置了paint的图形混合模式,这个PorterDuff.Mode的类中有很多个变量,大概十几种吧,具体每个什么意思,最好是自己写demo去体会,光记住是没用的.

DST_OUT:只在源图像和目标图像不相交的地方绘制目标图像,在相交的地方根据源图像的alpha进行过滤,源图像完全不透明则完全过滤,完全透明则不过滤

我们这个paint画笔是作用于Path上的,path是我们要绘制的目标图像,下面的底图是源图像,

在这里我画个图更好理解了:

android 自定义view实现刮刮卡效果_第8张图片

这样刮刮卡我们只要把图片去掉,换成绘制的文字就ok了,

整个代码如下:

package com.simple;
import android.content.Context;
import android.graphics.Bitmap;
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.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
 * Created by admin on 2017/5/3.
 */
public class ScratchCardView extends View {
    private static final String TAG ="ScratchCardView" ;
    private int width;
    private int height;
    private String text = "苍老师晚上约你";
    private Paint textPaint;
    private Paint paint;
    private Path path;
    private float downX,downY;
    private float tempX,tempY;
    private Canvas myCanvas;
    private Bitmap myBitmap;
    public ScratchCardView(Context context) {
        this(context,null);
    }

    public ScratchCardView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public ScratchCardView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaint();
    }
    private void initPaint() {
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(6);
        paint.setTextSize(24);
        paint.setDither(true);
        textPaint = new Paint();
        textPaint.setAntiAlias(true);
        textPaint.setStyle(Paint.Style.FILL);
        textPaint.setStrokeWidth(20);
        textPaint.setTextSize(24);
        textPaint.setDither(true);
        textPaint.setColor(Color.BLACK);
        path = new Path();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        width = w;
        height = h;
        myBitmap = Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888);
        myCanvas = new Canvas(myBitmap);
        myCanvas.drawColor(Color.GREEN);
    }
    @Override
    protected void onDraw(Canvas canvas) {
//        canvas.drawBitmap(bitmap,0,0,null);//底层图
        drawText(canvas);
        drawPath();//这个path是绘制在myvanvas画布上的
        canvas.drawBitmap(myBitmap,0,0,null);
        super.onDraw(canvas);
    }

    private void drawText(Canvas canvas) {
        Rect rect = new Rect();
        textPaint.getTextBounds(text, 0, text.length(), rect);
        int textWidth = rect.width();
        int textHeight = rect.height();
        float x = width/2-textWidth/2;
        float y = height/2+textHeight/2;
        canvas.drawText(text,x,y,textPaint);
    }
    private void drawPath() {
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
        myCanvas.drawPath(path,paint);
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                downX = event.getX();
                downY = event.getY();
                path.moveTo(downX,downY);
                invalidate();
                tempX = downX;
                tempY = downY;
                break;
            case MotionEvent.ACTION_MOVE:
                float moveX = event.getX();
                float moveY = event.getY();
                path.quadTo(tempX,tempY,moveX,moveY);
                invalidate();
                tempX = moveX;
                tempY = moveY;
                break;
        }
        return true;
    }
}
效果:

android 自定义view实现刮刮卡效果_第9张图片

github:https://github.com/zhouguizhi/scratchcard

你可能感兴趣的:(自定义控件)