View绘制流程与自定义View注意点
详细讲解
本问题的系统回答大家可以移步到高级UI课程里面,这个问题是一个综合问题,设计自定义View的各方面
这道题想考察什么?
是否了解View绘制流程与自定义View注意点与实际场景使用,是否熟悉View绘制流程与自定义View注意事项
考察的知识点
View绘制流程与自定义View注意事项的概念在实际项目中使用
考生应该如何回答
这个问题先需要回答View的绘制流程,然后再回到自定义View的注意点。
9.2.1. View的绘制流程
这个问题的回答请看7-1题,View的绘制原理。
9.2.2. 自定义View注意点?
1) View需要实现四个构造函数
自定义View中构造函数有四种
// 主要是在java代码中new一个View时所调用,没有任何参数,一个空的View对象
public ChildrenView(Context context) {
super(context);
}
// 在布局文件中使用该自定义view的时候会调用到,一般会调用到该方法
public ChildrenView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
//如果你不需要View随着主题变化而变化,则上面两个构造函数就可以了
//下面两个是与主题相关的构造函数
public ChildrenView(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public ChildrenView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
四个参数解释:
context:上下文
AttributeSet attrs:在xml中定义的参数内容
int defStyleAttr:主题中优先级最高的属性
int defStyleRes: 优先级次之的内置于View的style(这里就是自定义View设置样式的地方),只有当defStyleAttr为0或者当前Theme中没有给defStyleAttr属性赋值时才起作用.
在android中的属性可以在多个地方进行赋值,涉及到的优先级排序为:在布局xml中直接定义 > 在布局xml中通过style定义 > 自定义View所在的Activity的Theme中指定style引用 > 构造函数中defStyleRes指定的默认值
2)自定义View的种类各不相同,须要根据实际须要选择一种简单低成本的方式来实现,尽可能的减少UI的层级,view的种类如下,开发中需要尽可能的选择适合自己的。
- 继承View重写onDraw方法
主要用于实现不规则的效果,也就是说这种效果不适宜采用布局的组合方式来实现。也就是需要使用canvas,Paint,运用算法去“绘制”了。采用这种方式须要本身支持wrap_content,padding也须要本身处理canvas。
- 继承ViewGroup派生特殊的Layout
主要用于实现自定义的布局,看起来很像几种View组合在一块儿的时候,可使用这种方式。这种方式须要合适地处理ViewGroup的测量和布局,尤其在测量的时候需要注意padding 和margin,比如:自定义一个自动换行的LinerLayout等。
- 继承特定的View,好比TextView
这种方法主要是用于扩展某种已有的View,增长一些特定的功能。这种方法比较简单,也不须要本身支持wrap_content和padding。这种效果就相当于我们使用imageView来实现一个圆形的imageView。
- 继承特定的ViewGroup,好比LinearLayout
这种方式也比较常见,也就是基于原来已经存在的layout,在其上去添加新的功能。和上面的第2种方法比较相似,第2种方法更佳接近View的底层。
3)View需要支持padding
直接继承View的控件需要在onDraw方法中处理padding,否则用户设置padding属性就不会起作用。直接继承ViewGroup的控件需要在onMeasure和onLayout中考虑padding和子元素的margin对其造成的影响,不然将导致padding和子元素的margin失效。
4)尽量不要在view中使用handler
如果在view中需要使用handler来处理异步信息,这个时候尽量使用view中的post方法,因为view中给用户提供了post方法,这个方法的处理逻辑是:将Runable 的action 保存到一个队列,在viewRootImpl里面执行度量后再添加进Handler的MessageQueue中,这样就保障了action里面执行的内存是在完成度量 后的结果,避免了使用延迟消息带来的困扰具体的代码如下:
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
...
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
// Postpone the runnable until we know on which thread it needs to run.
// Assume that the runnable will be successfully placed after attach.
getRunQueue().post(action);
return true;
}
public boolean postDelayed(Runnable action, long delayMillis) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.postDelayed(action, delayMillis);
}
// Postpone the runnable until we know on which thread it needs to run.
// Assume that the runnable will be successfully placed after attach.
getRunQueue().postDelayed(action, delayMillis);
return true;
}
...
}
5)在自定义view的onMeasure,onDraw,onLayout方法中尽量少使用局部变量
由于onMeaaure &onDraw&onLayout这3个函数都可能存在频繁调用的可能,尤其在动画里面,onDraw是非常频繁的调度的,在这些频繁被调用的函数里面,开发过程中一定要尽量避免创建局部对象,尤其是比较占用内存的像bitmap等的局部对象,这很容易产生内存抖动,而内存抖动容易带来手机app的卡顿,具体的细节大家可以去享学课堂查找对应的课程进行学习。
最后
有需要以上面试题的朋友可以关注一下哇哇,以上都可以分享!!!
网友评论