Android自定义控件之控件的测量及形状。之前讲了自定义控件的绘制和动画的解决方案,现在进一步确定控件的onMeasure函数以及形状。
一、重写onMeasure函数
似乎网上的各种自定义控件的案例,重写onMeasure函数都是大同小异,无非都是为了wrap_content模式。
其代码思路无外乎是根据特定的模式以及传入的大小参照进行确定最终的大小。
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = measureDimension(radius*2, widthMeasureSpec); int height = measureDimension(radius*2, heightMeasureSpec); setMeasuredDimension(width, height); } public int measureDimension(int defaultSize, int measureSpec){ int result; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if(specMode == MeasureSpec.EXACTLY){ result = specSize; }else{ result = defaultSize;//UNSPECIFIED if(specMode == MeasureSpec.AT_MOST){ result = Math.min(result, specSize); } } return result; }
二、控件的圆形化
需求是一个圆形控件,但在上一篇文章中做出来的效果明显是两个圆在动,但真正的效果应该是上层圆只有在底层圆的区域才能被看到,不应该在两个圆不重叠的地方还能看到上层圆。而实现的要点就在Xfermode类。
先上标准图:
其中,先绘制的是dst,后绘制的是src。使用Xfermode最重要的是搞清楚哪个是dst,哪个是src。我曾经以为搞清楚这个问题,然后按照标准图去确定用哪个模式就可以了。但我用起来到处踩坑。。。。。。
如果说先绘制底层圆(dst),再绘制上层圆(src),应该用的是DstATop模式,但是效果是什么都没有,而换成SrcATop就跟没做一样,在两个圆没重叠的区域还是能看到上层圆。两个圆分别用两个画笔,设XFerMode两个都试过,各个模式也试过,要么啥都没有,要么跟没设置一样。折腾了好久最后请教了导师,说是要先savelayer保存图层。我的问题在于我第一次画底层圆的时候直接画在画布上,而此时底层圆和画布是一个整体,再去画上层圆,使用Xfermode的时候检测到的是下方整块画布,也就是整个屏幕区域都被Xfermode使用了,而不是我们原意用画出来的底层圆部分。
这篇文章会讲的更仔细一些:自定义控件三部曲之绘图篇(十三)——Canvas与图层(一)
于是代码按照上述文章更改如下:(就是抄人家的代码,然后加上自己的贝塞尔曲线运动部分)
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if ((percentLength+=0.005) >= 1) percentLength = 0; pathMeasure.getPosTan(pathMeasure.getLength() * percentLength, pos, tan); canvas.saveLayer(0, 0, radius * 2, radius * 2, paint, Canvas.ALL_SAVE_FLAG); canvas.drawBitmap(dstBmp, 0, 0, paint); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); canvas.drawBitmap(srcBmp, pos[0]-radius, pos[1]-radius/2, paint); paint.setXfermode(null); canvas.restore(); } static Bitmap makeDst(int w, int h) { Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); Canvas c = new Canvas(bm); Paint p = new Paint(Paint.ANTI_ALIAS_FLAG); p.setColor(0xFFFFCC44); c.drawOval(new RectF(0, 0, w, h), p); return bm; } static Bitmap makeSrc(int w, int h) { Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); Canvas c = new Canvas(bm); Paint p = new Paint(Paint.ANTI_ALIAS_FLAG); p.setColor(0xFF66AAFF); c.drawOval(0, 0, w, h, p); return bm; }
问题思考:1、之前运动是按圆心drawCircle,但现在是drawBitmap,坐标就出现偏移了。这里的坐标转换稍微有点混乱,我只大概的用半径加加减减变回了以前的正确路径。
2、虽然现在Xfermode是起作用了,但是上层圆的显示是一个方框加一个圆,也就是说把透明的正方形画布也显示进去了。
以上问题解决:不要把上层圆画在bitmap上,像上一篇文章一样画在画布上就行。
三、动画补充
1、上层圆的旋转
由于上层圆在按既定路径运动的同时还需要依据底层圆心旋转,效果是脑补不出来了,而由于这涉及到公司,也不放出效果了。
旋转原本打算直接用坐标加减sin cos算,但脑子没有绕出来。而在上一点的Xfermode知道了图层这个东西,遂想到去旋转图层。
canvas.rotate(angle, radius, radius);//角度、圆心坐标X、圆心坐标Y
2、旋转速度
由于后来听导师的用view.postdelay去做动画,所以在旋转速度上直接在postdelay的时间上做文章即可。
到此控件全部完成!