看效果: 中间一个画图板 上方小控件用来显示实时画出的图形 下方小控件用来做一些画图的控制 2个小控件都能移动
顺带还有一个刮刮卡效果,只需要改一个参数:
自定义view首先要自定义属性:
在values下面创建attrs.xml:
<!--画图板--> <declare-styleable name="Drawimg"> <attr name="Paintcolor" /> //画笔颜色 <attr name="PaintWIDth" /> // 画笔宽度 <attr name="Canvasimg" /> //画板图片 </declare-styleable> <!--指定单位--> <attr name="Paintcolor" format="color" /> <attr name="PaintWIDth" format="dimension" /> <attr name="Canvasimg" format="reference" />
对于下面3行指定单位的代码可以放出来,可以让多个自定义view 都能使用。
接下来新建自定义view类继承vIEw,重写前3个构造方法
红线标注是androID studio 3.0.0对于参数提示的新特性
通过this 让前2个构造方法都实现3个参数的构造方法。
简单说一下构造方法。一个参数的构造方法是在代码中 new 时用到,2个参数的构造方法在布局xml中用到,3个参数的基本就是自定义view类中使用,大概就是这样。
接下来从attrs.xml中通过TypedArray取出自定义属性:
@H_404_43@//从attrs文件中取出各个属性 TypedArray a = context.gettheme().obtainStyledAttributes(attrs,R.styleable.Drawimg,defStyleAttr,0); for (int i = 0; i < a.getIndexCount(); i++) { int attr = a.getIndex(i); switch (attr) { case R.styleable.Drawimg_PaintWIDth: //画笔宽度 paintWIDth = a.getDimensionPixelSize(attr,(int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP,-1,getResources().getdisplayMetrics())); break; case R.styleable.Drawimg_Paintcolor: //画笔颜色 paintcolor = a.getcolor(attr,color.GREEN); break; case R.styleable.Drawimg_Canvasimg: //画板图片 hasCanvasimg = a.getResourceID(attr,-1); break; } } //设置默认画笔宽度 if (paintWIDth == -1) { paintWIDth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,20,context.getResources().getdisplayMetrics()); } //取出bitmap if (hasCanvasimg != -1) { bitmap = BitmapFactory.decodeResource(getResources(),hasCanvasimg); } //onMeasure可能走多次,onDraw创建对象更不好 所以把画笔路径new在这里 path = new Path();需要默认值的设置默认值,以免布局中没有用到自定义属性导致报错。
重写自定义view关键方法onMeasure(),onDraw()。onMeasure()用来指定这个自定义view 的大小,onDraw()用来进行实时绘图
最重要的3个东西:画布Canvas,画笔Paint,路径Path
代码略长但是注释很全,把需要注意的提出来
在newPaint()方法中,paint有一个setXfermode()方法,这个表示图形混合方式,有18种 ~(比下图多了ADD和OVERLAY)~。给张图看一下。这里我们用到2种 SRC_IN和 DST_OUT。
SRC_IN:取两层交集部分,显示上层
DST_OUT:取两层非交集部分,显示下层
说实话这么说也很难懂,还是要自己动手试一试,不过这里只要知道:
使用SRC_IN就会有一个画图板的效果
使用DST_OUT就会有一个刮刮卡的效果
这是一堆对于这个vIEw来说比较复杂的代码,但是功能很简单,我们做了2件事:
1.通过MeasureSpec.getMode(测量模式),计算出整个控件的宽高
2.通过canvas.drawBitmap在画布上画出bitmap,同时 new 出画笔 Paint 给它设置颜色,粗细等属性
注意:
1.onDraw()方法在每次调用invalIDate(),或者视图变化时都会重走,所以不能在里面 new 东西.
2.有一个int[]类型的数组 bmPixels,这里大概说一下是个什么意思,具体的解释在Bitmap类getPixels和createBitmap方法详解中有说道。
bmPixels: 我们通过bitmap的宽度乘以高度,可以的到一个int[]类型的数组,这个数组就是组成bitmap的所有像素点,某一个像素点为0的时候就说明他是没有颜色,!0就说明是有颜色的。
既然是画图,那肯定要监听手指移动,ontouchEvent()方法:
@H_404_43@@OverrIDe public boolean ontouchEvent(MotionEvent event) { int currX = (int) event.getX(); int currY = (int) event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: //按下时,设置线条的起始点准备绘制 path.moveto(currX,currY); break; case MotionEvent.ACTION_MOVE: //滑动时,绘制路径 path.lineto(currX,currY); break; case MotionEvent.ACTION_UP: } // 绘制线条,请求重绘整个控件 canvas.drawPath(path,paint); //请求VIEw树进行重绘,即draw()方法,如果视图的大小发生了变化,还会调用layout()方法。 invalIDate(); return true; }这个就很简单,手指按下时记录位置,path.moveto给path设置起始点位置,移动时通过path.lineto()方法记录路径,同时使用 canvas.drawPath(path,paint)直接绘制出来,invalIDate()通知视图更新。
写到这里,在xml布局中使用这个vIEw,已经能画一画了
我们的画笔Paint类,可以指定颜色,粗细,模式,等等,这样我们就可以写一些公开的方法,给它动态的设置这些属性,从而让画笔更加多样性。
@H_404_43@//设置画笔颜色 public voID setPaintcolor(int color) { //path = new Path(); path.reset(); paint.setcolor(color); } //设置画笔类型 public voID setPaintMode(int style) { //path = new Path(); path.reset(); /** * SRC_IN:取两层交集部分,显示上层 * DST_OUT:取两层非交集部分,显示下层 */ if (style == 1) { paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); } else { paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); } resetCanvaas(); } //设置画布重置 public voID resetCanvaas() { //path = new Path(); path.reset(); canvas.drawBitmap(bitmap,null); invalIDate(); Listener.bitmapchangelistener(bitmap); }上面代码 设置画笔颜色 ,设置画笔类型以及画布重置为什么都要new Path呢,因为如果不新开一个路径给画笔,当你设置了新的颜色,用的还是以前的Path,画笔就会把以前的Path也重新设置新颜色,而不是保持原来的颜色。
这样就会出现一个问题,每次都在new Path,new一次创建一次,占用一次内存,想到一些避免方法,但是本文画图不是重点,就不在论述。(已改用path.reset())
效果中的右上角,显示了一个float类型的数,它是在刮刮卡模式下,已经抹掉部分所占bitmap的比例,onMeasure()方法中有一个int[]类型的数组 bmPixels ,这个时候我们就要利用这个数组来得到这个比例。
在ontouchEvent()方法的case MotionEvent.ACTION_UP加上一些代码:
@H_404_43@@OverrIDe public boolean ontouchEvent(MotionEvent event) { int currX = (int) event.getX(); int currY = (int) event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: //按下时,设置线条的起始点准备绘制 path.moveto(currX,currY); //通过回调,实时把bitmap显示出去 Listener.bitmapchangelistener(newBitmap); break; case MotionEvent.ACTION_UP: //抬起手指时,计算图片抹去了多少 int nullPixel = 0; newBitmap.getPixels(bmPixels,height); for (int i = 0; i < bmPixels.length; i++) { //抹去部分的像素点在数组中就会表示为0,找出为0的个数 if (bmPixels[i] == 0) { nullPixeL++; } } //计算抹去部分所占的百分比 Listener.showBitmapClear((float) nullPixel / (float) bmPixels.length); break; } // 绘制线条,请求重绘整个控件 canvas.drawPath(path,paint); //请求VIEw树进行重绘,即draw()方法,如果视图的大小发生了变化,还会调用layout()方法。 invalIDate(); return true; }有一句 newBitmap.getPixels(bmPixels,height);在getPixels方法详解中有解释,它的作用就是把newBitmap 中所有的像素点全部取出来,放到方法中的第一个参数bmPixels中。这个时候,我们再通过for循环遍历bmPixels数组,等于0的说明是没有颜色被抹掉的,统计他们的数量,计算他们所占的比例,就能算出抹掉的比例。同理我们也可以改变等于0这个判断条件,让他等于其他颜色,这样也就可以计算其他颜色所占比例。
写个回调接口,在代码中取出来就OK了。
有2个接口,一个实时的展示bitmap,一个展示抹去比例。
总结以上是内存溢出为你收集整理的Android 自定义view之画图板实现方法全部内容,希望文章能够帮你解决Android 自定义view之画图板实现方法所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)