androidview重绘
㈠ View绘制流程(一)
最近在学习 View 的绘制流程,看了几篇不错的博客( ViewRootImpl的独白,我不是一个View(布局篇) 、 Android应用层View绘制流程与源码分析 )自己对照源码,梳理了一遍。
在Activity的onResume之后,当前Activity的Window对象中的View会被添加在WindowManager中。
整个View树的绘图流程是在 ViewRootImpl 类的 performTraversals() 方法(这个方法巨长)开始的,该方法做的执行过程主要是根据之前设置的状态,判断是否重新计算视图大小 (measure) 、是否重新放置视图的位置 (layout) 、以及是否重绘 (draw) ,其核心也就是通过判断来选择顺序执行这三个方法。
ViewRootImpl 调用 performMeasure 执行Window对应的View的测量。
int widthMeasureSpec :他由两部分组成, 高2位表示MODE ,定义在MeasureSpec类(View的内部类)中,有三种类型, MeasureSpec.EXACTLY 表示确定大小, MeasureSpec.AT_MOST 表示最大大小, MeasureSpec.UNSPECIFIED 不确定。 低30位表示size ,也就是父View的大小。对于系统Window类的DecorVIew对象Mode一般都为MeasureSpec.EXACTLY ,而size分别对应屏幕宽高。对于子View来说大小是由父View和子View共同决定的。
默认的尺寸大小即传入的参数都是通过 getDefaultSize 返回的,我们就看一下该方法的实现。
到此一次最基础的元素View的 measure 过程就完成了。
View实际是嵌套的,而且measure是递归传递的,所以每个View都需要 measure ,能够嵌套的View都是ViewGroup的子类,所以在ViewGroup中定义了 measureChildren , measureChild , measureChildWithMargins 方法来对子视图进行测量, measureChildren 内部实质只是循环调用 measureChild , measureChild 和 measureChildWithMargins 的区别就是是否把 margin 和 padding 也作为子视图的大小。 ViewGroup 本身不调用 measureChildWithMargins 和 measureChildren 方法,由继承类通过for循环调用此方法进行子View的测量。下面看一下ViewGroup中稍微复杂的 measureChildWithMargins 方法。
getChildMeasureSpec 的逻辑是通过其父View提供的 MeasureSpec 参数得到 specMode 和 specSize ,然后根据计算出来的 specMode 以及子View的 childDimension (layout_width或layout_height)来计算自身的 measureSpec ,如果其本身包含子视图,则计算出来的 measureSpec 将作为调用其子视图 measure 函数的参数,同时也作为自身调用 setMeasuredDimension 的参数,如果其不包含子视图则默认情况下最终会调用 onMeasure 的默认实现,并最终调用到 setMeasuredDimension 。
在 Activity 的 onResume 之后,当前 Activity 的 Window 对象中的View(DecorView)会被添加在 WindowManager 中。也就是在 ActivityThread 的 handleResumeActivity 方法中调用 wm.addView(decor, l); 将DecorView添加到 WindowManager 中;
WindowManager 继承 ViewManager ,它的实现类为 WindowManagerImpl ,该类中的方法的具体实现是由其代理类 WindowManagerGlobal 实现的;
在它的 addView 方法中会创建 ViewRootImpl 的实例,然后将Window对应的View(DecorView),ViewRootImpl,LayoutParams顺序添加在WindowManager中,最后将Window所对应的View设置给创建的ViewRootImpl,通过 ViewRootImpl 来更新界面并完成Window的添加过程;
设置view调用的是 ViewRootImpl 的 setView 方法,在该方法中调用 requestLayout(); 方法来异步执行view的绘制方法;之后将 Window 添加到屏幕,通过 WMS (跨进程通信)
在 requestLayout 方法中最终会调用 ViewRootImpl 的 performTraversals(); 方法,该方法做的执行过程主要是根据之前设置的状态,判断是否重新计算视图大小 (measure) 、是否重新放置视图的位置 (layout) 、以及是否重绘 (draw) ,其核心也就是通过判断来选择顺序执行这三个方法: performMeasure 、 performLayout 、 performDraw ;
在 performMeasure 方法中调用的是 View 的 measure 方法,该方法是 final 修饰,不能被子类重写,在该方法中实际调用的是 View 的 onMeasure 方法,子类可以重写 onMeasure 方法来实现自己的测量规则。
View 默认的 onMeasure 方法很简单只是调用了 setMeasuredDimension 方法,该方法的作用是给 View 的成员变量 mMeasuredWidth 和 mMeasuredHeight 赋值,View的测量主要就是给这两个变量赋值,这两个变量一旦赋值,也就意味着测量过程的结束。
setMeasuredDimension 方法传入的尺寸是通过 getDefaultSize(int size, int measureSpec); 方法返回的,在
getDefaultSize 方法中解析 measureSpec 的 Mode 和 Size ,如果Mode为 MeasureSpec.AT_MOST 或者 MeasureSpec.EXACTLY ,最终的size的值为解析后的size;如果 Mode 为 MeasureSpec.UNSPECIFIED ,最终的size为建议的最小值= getSuggestedMinimumWidth ,该方法的具体实现为 return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth()); ,建议的最小宽度和高度都是由View的Background尺寸与通过设置View的 miniXXX 属性共同决定的
measureSpec 是由 getRootMeasureSpec 方法决定的: measureSpec = View.MeasureSpec.makeMeasureSpec(windowSize, View.MeasureSpec.EXACTLY); 根布局的大小是 Window 的大小,Window大小是不能改变的,总是全屏的。
View实际是嵌套的,而且measure是递归传递的,所以每个View都需要measure,能够嵌套的View都是ViewGroup的子类,所以在ViewGroup中定义了 measureChildren , measureChild , measureChildWithMargins 方法来对子视图进行测量, measureChildren 内部实质只是循环调用 measureChild , measureChild 和 measureChildWithMargins 的区别就是是否把 margin 和 padding 也作为子视图的大小。
measureChildWithMargins 方法的作用就是对 父View 提供的 measureSpec 参数结合 子View 的 LayoutParams 参数进行了调整,然后再来调用 child.measure() 方法,具体通过方法 getChildMeasureSpec 方法来进行参数调整。计算出来自身的 measureSpec 作为调用其子视图 measure 方法的参数,同时也作为自身调用 setMeasuredDimension 的参数,如果其不包含子视图则默认情况下最终会调用 onMeasure 的默认实现,并最终调用到 setMeasuredDimension 。
最终决定 View 的 measure 大小是 View 的 setMeasuredDimension 方法,该方法就是设置mMeasuredWidth和mMeasuredHeight的大小,ViewGroup在 onMeasure 方法调用 setMeasuredDimension 之前调整了 measureSpec 。
㈡ android 为什么禁用硬件加速 滑动屏幕会控件会重绘
你好;可能是你用的加速软件不够完善, 建议你使用腾讯手机管家给手机加速,打开腾讯手机管家,进行体检加速; 也可以健康优化给手机加速, 另外垃圾清理,垃圾扫描;也可以给手机加速; 而且它全新视觉界面,创新操作体验.;一键优化,轻松管理你的手机 让你享受一站式手机服务;短信、通讯录、应用等都可以快速添加、删除。
㈢ android 自定义View 怎么重绘
修改颜色后调用invalidate()
在自定义View 中提供一个方法
public void setRectColor(int color){
colors[x][x] = color; //修改颜色
invalidate();
}
㈣ android自定义 view组件重绘问题
new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.post(new Runnable() {
public void run() {
onDraw(null);
invalidate();
}
});
}
}).start();
新线程实现了runnable接口但是 里面的 第一个 run 方法只执行一次就退出了。应该加入循环不断更新。而hander.post 里的那个runnable 外层每循环一次就post一次 所以不用加入循环语句。
boolean flag=true;
new Thread(new runnable{
public void run(){
try{
while(flag){
Thread.sleep(1);
handler.post(new Runnable() {
public void run() {
onDraw(null);
invalidate();
}
});
}catch (InterruptedException e) {
e.printStackTrace();
flag=flase;
}
}
}).start();
㈤ Android 如何判断一个View重绘或加载完成
1、view重绘时回调(即监听函数,当view重绘完成自动动用,需要向view的观察者添加监听器)。格式:
view.getViewTreeObserver().addOnDrawListener(new OnDrawListener() {
@Override
public void onDraw() {
// TODO Auto-generated method stub
}
});
2、view加载完成时回调(当view加载完成自动动用,需要向view的观察者添加监听器)。格式:
view.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
// TODO Auto-generated method stub
}
});
(5)androidview重绘扩展阅读:
两种方式刷新:
1、主线程可以直接调用Invalidate()方法刷新
2、子线程可以直接调用postInvalidate()方法刷新。
API的描述 : Invalidatethe whole view. If the view is visible, onDraw(Canvas) will be called at somepoint in the future. This must be called from a UI thread. To call from anon-UI thread, call postInvalidate().。
API的描述译文:当Invalidate()被调用的时候,View的OnDraw()就会被调用,Invalidate()必须是在UI线程中被调用,如果在新线程中更新视图的就调用postInvalidate()。
㈥ android 自定义View 怎么重绘
修改颜色后调用invalidate()
在自定义View 中提供一个方法
public void setRectColor(int color){
colors[x][x] = color; //修改颜色
invalidate();
}
㈦ android怎么动态调用View.ondraw实现动态绘制自定义View
在自定义的时候,复写该方法,在代码中绘制控件时,会自动调用该方法,在修改了控件,需要重新绘制时,则使用View的invalidate()即可实现重绘!!!
㈧ android canvas view重绘 我在一个activity中调用了canvas。在canvas中我点击事件需要重新绘制当前view
除了SurfaceView,其它的都必须通过调用View.invalidate方法刷新View
所以不能直接执行moren(canvas),需要在onTouchEvent调用invalidate
㈨ 如何让fragment的view重绘
可以在fragment中调用父Activity的recreate()方法重绘父Activity,这样fragment也会被重绘。
代码如下:
java">getActivity().recreate();