本文共 4225 字,大约阅读时间需要 14 分钟。
之前也有一个类似于画板的操作,但是不够详细,这边先补上链接,有兴趣的小伙伴可以看看
还是老规矩,将内容之前说下我们的一些知识点,今天的第一个知识点有点高DA上的名词“硬件加速”。
硬件加速:
问题1:这玩意有什么用?
在绘制View的时候支持硬件加速,充分利用GPU的特性,使得绘制更加平滑。问题2:怎么用?
在AndroidManifest的Application 或者Activity节点<application android:hardwareAccelerated="true/false">
就可以进行开关。或者用Java代码:getWindow().setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
如果这些你都不喜欢,你只要那个类当中的某个View是对这部分功能有需求的,那么这样:view.setLayerType(View.LAYER_TYPE_SOFTWARE, null); 但是要记住Window只能打开,View只能关闭所以还是在Activity和Application层面操作吧。问题3:非用不可吗?
如果你的View的绘制内容比较复杂,比较对多,还是建议开着,诸如画密度很细的那种虚线或者颜色区域草鸡付擦的一些试图的画还是开着吧,不然说不定你再挂个什么网络的操作万一客户机烂一点一个未响应提示就来了。Paint:
这部分的内容就不提了,网上多如牛毛,贴一个写的比较简单易通的
Canvas:
如上,还是附上可参考的链接:
性能优化(跟自定义绘图相关的):
1.设置缓存:View 中设置缓存属性.setDrawingCache为true
2.优化布局:tools目录下的layoutopt 命令 3.将Acitivity 中的Window 的背景图设置为空(默认不为空)。getWindow().setBackgroundDrawable(null); 4.采用SurfaceView在子线程刷新UI,:避免手势的处理和绘制在同一UI线程(普通View都这样做)。 5:采用JNI:将耗时间的处理放到c/c++层来处理。
主要来说下自定义控件重要的三个方法“onMeasure()”,“onDraw()”,“onLayout()”:
1.onMeasure()方法就是用于测量视图的大小的,不然你的空间永远都只是白屏,不是没画上去,而是没有尺寸。
measure()方法接收两个参数,widthMeasureSpec和heightMeasureSpec也就是宽度和长度。
MeasureSpec的值由specSize和specMode共同组成的,specSize是大小,specMode是规格。
有三种规格:1. EXACTLY,AT_MOST,UNSPECIFIED。这三种规格细说太复杂了,我整合给大家听下
(1):子视图的大小应该是由specSize的值来决定,用户也可自行定义
(2):子视图最多只能是specSize中指定的大小,只能更小不能更大了
(3):可以将视图按照自己的意愿设置成任意的大小,没有任何限制
当rootDimension参数等于MATCH_PARENT的时候,MeasureSpec的specMode就等于EXACTLY,当rootDimension等于WRAP_CONTENT的时候,MeasureSpec的specMode就等于AT_MOST。并且MATCH_PARENT和WRAP_CONTENT时的specSize都是等于windowSize的,也就意味着根视图总是会充满全屏的。
这里的rootDimension是根布局的尺寸总结:视图大小的控制是由父视图、布局文件、以及视图本身共同完成的,父视图会提供给子视图参考的大小,而开发人员可以在XML文件中指定视图的大小,然后视图本身会对最终的大小进行拍板。
不然就会出现onCreate时发现控件宽高都是0,之前有提到,看这里2.onLayout()用于给视图进行布局的,也就是确定视图的位置,会在onMeasure方法之后执行。
layout()方法接收四个参数,分别代表着左、上、右、下的坐标,当然这个坐标是相对于当前视图的父视图而言的。
像这样layout(0, 0, host.mMeasuredWidth, host.mMeasuredHeight);
3.onDraw(),measure和layout的过程都结束后,接下来就进入到这个方法了。我们所有的作画的事,都在这干!
ViewRoot中的代码会继续执行并创建出一个Canvas对象,然后调用View的draw()方法来执行具体的绘制工作
我们的绘画都是在这个Canvas对象的支持下实现的,简单写个例子。
public class TestView extends View { Paint paint; Context context; public TestView(Context context) { super(context); this.context = context; } public TestView(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } public TestView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public TestView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(context, attrs); } private void init(Context context, AttributeSet attrs) { paint = new Paint();// paint.setColor(getResources().getColor(R.color.SlateBlue)); paint.setColor(getResources().getColor(R.color.Gold)); paint.setStrokeWidth(3); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Log.d("--->onDraw", "onDraw()");// canvas.drawCircle(0, 0, 90, paint); canvas.drawCircle(100, 100, 90, paint); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); Log.d("--->onLayout", "changed = " + changed + " left = " + left + " top = " + top + " right = " + right + " bottom " + bottom); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); Log.d("--->onMeasure", " widthMeasureSpec =" + widthMeasureSpec + " heightMeasureSpec = " + heightMeasureSpec); setMeasuredDimension(widthMeasureSpec, heightMeasureSpec); }}
注释部分运行效果:
现有代码效果:
打印的顺序
请仔细看onLayout()的那些坐标,他任然保持着父布局的一系列属性参数:
android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin"
相关资源收集: