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

来源:互联网 发布:编译linux内核 编辑:程序博客网 时间:2024/06/06 04:07

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

今天实现一个刮刮卡效果,主要用到的知识点还是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;    }}
布局文件:
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/activity_main"    android:layout_width="match_parent"    android:layout_height="match_parent"    >  <com.simple.ScratchCardView      android:layout_width="540dp"      android:layout_height="300dp"      android:layout_centerInParent="true"      android:background="@drawable/bg"      ></com.simple.ScratchCardView></RelativeLayout>
效果图:


这个就是刮刮卡效果中的擦除功能,只是你给paint设置的颜色不同而已,

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

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

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

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


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

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;    }}
布局文件:
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/activity_main"    android:layout_width="match_parent"    android:layout_height="match_parent"    >  <com.simple.ScratchCardView      android:layout_width="450dp"      android:layout_height="360dp"      android:layout_centerInParent="true"      ></com.simple.ScratchCardView></RelativeLayout>
我们看到布局文件中没有什么背景图片了,说明这是合并成的:


合并的原理其实很简单:


如果做过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;    }}
效果图:


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

我现在的底图是:


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


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

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

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

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

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

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


这样刮刮卡我们只要把图片去掉,换成绘制的文字就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;    }}
效果:


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

0 0
原创粉丝点击