Android刮刮卡实现原理与代码讲解
实现刮刮卡我们可以get到哪些技能?
* 圆形圆角图片的实现原理
* 双缓冲技术绘图
* bitmap获取像素值数据
* 获取绘制文本的长宽
* 自定义view的掌握
* 获取屏幕密度
* typevalue.applydemension
* canvas的一些绘制方法
* paint的一些常用的属性
* path的一些方法
刮刮卡的实现原理图
这里用到了13中模式中的dstout这种模式。
对于这幅图而言,首先绘制dst,设置xfermode,再绘制src。
刮刮卡的实现原理步骤
1.绘制显示中奖的文本
2.绘制图层区/前景图片
3.设置xfermode
4.绘制path
5.清除所有图层区方案
6.自定义属性
代码讲解
第一部分:绘制中奖信息
public class guaguale extends view{ /**中奖文本变量*/ private string mtext;//显示文本 private paint mtextpaint;//绘制文本的画笔 private rect mtextbounds;//文本的边界 public guaguale(context context) { this(context, null); } public guaguale(context context, attributeset attrs) { super(context, attrs); initview(); } /**初始化一些实例变量*/ private void initview() { settextmessage();//实例化文本的一些变量 } private void settextmessage() { mtext = "谢谢惠顾"; mtextbounds = new rect(); mtextpaint = new paint(); mtextpaint.setantialias(true); mtextpaint.setcolor(color.dkgray); mtextpaint.settextsize(22); mtextpaint.gettextbounds(mtext,0,mtext.length(),mtextbounds);//获取到绘制文本的长宽 } @override protected void onmeasure(int widthmeasurespec, int heightmeasurespec) { super.onmeasure(widthmeasurespec, heightmeasurespec); } @override protected void ondraw(canvas canvas) { //绘制文本 canvas.drawtext(mtext,getwidth()/2 - mtextbounds.width()/2,getheight()/2 + mtextbounds.height()/2,mtextpaint); } }
如下图:
第二部分:绘制图层区/前景图片
/**画板变量*/ private canvas mcanvas;//缓冲画板 private bitmap mlayerbimtap;//图层图片 private bitmap mbitmap;//画板上的画纸 private paint mpaint;//画笔 @override protected void onmeasure(int widthmeasurespec, int heightmeasurespec) { super.onmeasure(widthmeasurespec, heightmeasurespec); setcanvasmessage(getmeasuredwidth(),getmeasuredheight());//实例化缓存画板的一些变量 } /*** * 双缓冲技术 * 1.创建一个与屏幕绘制区域一致的canvas对象 * 2.先将图形绘制到内存中 * 3.再一次性将对象上的图形拷贝到屏幕上 * @param width * @param height */ private void setcanvasmessage(int width,int height) { mpaint = new paint(); mpaint.setantialias(true); mpaint.setstyle(paint.style.fill); mpaint.setstrokewidth(3); mpaint.setcolor(color.parsecolor("#c0c0c0")); mbitmap = bitmap.createbitmap(width, height, bitmap.config.argb_8888);//创建一个空白的位图 mcanvas = new canvas(mbitmap);//在此bitmap上作画 //mcanvas.drawcolor(color.parsecolor("#c0c0c0"));//绘制背景颜色 mcanvas.drawroundrect(new rectf(0, 0, width, height), 20, 20, mpaint);//绘制圆角矩形 mlayerbimtap = bitmapfactory.decoderesource(getresources(), r.mipmap.fg_guaguaka); mcanvas.drawbitmap(mlayerbimtap, null, new rectf(0, 0, width, height), null);//绘制图层图片 @override protected void ondraw(canvas canvas) { //绘制文本 canvas.drawtext(mtext,getwidth()/2 - mtextbounds.width()/2,getheight()/2 + mtextbounds.height()/2,mtextpaint); //绘制图层 //canvas.drawbitmap(mbitmap,0,0,null); //绘制图层图片 canvas.drawbitmap(mbitmap,0,0,null); } }
如下图(图层颜色):
如下图(图层图片):
第三部分:设置xfermode以及绘制path
/**路径变量*/ private path mpath; private paint mpathpaint; private int mlastx;//记录上一次x坐标 private int mlasty;//记录上一次y坐标 /**初始化一些实例变量*/ private void initview() { settextmessage();//实例化文本的一些变量 setpathmessage();//实例化路径的一些变量 } @override public boolean ontouchevent(motionevent event) { int action = event.getaction(); //获取手指在屏幕上的坐标 int x = (int) event.getx(); int y = (int) event.gety(); switch (action){ case motionevent.action_down: mlastx = x; mlasty = y; mpath.moveto(mlastx,mlasty); break; case motionevent.action_move: int dx = math.abs(x - mlastx); int dy = math.abs(y - mlasty); if(dx > 3 || dy >3){ mpath.lineto(x, y); } mlastx = x; mlasty = y; break; case motionevent.action_up: break; default: break; } invalidate();//主线程中刷新 return true;//截取事件向下分发 } @override protected void ondraw(canvas canvas) { //绘制文本 canvas.drawtext(mtext,getwidth()/2 - mtextbounds.width()/2,getheight()/2 + mtextbounds.height()/2,mtextpaint); //绘制路径 drawpath(); //绘制图片【图层背景颜色】 //canvas.drawbitmap(mbitmap,0,0,null); //绘制图片【图层图片】 canvas.drawbitmap(mbitmap, 0, 0, null); } //返回给系统的canvas是一幅带有路径的位图 private void drawpath() { //这之前是设置了图层图片 mpathpaint.setxfermode(new porterduffxfermode(porterduff.mode.dst_out)); mcanvas.drawpath(mpath,mpathpaint); }
如下图
第三部分:清除所有图层区方案
/**擦除是否完成*/ private volatile boolean iscomplete; /**回调接口*/ private oncompletelistener moncompletelistener; public interface oncompletelistener{ void complete(); } //对外设置调用的公开方法 public void setmoncompletelistener(oncompletelistener moncompletelistener){ this.moncompletelistener = moncompletelistener; } case motionevent.action_up: //开启一个线程去计算擦除的面积 new thread(new runnable() { @override public void run() { int width = getwidth(); int height = getheight(); int area = 0;//记录被擦数的范围大小 int percent ; for(int i=0;i<width;i++) for(int j = 0;j<height;j++){ if(mbitmap.getpixel(i,j) == 0){ area ++; } } if(area > 0){ percent = area *100/(width*height); if(percent > 20){ iscomplete = true; postinvalidate();//在子线程中刷新,可能比较耗时操作 } } } }).start(); @override protected void ondraw(canvas canvas) { //绘制文本 canvas.drawtext(mtext, getwidth() / 2 - mtextbounds.width() / 2, getheight() / 2 + mtextbounds.height() / 2, mtextpaint); if(iscomplete){ if(moncompletelistener != null){ moncompletelistener.complete();//接口回调,在主线程中 } } if(!iscomplete){ //绘制路径 drawpath(); //绘制图片【图层背景颜色】 //canvas.drawbitmap(mbitmap,0,0,null); //绘制图片【图层图片】 canvas.drawbitmap(mbitmap, 0, 0, null); } } public class mainactivity extends appcompatactivity { @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); guaguale guagualeview = (guaguale) findviewbyid(r.id.guaguale); //注册了一个观察者 guagualeview.setmoncompletelistener(new guaguale.oncompletelistener() { @override public void complete() { toast.maketext(mainactivity.this, "用户绘制了大部分", toast.length_short).show(); } }); } }
如下图:
第五部分:自定义view的属性
在values/attrs.xml中定义 <declare-styleable name="guaguale"> <attr name="text" format="string"></attr> <attr name="textsize" format="dimension"></attr> <attr name="textcolor" format="color"></attr> </declare-styleable> 在布局文件中定义属性 <zlll.bg.com.example.cn.myapplication.view.guaguale android:id="@+id/guaguale" android:layout_width="350dp" android:layout_height="200dp" app:text="$5,000" app:textcolor="#c02040" app:textsize="22sp" android:layout_centerinparent="true"/> 在主界面中定义text文本 public class mainactivity extends appcompatactivity { @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); guaguale guagualeview = (guaguale) findviewbyid(r.id.guaguale); //注册了一个观察者 guagualeview.setmoncompletelistener(new guaguale.oncompletelistener() { @override public void complete() { toast.maketext(mainactivity.this, "用户绘制了大部分", toast.length_short).show(); } }); guagualeview.settextbyuser("android 新技能get"); } } /**中奖文本变量*/ private string mtext;//显示文本 private paint mtextpaint;//绘制文本的画笔 private rect mtextbounds;//文本的边界 //以下两个是新增的 private int mtextsize;//文本大小 private int mtextcolor;//文本颜色 //对外设置调用的设置文本公开方法 public void settextbyuser(string text){ mtext = text; mtextpaint.gettextbounds(mtext,0,mtext.length(),mtextbounds);//重新计算文本的范围 } 在构造函数中定义 public guaguale(context context, attributeset attrs) { super(context, attrs); //获取自定义属性的值 typedarray a = context.obtainstyledattributes(attrs,r.styleable.guaguale); mtext = a.getstring(r.styleable.guaguale_text); mtextcolor = a.getcolor(r.styleable.guaguale_textcolor, color.parsecolor("#c0c0c0")); //typedvalue.applydimension内部就是将sp,dp转化为px mtextsize = a.getdimensionpixelsize(r.styleable.guaguale_textsize, (int) typedvalue.applydimension (typedvalue.complex_unit_sp,22,getresources().getdisplaymetrics())); a.recycle(); initview(); }
如下图:
下图是用户自行设置的文本
经过上面几个步骤,就可以完成呱呱卡的实现效果了。
总结一下:
(1)圆形圆角图片的原理
1.绘制图片
2.paint.setxfermode(dstin); – 取交集以先绘制的图层为主 dst是背景图片
3.绘制圆形图片
1.绘制圆形图片
2.paint.setxfermode(srcin); – 取交集以后绘制的图层为主 src是结构图片
3.绘制图片
(2)双缓冲技术绘图【在内存中绘制,再一次性位图拷贝到屏幕上】
1.在内存中创建与画布一致宽高的canvas
2.在缓冲区画图
3.将缓冲区位图拷贝到当前画布上
4.释放缓冲区
自己的理解是:在内存中定义一个画板,然后在上面绘图,将绘图结果放在一个bitmap中,最后让系统的canvas绘制这个bitmap
(3)bimtap获取像素数据(方式一)
bm.getpixels(x,y) 直接获取x,y坐标处的像素点数据
(4)获取绘制文本的长宽的(方式一)
1.计算文字所在的矩形,获取宽高[rect]
rect rect = new rect();
paint.gettextbounds(str,0,str,length,rect);
int w = rect.width;
int h = rect.height;
(5)自定义view
1.在values目录下新建attrs.xml文件
2.设置属性有text,textcolor,textsize
3.在布局文件中引用
4.在构造函数中获取自定义属性
5.对外提供方法,让用户动态的设置中奖信息
(6)获取屏幕密度(方式一)
displaymetrics dm = getresource().getdisplaymetrics();
float density = dm.density; – 像素比例(0.75/1.0/1.5/2.0/3.0/4.0)显示的就是px/dp的比例
int densitydpi = dm.densitydpi; – 每寸像素(120/160/240/320/480/640)
int screenwidth = dm.widthpixels; – 宽像素
int screenheight = dm.heightpixels; – 高像素
(7)canva的一些方法
drawcolor 绘制背景颜色
drawtext 绘制文本
drawbitmap 绘制图片
【drawbitmap(bitmap,x,y,paint) – 将图画到指定坐标】
【drawbitma(bitmap,srcrect,dstrect,paint) – 将图指定区域其参数是四个顶点的坐标,左上右下】
drawroundrect 绘制圆角矩形
【drawroundrect(rectf,rx,ry,paint) – rx(x方向上的圆角半径) ry(y方向上的圆角半径)】
(8)paint的一些常用属性
setcolor/setargb 设置绘制颜色
setalpha 设置绘制透明度
setantialias 设置平滑
setdither 设置图像抖动
setstyle 设置画笔的样式
setstrokewidth 设置画笔的宽度
setxfermode 设置图形重叠时处理方式
setstrokejoin 设置图形结合方式
settextsize 设置文本的大小、
(9)path(将view上的n个点连成一条“路径”,然后调用canvas的drawpath绘制图形)的一些方法
reset 清除path
moveto(x,y) 将起始点移至x,y坐标点
lineto 绘制直线
quadto 绘制曲线
以上就是本文的全部内容,你中奖了吗?希望对大家的学习有所帮助。