目录介绍
- 3.0.0.1 View的绘制需要经过哪些过程?有哪些常用回调方法?View的绘制流程的详细流程是怎样的?
- 3.0.0.2 View绘制流程,当一个TextView的实例调用setText()方法后执行了什么?请说一下原理……
- 3.0.0.3 requestLayout()、invalidate()与postInvalidate()有什么区别?
- 3.0.0.4 DecorView的作用是什么?DecorView中如何获取ContentView以及Activity所设置的View?ViewRootIml如何和DecorView建立联系?
- 3.0.0.5 getWidth()方法和getMeasureWidth()区别呢?为什么有时候用getWidth()或者getMeasureWidth()得到0?
- 3.0.0.6 平时写的自定义控件有哪些?如何优化自定义view?View的绘制流程说一下?自定义View的注意点?
- 3.0.0.7 View的wrap_content和match_parent效果一致的原因分析?getDefaultSize方法的处理逻辑?
- 3.0.0.8 ViewGroup(抽象类)的measure流程?getChildMeasureSpec获取子元素MeasureSpec的要点?
- 3.0.0.9 View的layout过程?View的layout()源码分析?LinearLayout的onLayout方法?View的测量宽高和最终宽高有什么区别?
- 3.0.1.0 draw的过程步骤是什么?View特殊方法setWillNotDraw是干什么用的?
- 3.0.1.1 View中x,y,translationX,translationY分别是什么?View平移时是否改变了left、top等原始参数?
- 3.0.1.2 MeasureSpec是什么?MeasureSpec的组成?测量模式SpecMode的类型和具体含义?MeasureSpec和LayoutParams的对应关系?
- 3.0.1.3 如何获取View的测量宽/高?如何在Activity启动时获得View的宽/高?Activity中获得View宽高的4种办法?
- 3.0.1.4 Activity启动到最终加载ViewRoot(执行三大流程)的流程是什么?
- 3.0.1.5 自定义View性能优化有哪些?针对异常销毁,自定义View如何优化?如何避免创建大量对象?
好消息
- 博客笔记大汇总【15年10月到至今】,包括Java基础及深入知识点,Android技术博客,Python学习笔记等等,还包括平时开发中遇到的bug汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是markdown格式的!同时也开源了生活博客,从12年起,积累共计500篇[近100万字],将会陆续发表到网上,转载请注明出处,谢谢!
- 链接地址:https://github.com/yangchong211/YCBlogs
- 如果觉得好,可以star一下,谢谢!当然也欢迎提出建议,万事起于忽微,量变引起质变!所有的笔记将会更新到GitHub上,同时保持更新,欢迎同行提出或者push不同的看法或者笔记!
3.0.0.1 View的绘制需要经过哪些过程?有哪些常用回调方法?View的绘制流程的详细流程是怎样的?
- View的绘制需要经过哪些过程?
- measure:测量View的宽和高
- View的measure方法是final类型方法——表明该方法无法被重载
- View的measure方法会调用onMeasure方法,onMeasure会调用setMeasuredDimension方法设置View宽/高的测量值
- layout:确定View在父控件中的放置位置
- draw:负责将View绘制在屏幕上。技术博客大总结
- measure:测量View的宽和高
- 有哪些常用回调方法?
- 构造方法
- onAttachToWindow:在包含View的Activity启动时调用
- onDetachFromWindow:在包含View的Activity退出或者View被remove时回调
- onVisibilityChanged:当View的可见状态发生改变时调用
- 比较重要的概念
- ViewRoot:连接WindowManager(外界访问Window的入口)和DecorView(顶级View)的纽带,View的三大流程均是通过ViewRoot来完成的。
- DecorView:顶级View
- DecorView是顶级View,本质就是一个FrameLayout
- 包含了两个部分,标题栏和内容栏
- 内容栏id是content,也就是activity中setContentView所设置的部分,最终将布局添加到id为content的FrameLayout中
- View的绘制流程的详细流程是怎样的?技术博客大总结
- View的绘制流程是从ViewRoot的PerformTraversals方法开始的。大概的流程如下所示
- performTraversals会依次调用performMeasure, performLayout, performDraw三个方法,这三个方法分别完成顶层View的measure,layout,draw方法,onMeasure又会调用所有子元素的measure过程,直到完成整个View树的遍历。同理,performLayout, performDraw的传递流程与performMeasure相似。唯一不同在于,performDraw的传递过程在draw方法中通过dispatchDraw实现,但没有本质区别。
- Measure过程后可以调用getMeasureWidth和getMeasureHeight方法获取View测量后的宽高,与getWidth和getHeight的区别是:getMeasuredHeight()返回的是原始测量高度,与屏幕无关,getHeight()返回的是在屏幕上显示的高度。实际上在当屏幕可以包裹内容的时候,他们的值是相等的,只有当view超出屏幕后,才能看出他们的区别。当超出屏幕后,getMeasuredHeight()等于getHeight()加上屏幕之外没有显示的高度。
- Layout过程确定View四个顶点的位置和实际的宽高。
- Draw过程确定View的显示,只有draw方法完成后View的内容才会出现在屏幕上。
- View的绘制流程是从ViewRoot的PerformTraversals方法开始的。大概的流程如下所示
3.0.0.2 View绘制流程,当一个TextView的实例调用setText()方法后执行了什么?请说一下原理……
- View的绘制流程主要分为三步:
- onMeasure:测量视图的大小,从顶层父View到子View递归调用measure()方法,measure()调用onMeasure()方法,onMeasure()方法完成绘制工作。
- onLayout:确定视图的位置,从顶层父View到子View递归调用layout()方法,父View将上一步measure()方法得到的子View的布局大小和布局参数,将子View放在合适的位置上。
- onDraw:绘制最终的视图,首先ViewRoot创建一个Canvas对象,然后调用onDraw()方法进行绘制。onDraw()方法的绘制流程为
- ① 绘制视图背景。
- ② 绘制画布的图层。 技术博客大总结
- ③ 绘制View内容。
- ④ 绘制子视图,如果有的话。
- ⑤ 还原图层。
- ⑥ 绘制滚动条。
3.0.0.3 requestLayout()、invalidate()与postInvalidate()有什么区别?requestLayout()何时不会触发onDraw()?
- invalidate() postInvalidate()
- invalidate()该方法递归调用父View的invalidateChildInParent()方法,直到调用ViewRootImpl的invalidateChildInParent()方法,最终触发ViewRootImpl的performTraversals()方法,此时mLayoutRequestede为false,不会触发onMesaure()与onLayout()方法,有可能会触发onDraw()方法。
- 共同点:都是调用onDraw()方法,然后去达到重绘view的目的
- 区别:invalidate()用于主线程,postInvalidate()用于子线程
- requestLayout()
- 该方法会递归调用父窗口的requestLayout()方法,直到触发ViewRootImpl的performTraversals()方法,此时mLayoutRequestede为true,会触发onMesaure()与onLayout()方法,不一定会触发onDraw()方法。
- 当view确定自身已经不再适合现有的区域时,该view本身调用这个方法要求parent view(父类的视图)重新调用他的onMeasure、onLayout来重新设置自己位置。特别是当view的layoutparameter发生改变,并且它的值还没能应用到view上时,这时候适合调用这个方法requestLayout()。requestLayout调用onMeasure和onLayout,不一定调用onDraw技术博客大总结
- 如何选择
- 一般说来需要重新布局就调用requestLayout()方法,需要重新绘制就调用invalidate()方法。
- requestLayout()何时不会触发onDraw()?
- 如果没有改变控件的left ight opottom就不会触发onDraw()
- invalidate()在什么情况下不会触发onDraw?
- 在ViewGroup中,invalidate默认不重新绘制子view。
- 如何让ViewGroup在invalidate时会触发onDraw?技术博客大总结
- 本质需要将ViewGroup的dirtyOpaque设置为false
- 1.在构造函数中调用setWillNotDraw(false);
- 2.给ViewGroup设置背景。调用setBackground。
- 本质需要将ViewGroup的dirtyOpaque设置为false
3.0.0.4 DecorView的作用是什么?DecorView中如何获取ContentView以及Activity所设置的View?ViewRootIml如何和DecorView建立联系?
- DecorView的作用是什么?
- DecorView是顶级View,本质就是一个FrameLayout
- 包含了两个部分,标题栏和内容栏,内容栏id是content,也就是activity中setContentView所设置的部分,最终将布局添加到id为content的FrameLayout中技术博客大总结
- DecorView中如何获取ContentView以及Activity所设置的View?
- 获取content:ViewGroup content = findViewById(R.android.id.content)
- 获取设置的View:content.getChidlAt(0)
- ViewRootIml如何和DecorView建立联系?技术博客大总结
- Activity对象在ActivityThread中创建完毕后,会将DecorView添加到Window中
- 同时会创建ViewRootImpl,调用ViewRoot的setView方法将ViewRootImpl和DevorView建立关联
root = new ViewRootImpl(view.getContext(), display); root.setView(view, wparams, panelParentView);
- ViewRoot为什么要和DecorView建立关联
- DecorView等View的三大流程需要通过ViewRoot完成
3.0.0.5 getWidth()方法和getMeasureWidth()区别呢?为什么有时候用getWidth()或者getMeasureWidth()得到0?
- getWidth()方法和getMeasureWidth()区别呢
- getMeasureWidth()
- getMeasureWidth()方法在measure()过程结束后就可以获取到了,另外,getMeasureWidth()方法中的值是通过setMeasuredDimension()方法来进行设置的
- 这里mMeasuredWidth & MEASURED_SIZE_MASK表示的是测量阶段结束之后,view真实的值。
public final int getMeasuredWidth() { return mMeasuredWidth & MEASURED_SIZE_MASK; }
- getWidth()
- getWidth()方法要在layout()过程结束后才能获取到,getWidth()方法中的值则是通过视图右边的坐标减去左边的坐标计算出来的。
- mRight和mLeft是什么值,是在什么时候被设置的。具体看layout()过程中源码
@ViewDebug.ExportedProperty(category = "layout") public final int getWidth() { return mRight - mLeft; }
- getMeasureWidth()
- 为什么有时候用getWidth()或者getMeasureWidth()得到0
- 问题描述:使用getMeasuredWidth()和getMeasuredHeight()方法,无论是在onCreate()、onStart()、onResume()中调用,都无法得到控件的长度、和宽度。如下图,测量的结果为0。
- 解释:技术博客大总结
- 因为View的Measure过程和Activity的生命周期方法不是同步执行的,所以无法保证Activity执行了onCreate()、onStart()、onResume()时某个View已经测量完毕了,如果View还没有测量完毕,那么获得宽/高就是0。
- 后来觉得这种解释有点牵强,比如
- 解决控件测量宽高问题
- 如下所示
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); test(); } @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); getWidth(4); } private void test(){ getWidth(1); marqueeView.measure(0, 0); getWidth(2); marqueeView.post(new Runnable() { @Override public void run() { getWidth(3); } }); } private void getWidth(int a){ int width2 = marqueeView.getWidth(); int measuredWidth2 = marqueeView.getMeasuredWidth(); Log.e(a+"MainActivity-----",width2+"-----"+measuredWidth2); } //11-28 17:03:17.559 15990-15990/com.yc.cn.ycbanner E/1MainActivity-----: 0-----0 //11-28 17:03:17.567 15990-15990/com.yc.cn.ycbanner E/2MainActivity-----: 0-----760 //11-28 17:03:17.684 15990-15990/com.yc.cn.ycbanner E/3MainActivity-----: 960-----960 //11-28 17:03:17.685 15990-15990/com.yc.cn.ycbanner E/4MainActivity-----: 960-----960
- 什么时候测量宽高不等于实际宽高?
- MeasuredWidth/height!=getWidth/Height()的场景:更改View的布局参数并进行重新布局后,就会导致测量宽高!=实际宽高
3.0.0.6 平时写的自定义控件有哪些?如何优化自定义view?View的绘制流程说一下?自定义View的注意点?
- 平时写的自定义控件有哪些?
- 1组合控件。这种自定义控件不需要我们自己绘制,而是使用原生控件组合成的新控件。如标题栏,recyclerView封装控件。
- 2继承原有的控件。这种自定义控件在原生控件提供的方法外,可以自己添加一些方法。比如图片缩放控件,进度条控件。
- 3完全自定义控件:这个View上所展现的内容全部都是我们自己绘制出来的。比如百分比进度条控件
- 如何优化自定义 view
- 为了加速你的view,对于频繁调用的方法,需要尽量减少不必要的代码。先从onDraw开始,需要特别注意不应该在这里做内存分配的事情,因为它会导致GC,从而导致卡顿。在初始化或者动画间隙期间做分配内存的动作。不要在动画正在执行的时候做内存分配的事情。
- 你还需要尽可能的减少onDraw被调用的次数,大多数时候导致onDraw都是因为调用了invalidate().因此请尽量减少调用invaildate()的次数。如果可能的话,尽量调用含有4个参数的invalidate()方法而不是没有参数的invalidate()。没有参数的invalidate会强制重绘整个view。技术博客大总结
- 另外一个非常耗时的操作是请求layout。任何时候执行requestLayout(),会使得Android UI系统去遍历整个View的层级来计算出每一个view的大小。如果找到有冲突的值,它会需要重新计算好几次。另外需要尽量保持View的层级是扁平化的,这样对提高效率很有帮助。
- 如果你有一个复杂的UI,你应该考虑写一个自定义的ViewGroup来执行他的layout操作。与内置的view不同,自定义的view可以使得程序仅仅测量这一部分,这避免了遍历整个view的层级结构来计算大小。这个PieChart 例子展示了如何继承ViewGroup作为自定义view的一部分。PieChart 有子views,但是它从来不测量它们。而是根据他自身的layout法则,直接设置它们的大小。
- View的绘制流程说一下?
- View的绘制流程:OnMeasure()——>OnLayout()——>OnDraw()
- 第一步:OnMeasure():测量视图大小。从顶层父View到子View递归调用measure方法,measure方法又回调OnMeasure。
- 第二步:OnLayout():确定View位置,进行页面布局。从顶层父View向子View的递归调用view.layout方法的过程,即父View根据上一步measure子View所得到的布局大小和布局参数,将子View放在合适的位置上。
- 第三步:OnDraw():绘制视图。ViewRoot创建一个Canvas对象,然后调用OnDraw()。六个步骤:①、绘制视图的背景;②、保存画布的图层(Layer);③、绘制View的内容;④、绘制View子视图,如果没有就不用;⑤、还原图层(Layer);⑥、绘制滚动条。技术博客大总结
- 自定义View的注意点?
- View需要支持wrap_content、padding
- ViewGroup需要支持子View的margin和自身的padding
- 尽量不要在View中使用Handler,View已经有post系列方法
- View如果有线程或者动画,需要及时停止(onDetachedFromWindow会在View被remove时调用)——避免内存泄露
- View如果有滑动嵌套情形,需要处理好滑动冲突
3.0.0.7 View的wrap_content和match_parent效果一致的原因分析?getDefaultSize方法的处理逻辑?
- View的wrap_content和match_parent效果一致的原因分析?
- 根据View的onMeasure方法中的getDefaultSize方法,我们可以发现在两种模式下,View的测量值等于该View的测量规格MeasureSpec中的尺寸。
- View的MeasureSpec本质是由自身的LayoutParams和父容器的MeasureSpec决定的。
- 当View为wrap_content时,该View的模式为AT_MOST,且尺寸specSize为父容器的剩余空间大小。
- 当View为match_parent时,该View的模式跟随父容器的模式(AT_MOST/EXACTLY), 且尺寸specSize为父容器的剩余空间大小。
- 因此getDefaultSize中无论View是哪种模式,最终测量宽/高均等于尺寸specSize,因此两种属性效果是完全一样的(View的大小充满了父容器的剩余空间)技术博客大总结
- 除非给定View固定的宽/高,View的specSize才会等于该固定值。
- getDefaultSize方法的处理逻辑?
- getDefaultSize: 根据建议获取的最小宽高和测量规格,决定实际的测量宽高
- UNSPECIFIED模式:测量宽高 = 建议的最小宽高
- EXACTLY / AT_MOST模式:测量宽高 = specSize技术博客大总结
- View的getDefaultSize源码要点(决定了View宽高的测量值)
- UNSPECIFIED模式时,宽/高为第一个参数也就是getSuggestedMinimumWidth()获取的建议最小值
- AT_MOST(wrap_content)和EXACTLY(match_parent/具体值dp等)这两个模式下,View宽高的测量值为当前View的MeasureSpec(测量规格)中指定的尺寸specsize
- getDefaultSize: 根据建议获取的最小宽高和测量规格,决定实际的测量宽高
3.0.0.8 ViewGroup(抽象类)的measure流程?getChildMeasureSpec获取子元素MeasureSpec的要点?
- ViewGroup(抽象类)的measure流程?
- ViewGroup没有onMeasure方法,只定义了measureChildren方法(onMeasure根据不同布局难以统一)
- measureChildren中遍历所有子元素并调用measureChild方法
- measureChild方法中会获取子View的MeasureSpec(getChildMeasureSpec),然后调用子元素View的measure方法进行测量
- getChildMeasureSpec获取子元素MeasureSpec的要点?
- 子View的MeasureSpec是根据自身的LayoutParams和父容器SpecMode生成
- 当子View的布局参数为wrap_content,且父容器模式为AT_MOST时,效果与子元素布局为match_parent是一样的。因此当子View的布局参数为wrap_content时,需要给指定默认的宽/高
- LinearLayout的onMeasure()分析
- ViewGroup因为布局的不同,无法统一onMeasure方法,具体内容根据布局的不同而不同,这里直接以LinearLayout进行分析
- onMeasure会根据orientation选择measureVertical或者measureHorizontal进行测量
- measureVertical本质是遍历子元素,并执行子元素的measure方法,并获得子元素的总高度以及子元素在竖直方向上的margin等。技术博客大总结
- 最终LinearLayout会测量自己的大小,在orientation的方向上,如果布局是match_parent或者具体数值,测量过程与View一致(高度为specSize);如果布局是wrap_content,高度是所有子元素高度总和,且不会超过父容器的剩余空间,最终高度需要考虑在竖直方向上的padding
3.0.0.9 View的layout过程?View的layout()源码分析?LinearLayout的onLayout方法?View的测量宽高和最终宽高有什么区别?
- View的layout过程?
- 使用layout方法确定View本身的位置
- layout中调用onLayout方法确定所有子View的位置
- View的layout()源码分析?
- 调用setFrame()设置View四个定点位置(即初始化mLeft,mRight,mTop,mBottom的值)
- 之后调用onLayout确定子View位置,该方法类似于onMeasure,View和ViewGroup中均没有实现,具体实现与具体布局有关。
- LinearLayout的onLayout方法?
- 根据orientation选择调用layoutVertical或者layoutHorizontal
- layoutVertical中会遍历所有子元素并调用setChildFrame(里面直接调用子元素的layout方法)
- 层层传递下去完成了整个View树的layout过程
- setChildFrame中的宽/高实际就是子元素的测量宽/高(getMeasure…后直接传入)
- View的测量宽高和最终宽高有什么区别?技术博客大总结
- 等价于getMeasuredWidth和getWidth有什么区别
- getWidth = mRight - mLeft,结合源码测量值和最终值是完全相等的。
- 区别在于:测量宽高形成于measure过程,最终宽高形成于layout过程(赋值时机不同)
- 也有可能导致两者不一致:强行重写View的layout方法,在传参方面改变最终宽/高(虽然这样毫无实际意义)
- 某些情况下,View需要多次measure才能确定自己的测量宽高,在前几次测量中等到的值可能有最终宽高不一致。但是最终结果上,测量宽高=最终宽高
3.0.1.0 draw的过程步骤是什么?View特殊方法setWillNotDraw是干什么用的?
- draw的过程步骤是什么?
- 绘制背景(drawBackground(canvas))
- 绘制自己(onDraw)
- 绘制children(dispatchDraw)-遍历调用所有子View的draw方法
- 绘制装饰(如onDrawScollBars)
- View特殊方法setWillNotDraw是干什么用的?
- 若一个View不绘制任何内容,需要将该标志置为true,系统会进行相应优化
- 默认View不开启该标志位技术博客大总结
- 默认ViewGroup开启该标志位
- 如果我们自定义控件继承自ViewGroup并且本身不进行绘制时,就可以开启该标志位
- 当该ViewGroup明确通过onDraw绘制内容时,就需要显式关闭WILL_NOT_DRAW标志位。
3.0.1.1 View中x,y,translationX,translationY分别是什么?View平移时是否改变了left、top等原始参数?
- View中x,y,translationX,translationY分别是什么?
- x,y是View当前左上角的坐标
- translationX,translationY是在滑动/动画后,View当前位置和View最原始位置的距离。
- 因此得出等式:x(View左上角当前位置) = left(View左上角初始位置) + translationX(View左上角偏移的距离)
- View平移时是否改变了left、top等原始参数?技术博客大总结
- View平移时top、left等参数不变,改变的是x,y,tranlsationX和tranlsationY
3.0.1.2 MeasureSpec是什么?MeasureSpec的组成?测量模式SpecMode的类型和具体含义?MeasureSpec和LayoutParams的对应关系?
- MeasureSpec是什么?
- MeasureSpec是一种“测量规则”或者“测量说明书”,决定了View的测量过程
- View的MeasureSpec会根据自身的LayoutParamse和父容器的MeasureSpec生成。
- 最终根据View的MeasureSpec测量出View的宽/高(测量时数据并非最终宽高)
- MeasureSpec的组成?
- MeasureSpec代表一个32位int值,高2位是SpecMode,低30位是SpecSize
- SpecMode是指测量模式
- SpecSize是指在某种测量模式下的大小
- 类MesaureSpec提供了用于SpecMode和SpecSize打包和解包的方法
- 测量模式SpecMode的类型和具体含义?技术博客大总结
- UNSPECIFIED:父容器不对View有任何限制,一般用于系统内部
- EXACTLY:精准模式,View的最终大小就是SpecSize指定的值(对应于LayoutParams的match_parent和具体的数值)
- AT_MOST:最大值模式,大小不能大于父容器指定的值SpecSize(对应于wrap_content)
- MeasureSpec和LayoutParams的对应关系?
- View的MeasureSpec是需要通过自身的LayoutParams和父容器的MeasureSpec一起才能决定
- DecorView(顶级View)是例外,其本身MeasureSpec由窗口尺寸和自身LayoutParams共同决定
- MeasureSpec一旦确定,onMeasure中就可以确定View的测量宽/高
3.0.1.3 如何获取View的测量宽/高?如何在Activity启动时获得View的宽/高?Activity中获得View宽高的4种办法?
- 如何获取View的测量宽/高?
- 在measure完成后,可以通过getMeasuredWidth/Height()方法,就能获得View的测量宽高
- 在一定极端情况下,系统需要多次measure,因此得到的值可能不准确,最好的办法是在onLayout方法中获得测量宽/高或者最终宽/高
- 如何在Activity启动时获得View的宽/高?
- Activity的生命周期与View的measure不是同步运行,因此在onCreate/onStart/onResume均无法正确得到
- 若在View没有测量好时,去获得宽高,会导致最终结果为0
- 有四种办法去正确获得宽高
- Activity中获得View宽高的4种办法?技术博客大总结
- onWindowFocusChanged
- View已经初始化完毕,可以获得宽高;Activity得到焦点和失去焦点均会调用一次(频繁onResume和onPause会导致频繁调用)
- view.post(runnable)
- 通过post将一个runnable投递到消息队列尾部;等到Looper调用次runnable时,View已经完成初始化
- ViewTreeObserver
- 使用ViewTreeObserver的接口,可以在View树状态改变或者View树内部View的可见性改变时,onGlobalLayout会被回调;能正确获取View宽/高
- view.measure
- onWindowFocusChanged
3.0.1.4 Activity启动到最终加载ViewRoot(执行三大流程)的流程是什么?
- Activity启动到最终加载ViewRoot(执行三大流程)的流程是什么?
- Activity调用startActivity方法,最终会调用ActivityThread的handleLaunchActivity方法
- handleLaunchActivity会调用performLauchActivity方法(会调用Activity的onCreate,并完成DecorView的创建)和handleResumeActivity方法
- handleResumeActivity方法会做四件事:performResumeActivity(调用activity的onResume方法)、getDecorView(获取DecorView)、getWindowManager(获取WindowManager)、WindowManager.addView(decor, 1)
- WindowManager.addView(decor, 1)本质是调用WindowManagerGlobal的addView方法。其中主要做两件事:1、创建ViewRootImpl实例 2、root.setView(decor, ….)将DecorView作为参数添加到ViewRoot中,这样就将DecorView加载到了Window中
- ViewRootImpl还有一个方法performTraveals方法,用于让ViewTree开始View的工作流程:其中会调用performMeasure/Layout/Draw()三个方法,分别对应于View的三大流程。
3.0.1.5 自定义View性能优化有哪些?针对异常销毁,自定义View如何优化?如何避免创建大量对象?
- 自定义View性能优化有哪些?
- 避免过度绘制
- 尽量减少或简化计算
- 避免创建大量对象造成频繁GC
- 禁止或避免I/O操作
- onDraw中避免冗余代码、避免创建对象
- 复合View,要减少布局层级。
- 状态和恢复和保存
- 开启硬件加速
- 合理使用invalidate的参数版本。
- 减少冗余代码:不要使用Handler,因为已经有post系列方法.
- 使用的线程和动画,要在onDetachedFromWindow中进行清理工作。
- 要妥善处理滑动冲突。
- 避免过度绘制
- 像素点能画一次就不要多次绘制,以及绘制看不到的背景
- 开发者选项里内的工具,只对xml布局有效果,看不到自定义View的过度绘制,仍然需要注意。
- 尽量减少或简化计算
- 不要做无用计算。尽可能的复用计算结果。技术博客大总结
- 没有数据,或者数据较少的时候应如何处理,没有事件需要响应的时候如何处理。
- 应该避免在for或while循环中做计算。比如:去计算屏幕宽度等信息。
- 避免创建大量对象造成频繁GC
- 应该避免在for或while循环中new对象。这是减少内存占用量的有效方法。
- 禁止或避免I/O操作
- I/O操作对性能损耗极大,不要在自定义View中做IO操作。
- onDraw中避免冗余代码、避免创建对象
- onDraw中禁止new对象.如:不应该在ondraw中创建Paint对象。Paint类提供了reset方法。可以在初始化View时创建对象。
- 要避免冗余代码,提高效率。
- 复合View,要减少布局层级。
- 复合控件:继承自现有的LinearLayout等ViewGroup,然后组合多个控件来实现效果。这种实现方法要注意减少布局层级,层级越高性能越差。
- 状态和恢复和保存
- Activity还会因为内存不足或者旋转屏幕而导致重建Activity,自定义View也要去进行自我状态的保存和读取。
- 在onSaveInstanceState()保存状态;在onRestoreInstanceState()恢复状态
- 开启硬件加速
- 合理使用invalidate的参数版本。技术博客大总结
- 避免任何请款下之际调用默认参数的invalidate
- 调用有参数的invalidate进行局部和子View刷新,能够提高性能。
- 减少冗余代码:不要使用Handler,因为已经有post系列方法
- View已经有post系列方法,没有必要重复去写。
- 可以直接使用,最终会投递到主线程的Handler中
- 使用的线程和动画,要在onDetachedFromWindow中进行清理工作。
- View如果有线程或者动画,需要及时停止.
- View的onDetachedFromWindow会在View被remove时调用,在该方法内进行终止
- 这样能避免内存泄露
- 要妥善处理滑动冲突。
- View如果有滑动嵌套情形,需要处理好滑动冲突
关于其他内容介绍
01.关于博客汇总链接
02.关于我的博客
- 我的个人站点:www.yczbj.org, www.ycbjie.cn
- github:https://github.com/yangchong211
- 知乎:https://www.zhihu.com/people/yczbj/activities
- 简书:http://www.jianshu.com/u/b7b2c6ed9284
- csdn:http://my.csdn.net/m0_37700275
- 喜马拉雅听书:http://www.ximalaya.com/zhubo/71989305/
- 开源中国:https://my.oschina.net/zbj1618/blog
- 泡在网上的日子:http://www.jcodecraeer.com/member/content_list.php?channelid=1
- 邮箱:yangchong211@163.com
- 阿里云博客:https://yq.aliyun.com/users/article?spm=5176.100- 239.headeruserinfo.3.dT4bcV
- segmentfault头条:https://segmentfault.com/u/xiangjianyu/articles
- 掘金:https://juejin.im/user/5939433efe88c2006afa0c6e