android View的绘制过程和获取组件高宽值的三种方法
2015-01-26 17:57
399 查看
view绘制过程中的几个方法的调用顺序
onViewAdded
绘制View的流程是在onCreate中使用setContentView来设置要显示Layout文件或直接创建一个View,当调用setContentView后系统会对这个View进行解析,我们看到系统调用了onViewAdded()方法。
OnFinishInflate
在View.java类中
protectedvoid
onFinishInflate() {
}
View对象和它的所有子对象都用XML填充完之后,调用这个方法。
在系统将子View都添加完之后,就会回调当前视图View中的onFinishInflate方法。
只有解析了这个View我
们才能在这个View容器中获取到对子View的引用,同样因为系统解析完View之后才会调用onFinishInflate方法,所以我们自定义组件时可以onFinishInflate方法中获取指定子View的引用。
一般我们在该函数中获取View的引用还有高度。获取高度的方式有三种,每一种都有一些区别。
1
.
int
w = View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED);
int
h = View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED);
imageView.measure(w,
h);
int
height = imageView.getMeasuredHeight();
int
width = imageView.getMeasuredWidth();
通过这种方式获取的高度不是很准确。
试一:<Linearlayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="305dp"
android:background="@drawable/lauch_page_background">
<LinearLayout/>
如果你的Layout中背景图片的高度小于305dp,我们获取的高度就是305dp。
如果你的Layout中背景图片的高度大于305dp,我们获取的高度就是背景图片的高度。不是实际上屏幕的高度。
(原因还需继续研究)
使用第二种方式就是ok的。
2.
ViewTreeObserver
vto = imageView.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new
OnGlobalLayoutListener() {
@Override
public
void onGlobalLayout() {
imageView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
//我们在每次监听前remove前一次的监听,避免重复监听。
imageView.getHeight();
imageView.getWidth();
}
});
该方法获取的高度就是实际在屏幕上显示的高度。
publicinterface
OnGlobalLayoutListener {
/**
* Callback
method to be invoked when the global layout state or the visibility
of views
within
the view tree changes
全局布局状态或者Views的可见性发生变化时回调该方法。
*/
publicvoid
onGlobalLayout();
}
我们可以看一下这个接口时在什么时候调用的:
publicfinalvoid
dispatchOnGlobalLayout() {
final
CopyOnWriteArray<OnGlobalLayoutListener> listeners =
mOnGlobalLayoutListeners;
if
(listeners != null
&& listeners.size() > 0) {
CopyOnWriteArray.Access<OnGlobalLayoutListener>
access = listeners.start();
try
{
int
count = access.size();
for
(int
i = 0; i < count; i++) {
access.get(i).onGlobalLayout();
}
} finally
{
listeners.end();
}
}
}
这个dispatchOnGlobalLayout()是final类型的,它是在
privatevoid
performTraversals() {
…
performMeasure(childWidthMeasureSpec,
childHeightMeasureSpec);
…
if
(triggerGlobalLayoutListener) {
attachInfo.mRecomputeGlobalAttributes
= false;
attachInfo.mTreeObserver.dispatchOnGlobalLayout();
}
}
3.
ViewTreeObserver
vto = imageView.getViewTreeObserver();
vto.addOnPreDrawListener(new
ViewTreeObserver.OnPreDrawListener() {
public
boolean onPreDraw() {
vto.removeOnPreDrawListener(this);
int
height = imageView.getMeasuredHeight();
int
width = imageView.getMeasuredWidth();
return
true;
}
});
这个方法会被调用多次,初始化的时候会被调用多次,离开界面的时候也会被调用多次。只要界面有变化就会被调用。
publicinterface
OnPreDrawListener {
/**
*
将要画view的时候调用。这个时候所有的View树都已经被测量了并且给了frame
*/
publicboolean
onPreDraw();
}
publicfinalboolean
dispatchOnPreDraw() {
boolean
cancelDraw = false;
final
CopyOnWriteArray<OnPreDrawListener> listeners =
mOnPreDrawListeners;
if
(listeners != null
&& listeners.size() > 0) {
CopyOnWriteArray.Access<OnPreDrawListener>
access = listeners.start();
try
{
int
count = access.size();
for
(int
i = 0; i < count; i++) {
cancelDraw
|= !(access.get(i).onPreDraw());
}
} finally
{
listeners.end();
}
}
return
cancelDraw;
}
它的调用顺序在方法2监听器的后面。
privatevoid
performTraversals() {
…
performMeasure(childWidthMeasureSpec,
childHeightMeasureSpec);
…
if
(triggerGlobalLayoutListener) {
attachInfo.mRecomputeGlobalAttributes
= false;
attachInfo.mTreeObserver.dispatchOnGlobalLayout();
}
…
boolean
cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw()
||
viewVisibility
!= View.VISIBLE;
...
}
通过后两种方法得到组件的高度和宽度比较方便,第一种完全时自己测量组件高和宽,比较麻烦。
onMeasure(int,
int)
调用这个方法确定View对象及其所有子对象的尺寸要求。
ViewRootImpl.java
privatevoid
performTraversals() {
performMeasure(childWidthMeasureSpec,
childHeightMeasureSpec);
...
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
...
performDraw();
}
整个View树的绘图流程是从performTraversals()函数展开的,该函数做的执行过程可概况为:根据之前设置的状态,判断是否需要重新计算视图大小(measure())、是否重新需要安置视图的位置(layout())、以及是否需要重绘(draw())。
privatevoid
performMeasure(int
childWidthMeasureSpec, int
childHeightMeasureSpec) {
long
startMeasure = System.nanoTime();
//Flyme_Added
Trace.traceBegin(Trace.TRACE_TAG_VIEW,
"measure");
try
{
mView.measure(childWidthMeasureSpec,
childHeightMeasureSpec);
} finally
{
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
//Flyme_Added
float
totalMeasure = (float)(System.nanoTime()
- startMeasure) * 0.000001f;
mAttachInfo.mMeasureTime
+= totalMeasure;
}
}
mView(其类型一般为ViewGroup类型)调用measure()方法去计算View树的大小,回调
View/ViewGroup对象的onMeasure()方法。(ViewGroup对象
需要重载onMeasure()方法)
publicfinalvoid
measure(int
widthMeasureSpec, int
heightMeasureSpec) {
...
onMeasure(widthMeasureSpec, heightMeasureSpec);
...
}
回调的过程分为两部分:
1.回调View视图里的onMeasure过程
protectedvoid
onMeasure(int
widthMeasureSpec, int
heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(),
widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(),
heightMeasureSpec));
}
//设置该view的实际宽(mMeasuredWidth)高(mMeasuredHeight)
//
setMeasuredDimension(h , l) ; 该方法必须在onMeasure中调用,否者报异常。
2、如果该View是ViewGroup类型,重写onMeasure()方法。则对它的每个子View进行measure()过程
int
childCount = getChildCount() ;
for(int
i=0 ;i<childCount ;i++){
//2.1、获得每个子View对象引用
View
child = getChildAt(i) ;
//整个measure()过程就是个递归过程
//该方法只是一个过滤器,最后会调用measure()
measureChild(child
, h, i) ;
}
}
protectedvoid
measureChildWithMargins(View child,
child.measure(childWidthMeasureSpec,
childHeightMeasureSpec);
}
//又会调用View中的measure(int
, int);
Measure()的过程传递的布局参数,都封装在MeasureSpec类中,MeasureSpec由大小和模式组成,一共有三种模式:UNSPECIFIED(未指定),父元素部队自元素施加任何束缚,子元素可以得到任意想要的大小;EXACTLY(完全),父元素决定自元素的确切大小,子元素将被限定在给定的边界里而忽略它本身大小;AT_MOST(至多),子元素至多达到指定大小的值。
onLayout(boolean,int,int,int,int)
当View对象给它的所有子对象分配尺寸和位置时调用。
这个函数是一个抽象函数,要求实现ViewGroup的函数必须实现这个函数,这也就是ViewGroup是一个抽象函数的原因。因为各种组件实现的布局方式不一样,而onLayout是必须被重载的函数
ViewRootImpl.java
privatevoid
performTraversals() {
performMeasure(childWidthMeasureSpec,
childHeightMeasureSpec);
...
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
...
performDraw();
}
接着host.layout()开始View树的布局,继而回调给View/ViewGroup类中的layout()方法。具体流程如下
1、
layout方法会设置该View视图位于父视图的坐标轴,int
left, int
top, int
right, int
bottom) (调用setFrame()函数去实现)。
publicvoid
layout(int
l, int
t, int
r, int
b) {
...
onMeasure(mOldWidthMeasureSpec,
mOldHeightMeasureSpec);
…
boolean
changed = isLayoutModeOptical(mParent)
?
setOpticalFrame(l,
t, r, b) : setFrame(l, t, r, b);
onLayout(changed, l, t, r, b);
...
}
接下来回调onLayout()方法;
protectedvoid
onLayout(boolean
changed, int
left, int
top, int
right, int
bottom) {
}
2、如果该View是个ViewGroup类型,需要遍历每个子视图chiildView,调用该子视图的layout()方法去设置它的坐标值。(以RelativeLayout为例)
protectedvoid
onLayout(boolean
changed, int
l, int
t, int
r, int
b) {
finalint
count = getChildCount();
for
(int
i = 0; i < count; i++) {
View
child = getChildAt(i);
if
(child.getVisibility() != GONE)
{
RelativeLayout.LayoutParams
st =
(RelativeLayout.LayoutParams)
child.getLayoutParams();
child.layout(st.mLeft,
st.mTop,
st.mRight,
st.mBottom);
}
}
}
onSizeChanged(int,int,int,int)
当这个View对象的尺寸发生改变时,调用这个方法。
onDraw(Canvas)
从performTraversals()方法开始。
privatevoid
performTraversals() {
performMeasure(childWidthMeasureSpec,
childHeightMeasureSpec);
...
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
...
performDraw();
}
接着调用draw()方法发起绘制该View树,值得注意的是每次发起绘图时,并不会重新绘制每个View树的视图,而只会重新绘制那些“需要重绘”的视图,View类内部变量包含了一个标志位DRAWN,当该视图需要重绘时,就会为该View添加该标志位。
mView.draw()开始绘制,draw()方法实现的功能如下:
1
、绘制该View的背景
2
、为显示渐变框做一些准备操作
3、调用onDraw()方法绘制视图本身
(每个View都需要重载该方法,ViewGroup不需要实现该方法)
4、调用dispatchDraw
()方法绘制子视图(如果该View类型不为ViewGroup,即不包含子视图,不需要重载该方法)值得说明的是,ViewGroup类已经为我们重写了dispatchDraw
()的功能实现,应用程序一般不需要重写该方法,但可以重载父类函数实现具体的功能。
4.1
dispatchDraw()方法内部会遍历每个子视图,调用drawChild()去重新回调每个子视图的draw()方法(注意,这个地方“需要重绘”的视图才会调用draw()方法)。值得说明的是,ViewGroup类已经为我们重写了dispatchDraw()的功能实现,应用程序一般不需要重写该方法,但可以重载父类函数实现具体的功能。
5、绘制滚动条
onViewAdded
绘制View的流程是在onCreate中使用setContentView来设置要显示Layout文件或直接创建一个View,当调用setContentView后系统会对这个View进行解析,我们看到系统调用了onViewAdded()方法。
OnFinishInflate
在View.java类中
protectedvoid
onFinishInflate() {
}
View对象和它的所有子对象都用XML填充完之后,调用这个方法。
在系统将子View都添加完之后,就会回调当前视图View中的onFinishInflate方法。
只有解析了这个View我
们才能在这个View容器中获取到对子View的引用,同样因为系统解析完View之后才会调用onFinishInflate方法,所以我们自定义组件时可以onFinishInflate方法中获取指定子View的引用。
一般我们在该函数中获取View的引用还有高度。获取高度的方式有三种,每一种都有一些区别。
1
.
int
w = View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED);
int
h = View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED);
imageView.measure(w,
h);
int
height = imageView.getMeasuredHeight();
int
width = imageView.getMeasuredWidth();
通过这种方式获取的高度不是很准确。
试一:<Linearlayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="305dp"
android:background="@drawable/lauch_page_background">
<LinearLayout/>
如果你的Layout中背景图片的高度小于305dp,我们获取的高度就是305dp。
如果你的Layout中背景图片的高度大于305dp,我们获取的高度就是背景图片的高度。不是实际上屏幕的高度。
(原因还需继续研究)
使用第二种方式就是ok的。
2.
ViewTreeObserver
vto = imageView.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new
OnGlobalLayoutListener() {
@Override
public
void onGlobalLayout() {
imageView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
//我们在每次监听前remove前一次的监听,避免重复监听。
imageView.getHeight();
imageView.getWidth();
}
});
该方法获取的高度就是实际在屏幕上显示的高度。
publicinterface
OnGlobalLayoutListener {
/**
* Callback
method to be invoked when the global layout state or the visibility
of views
within
the view tree changes
全局布局状态或者Views的可见性发生变化时回调该方法。
*/
publicvoid
onGlobalLayout();
}
我们可以看一下这个接口时在什么时候调用的:
publicfinalvoid
dispatchOnGlobalLayout() {
final
CopyOnWriteArray<OnGlobalLayoutListener> listeners =
mOnGlobalLayoutListeners;
if
(listeners != null
&& listeners.size() > 0) {
CopyOnWriteArray.Access<OnGlobalLayoutListener>
access = listeners.start();
try
{
int
count = access.size();
for
(int
i = 0; i < count; i++) {
access.get(i).onGlobalLayout();
}
} finally
{
listeners.end();
}
}
}
这个dispatchOnGlobalLayout()是final类型的,它是在
privatevoid
performTraversals() {
…
performMeasure(childWidthMeasureSpec,
childHeightMeasureSpec);
…
if
(triggerGlobalLayoutListener) {
attachInfo.mRecomputeGlobalAttributes
= false;
attachInfo.mTreeObserver.dispatchOnGlobalLayout();
}
}
3.
ViewTreeObserver
vto = imageView.getViewTreeObserver();
vto.addOnPreDrawListener(new
ViewTreeObserver.OnPreDrawListener() {
public
boolean onPreDraw() {
vto.removeOnPreDrawListener(this);
int
height = imageView.getMeasuredHeight();
int
width = imageView.getMeasuredWidth();
return
true;
}
});
这个方法会被调用多次,初始化的时候会被调用多次,离开界面的时候也会被调用多次。只要界面有变化就会被调用。
publicinterface
OnPreDrawListener {
/**
*
将要画view的时候调用。这个时候所有的View树都已经被测量了并且给了frame
*/
publicboolean
onPreDraw();
}
publicfinalboolean
dispatchOnPreDraw() {
boolean
cancelDraw = false;
final
CopyOnWriteArray<OnPreDrawListener> listeners =
mOnPreDrawListeners;
if
(listeners != null
&& listeners.size() > 0) {
CopyOnWriteArray.Access<OnPreDrawListener>
access = listeners.start();
try
{
int
count = access.size();
for
(int
i = 0; i < count; i++) {
cancelDraw
|= !(access.get(i).onPreDraw());
}
} finally
{
listeners.end();
}
}
return
cancelDraw;
}
它的调用顺序在方法2监听器的后面。
privatevoid
performTraversals() {
…
performMeasure(childWidthMeasureSpec,
childHeightMeasureSpec);
…
if
(triggerGlobalLayoutListener) {
attachInfo.mRecomputeGlobalAttributes
= false;
attachInfo.mTreeObserver.dispatchOnGlobalLayout();
}
…
boolean
cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw()
||
viewVisibility
!= View.VISIBLE;
...
}
通过后两种方法得到组件的高度和宽度比较方便,第一种完全时自己测量组件高和宽,比较麻烦。
onMeasure(int,
int)
调用这个方法确定View对象及其所有子对象的尺寸要求。
ViewRootImpl.java
privatevoid
performTraversals() {
performMeasure(childWidthMeasureSpec,
childHeightMeasureSpec);
...
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
...
performDraw();
}
整个View树的绘图流程是从performTraversals()函数展开的,该函数做的执行过程可概况为:根据之前设置的状态,判断是否需要重新计算视图大小(measure())、是否重新需要安置视图的位置(layout())、以及是否需要重绘(draw())。
privatevoid
performMeasure(int
childWidthMeasureSpec, int
childHeightMeasureSpec) {
long
startMeasure = System.nanoTime();
//Flyme_Added
Trace.traceBegin(Trace.TRACE_TAG_VIEW,
"measure");
try
{
mView.measure(childWidthMeasureSpec,
childHeightMeasureSpec);
} finally
{
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
//Flyme_Added
float
totalMeasure = (float)(System.nanoTime()
- startMeasure) * 0.000001f;
mAttachInfo.mMeasureTime
+= totalMeasure;
}
}
mView(其类型一般为ViewGroup类型)调用measure()方法去计算View树的大小,回调
View/ViewGroup对象的onMeasure()方法。(ViewGroup对象
需要重载onMeasure()方法)
publicfinalvoid
measure(int
widthMeasureSpec, int
heightMeasureSpec) {
...
onMeasure(widthMeasureSpec, heightMeasureSpec);
...
}
回调的过程分为两部分:
1.回调View视图里的onMeasure过程
protectedvoid
onMeasure(int
widthMeasureSpec, int
heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(),
widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(),
heightMeasureSpec));
}
//设置该view的实际宽(mMeasuredWidth)高(mMeasuredHeight)
//
setMeasuredDimension(h , l) ; 该方法必须在onMeasure中调用,否者报异常。
2、如果该View是ViewGroup类型,重写onMeasure()方法。则对它的每个子View进行measure()过程
int
childCount = getChildCount() ;
for(int
i=0 ;i<childCount ;i++){
//2.1、获得每个子View对象引用
View
child = getChildAt(i) ;
//整个measure()过程就是个递归过程
//该方法只是一个过滤器,最后会调用measure()
measureChild(child
, h, i) ;
}
}
protectedvoid
measureChildWithMargins(View child,
child.measure(childWidthMeasureSpec,
childHeightMeasureSpec);
}
//又会调用View中的measure(int
, int);
Measure()的过程传递的布局参数,都封装在MeasureSpec类中,MeasureSpec由大小和模式组成,一共有三种模式:UNSPECIFIED(未指定),父元素部队自元素施加任何束缚,子元素可以得到任意想要的大小;EXACTLY(完全),父元素决定自元素的确切大小,子元素将被限定在给定的边界里而忽略它本身大小;AT_MOST(至多),子元素至多达到指定大小的值。
onLayout(boolean,int,int,int,int)
当View对象给它的所有子对象分配尺寸和位置时调用。
这个函数是一个抽象函数,要求实现ViewGroup的函数必须实现这个函数,这也就是ViewGroup是一个抽象函数的原因。因为各种组件实现的布局方式不一样,而onLayout是必须被重载的函数
ViewRootImpl.java
privatevoid
performTraversals() {
performMeasure(childWidthMeasureSpec,
childHeightMeasureSpec);
...
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
...
performDraw();
}
接着host.layout()开始View树的布局,继而回调给View/ViewGroup类中的layout()方法。具体流程如下
1、
layout方法会设置该View视图位于父视图的坐标轴,int
left, int
top, int
right, int
bottom) (调用setFrame()函数去实现)。
publicvoid
layout(int
l, int
t, int
r, int
b) {
...
onMeasure(mOldWidthMeasureSpec,
mOldHeightMeasureSpec);
…
boolean
changed = isLayoutModeOptical(mParent)
?
setOpticalFrame(l,
t, r, b) : setFrame(l, t, r, b);
onLayout(changed, l, t, r, b);
...
}
接下来回调onLayout()方法;
protectedvoid
onLayout(boolean
changed, int
left, int
top, int
right, int
bottom) {
}
2、如果该View是个ViewGroup类型,需要遍历每个子视图chiildView,调用该子视图的layout()方法去设置它的坐标值。(以RelativeLayout为例)
protectedvoid
onLayout(boolean
changed, int
l, int
t, int
r, int
b) {
finalint
count = getChildCount();
for
(int
i = 0; i < count; i++) {
View
child = getChildAt(i);
if
(child.getVisibility() != GONE)
{
RelativeLayout.LayoutParams
st =
(RelativeLayout.LayoutParams)
child.getLayoutParams();
child.layout(st.mLeft,
st.mTop,
st.mRight,
st.mBottom);
}
}
}
onSizeChanged(int,int,int,int)
当这个View对象的尺寸发生改变时,调用这个方法。
onDraw(Canvas)
从performTraversals()方法开始。
privatevoid
performTraversals() {
performMeasure(childWidthMeasureSpec,
childHeightMeasureSpec);
...
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
...
performDraw();
}
接着调用draw()方法发起绘制该View树,值得注意的是每次发起绘图时,并不会重新绘制每个View树的视图,而只会重新绘制那些“需要重绘”的视图,View类内部变量包含了一个标志位DRAWN,当该视图需要重绘时,就会为该View添加该标志位。
mView.draw()开始绘制,draw()方法实现的功能如下:
1
、绘制该View的背景
2
、为显示渐变框做一些准备操作
3、调用onDraw()方法绘制视图本身
(每个View都需要重载该方法,ViewGroup不需要实现该方法)
4、调用dispatchDraw
()方法绘制子视图(如果该View类型不为ViewGroup,即不包含子视图,不需要重载该方法)值得说明的是,ViewGroup类已经为我们重写了dispatchDraw
()的功能实现,应用程序一般不需要重写该方法,但可以重载父类函数实现具体的功能。
4.1
dispatchDraw()方法内部会遍历每个子视图,调用drawChild()去重新回调每个子视图的draw()方法(注意,这个地方“需要重绘”的视图才会调用draw()方法)。值得说明的是,ViewGroup类已经为我们重写了dispatchDraw()的功能实现,应用程序一般不需要重写该方法,但可以重载父类函数实现具体的功能。
5、绘制滚动条
相关文章推荐
- Android中View的绘制过程 onMeasure方法简述 附有自定义View例子
- Android的View绘制过程,onMeasure()方法介绍
- Android自定义View绘制的方法及过程(二)
- Android中View的绘制过程 onMeasure和onLayout()方法
- 【Android 应用开发】 自定义组件 宽高适配方法, 手势监听器操作组件, 回调接口维护策略, 绘制方法分析 -- 基于 WheelView 组件分析自定义组件
- Android获取view尺寸的三种方法
- 【Android 应用开发】 自定义组件 宽高适配方法, 手势监听器操作组件, 回调接口维护策略, 绘制方法分析 -- 基于 WheelView 组件分析自定义组件
- Android中View的绘制过程 onMeasure方法简述 附有自定义View例子
- Android中View的绘制过程 onMeasure方法简述 附有自定义View例
- Android中View的绘制过程 onMeasure方法简述
- Android中View的绘制过程 onMeasure方法简述 附有自定义View例子
- Android中View的绘制过程 onMeasure方法
- 【Android 应用开发】 自定义组件 宽高适配方法, 手势监听器操作组件, 回调接口维护策略, 绘制方法分析 -- 基于 WheelView 组件分析自定义组件
- Android中View的绘制过程 onMeasure方法简述 附有自定义View例子
- Android中View的绘制过程 onMeasure方法简述
- Android中View的绘制过程 onMeasure方法简述
- Android中View的绘制过程 onMeasure方法简述 附有自定义View例子
- Android中View的绘制过程 onMeasure方法简述 附有自定义View例子
- 【转】Android中View的绘制过程 onMeasure方法简述 附有自定义View例子
- Android中View的绘制过程 onMeasure方法简述 附有自定义View例子