学习内容来自“慕课网”
很多电商APP中都有刮刮卡活动,刮开涂层,获取刮刮卡内部信息
原理图:
刮刮卡效果:通过画笔画笔来实现,黄色涂层,蓝色涂层,刮动则将两涂层共有的部分去掉, 就是DstOut对应的 效果
MainActivity.java
1 package com.example.gauguaka; 2 3 import android.os.Bundle; 4 import android.app.Activity; 5 import android.view.Menu; 6 7 public class MainActivity extends Activity { 8 9 @Override 10 protected void onCreate(Bundle savedInstanceState) { 11 super.onCreate(savedInstanceState); 12 setContentView(R.layout.activity_main); 13 } 14 15 16 }
新建一个包guaguaka.java 在包中新建类Guaguaka.java
![](https://yqfile.alicdn.com/img_1c53668bcee393edac0d7b3b3daff1ae.gif?x-oss-process=image/resize,w_1400/format,webp)
![](https://yqfile.alicdn.com/img_405b18b4b6584ae338e0f6ecaf736533.gif?x-oss-process=image/resize,w_1400/format,webp)
1 package guaguaka.view; 2 3 import com.example.gauguaka.R; 4 5 import android.content.Context; 6 import android.graphics.Bitmap; 7 import android.graphics.Bitmap.Config; 8 import android.graphics.BitmapFactory; 9 import android.graphics.Canvas; 10 import android.graphics.Color; 11 import android.graphics.Paint; 12 import android.graphics.Paint.Style; 13 import android.graphics.Path; 14 import android.graphics.PorterDuff.Mode; 15 import android.graphics.PorterDuffXfermode; 16 import android.graphics.Rect; 17 import android.graphics.RectF; 18 import android.util.AttributeSet; 19 import android.util.Log; 20 import android.view.MotionEvent; 21 import android.view.View; 22 23 public class Guaguaka extends View{ 24 //画笔 25 private Paint moutterpaint; 26 //记录绘制路径 27 private Path mpath; 28 //画布 29 private Canvas mcanvas; 30 //图片 31 private Bitmap mbitmap; 32 //绘制坐标值 33 private int mlastx; 34 private int mlasty; 35 /*----------------------*/ 36 private Bitmap bitmap; 37 private Bitmap moutterbitmap; 38 // 判断遮盖层区域是否消除达到阈值 39 private volatile boolean mComplete = false; 40 41 42 43 public Guaguaka(Context context) { 44 // TODO Auto-generated constructor stub 45 this(context,null); 46 } 47 public Guaguaka(Context context, AttributeSet attrs) { 48 this(context, attrs,0); 49 // TODO Auto-generated constructor stub 50 } 51 public Guaguaka(Context context, AttributeSet attrs,int defStyle) { 52 super(context, attrs ,defStyle); 53 // TODO Auto-generated constructor stub 54 init(); 55 } 56 //获得控件的宽度和高度 57 @Override 58 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 59 // TODO Auto-generated method stub 60 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 61 62 int width = getMeasuredWidth(); 63 int height = getMeasuredHeight(); 64 // 初始化我们的bitmap 65 mbitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888); 66 mcanvas = new Canvas(mbitmap); 67 // 设置绘制path画笔的一些属性 68 setupOutPaint(); 69 //setUpBackPaint(); 70 //mcanvas.drawColor(Color.parseColor("#c0c0c0")); 71 //设置刮刮卡框架为圆角 72 mcanvas.drawRoundRect(new RectF(0, 0, width, height), 30, 30,moutterpaint); 73 //显示刮刮卡未刮开是的图案 74 mcanvas.drawBitmap(moutterbitmap, null, new Rect(0, 0, width, height),null); 75 76 77 } 78 //初始化操作 79 private void init() { 80 // TODO Auto-generated method stub 81 moutterpaint = new Paint(); 82 mpath = new Path(); 83 //刮开后的图片(chaji_1是一个茶壶的图片) 84 bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.chaji_1); 85 //刮刮卡未刮时候的图案(fg_guaguaka 是一个刮刮卡字样的图片) 86 moutterbitmap = BitmapFactory.decodeResource(getResources(),R.drawable.fg_guaguaka); 87 } 88 /** 89 * 设置绘制path画笔的一些属性 90 */ 91 private void setupOutPaint() 92 { 93 //画笔颜色 --红色 94 moutterpaint.setColor(Color.parseColor("#c0c0c0")); 95 //锯齿 96 moutterpaint.setAntiAlias(true); 97 moutterpaint.setDither(true); 98 //线条圆角 99 moutterpaint.setStrokeJoin(Paint.Join.ROUND); 100 moutterpaint.setStrokeCap(Paint.Cap.ROUND); 101 moutterpaint.setStyle(Style.FILL); 102 //画笔宽度 103 moutterpaint.setStrokeWidth(20); 104 } 105 /** 106 * 设置我们绘制获奖信息的画笔属性 107 */ 108 109 //绘制事件 110 @Override 111 public boolean onTouchEvent(MotionEvent event) 112 { 113 int action = event.getAction(); 114 115 int x = (int) event.getX(); 116 int y = (int) event.getY(); 117 118 switch (action) 119 { 120 case MotionEvent.ACTION_DOWN://按下 121 122 mlastx = x; 123 mlasty = y; 124 mpath.moveTo(mlastx, mlasty); 125 break; 126 case MotionEvent.ACTION_MOVE://移动 127 128 int dx = Math.abs(x - mlastx); 129 int dy = Math.abs(y - mlasty); 130 131 if (dx > 3 || dy > 3) 132 { 133 mpath.lineTo(x, y); 134 } 135 //更新坐标 136 mlastx = x; 137 mlasty = y; 138 139 break; 140 case MotionEvent.ACTION_UP://抬起 141 new Thread(mRunnable).start();// 统计擦除区域任务 142 break; 143 } 144 145 invalidate(); 146 return true; 147 148 } 149 @Override 150 protected void onDraw(Canvas canvas) 151 { 152 canvas.drawBitmap(bitmap, 0 , 0, null); 153 //注意任务结束,会把一个mComplete设置为true;当为true时,直接展现刮奖区 154 if (!mComplete) 155 { 156 drawPath(); 157 canvas.drawBitmap(mbitmap, 0, 0, null); 158 } 159 } 160 161 private void drawPath() 162 { 163 moutterpaint.setStyle(Style.STROKE); 164 moutterpaint.setXfermode(new PorterDuffXfermode(Mode.DST_OUT)); 165 mcanvas.drawPath(mpath, moutterpaint); 166 } 167 /** 168 * 统计擦除区域任务 169 */ 170 private Runnable mRunnable = new Runnable() 171 { 172 private int[] mPixels; 173 174 @Override 175 public void run() 176 { 177 178 int w = getWidth(); 179 int h = getHeight(); 180 181 float wipeArea = 0; 182 float totalArea = w * h; 183 184 Bitmap bitmap = mbitmap; 185 186 mPixels = new int[w * h]; 187 188 /** 189 * 拿到所有的像素信息 190 */ 191 bitmap.getPixels(mPixels, 0, w, 0, 0, w, h); 192 193 /** 194 * 遍历统计擦除的区域 195 */ 196 for (int i = 0; i < w; i++) 197 { 198 for (int j = 0; j < h; j++) 199 { 200 int index = i + j * w; 201 if (mPixels[index] == 0) 202 { 203 wipeArea++; 204 } 205 } 206 } 207 208 /** 209 * 根据所占百分比,进行一些操作 210 */ 211 if (wipeArea > 0 && totalArea > 0) 212 { 213 int percent = (int) (wipeArea * 100 / totalArea); 214 Log.e("TAG", percent + ""); 215 216 if (percent > 70) 217 { 218 mComplete = true; 219 postInvalidate(); 220 } 221 } 222 } 223 224 }; 225 }
将布局文件修改:
![](https://yqfile.alicdn.com/img_1c53668bcee393edac0d7b3b3daff1ae.gif?x-oss-process=image/resize,w_1400/format,webp)
![](https://yqfile.alicdn.com/img_405b18b4b6584ae338e0f6ecaf736533.gif?x-oss-process=image/resize,w_1400/format,webp)
1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 > 6 7 <guaguaka.view.Guaguaka 8 android:id="@+id/id_guaguaka" 9 android:layout_width="300dp" 10 android:layout_height="100dp" 11 android:layout_centerInParent="true" 12 /> 13 </RelativeLayout>
效果图:
接下来就行效果的优化。
当刮开涂层达到总面积的多少的时候,将全部图案显示出来
首先定义一个布尔值
1 // 判断遮盖层区域是否消除达到阈值 2 private volatile boolean mComplete = false;
添加计算刮开区域面积的线程
1 private Runnable mRunnable = new Runnable() 2 { 3 private int[] mPixels; 4 5 @Override 6 public void run() 7 { 8 9 int w = getWidth(); 10 int h = getHeight(); 11 12 float wipeArea = 0; 13 float totalArea = w * h; 14 15 Bitmap bitmap = mbitmap; 16 17 mPixels = new int[w * h]; 18 19 /** 20 * 拿到所有的像素信息 21 */ 22 bitmap.getPixels(mPixels, 0, w, 0, 0, w, h); 23 24 /** 25 * 遍历统计擦除的区域 26 */ 27 for (int i = 0; i < w; i++) 28 { 29 for (int j = 0; j < h; j++) 30 { 31 int index = i + j * w; 32 if (mPixels[index] == 0) 33 { 34 wipeArea++; 35 } 36 } 37 } 38 39 /** 40 * 根据所占百分比,进行一些操作 41 */ 42 if (wipeArea > 0 && totalArea > 0) 43 { 44 int percent = (int) (wipeArea * 100 / totalArea); 45 Log.e("TAG", percent + ""); 46 47 if (percent > 70) //如果刮开面积达到70% 则将mComplete布尔值设为true 将全部图案显示出来 48 { 49 mComplete = true; 50 postInvalidate(); 51 } 52 } 53 } 54 55 };
在ACTION_UP,即松开触屏的时候调用
1 case MotionEvent.ACTION_UP://抬起 2 new Thread(mRunnable).start();// 统计擦除区域任务 3 break;
任务结束,会把一个mComplete设置为true;当为true时,直接展现刮奖区
1 @Override 2 protected void onDraw(Canvas canvas) 3 { 4 drawBackText(canvas); 5 6 if (!isComplete) 7 { 8 drawPath(); 9 canvas.drawBitmap(mBitmap, 0, 0, null); 10 } 11 12 }
效果图: