android View绘制流程解析
来源:互联网 发布:货物进出库软件 编辑:程序博客网 时间:2024/05/16 14:45
今天探讨下view的绘制流程,我们知道系统一个view控件想在屏幕上显示,一般要经过三个过程,测量,计算大小,绘制,也就说想让控件显示在屏幕上首先要做的是测量也就是onMeasure(),现在我们自定义一个View对象,然后跟到源码里去看看,
MyView.java
public class MyView extends View {public MyView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);// TODO Auto-generated constructor stub}public MyView(Context context, AttributeSet attrs) {super(context, attrs);// TODO Auto-generated constructor stub}public MyView(Context context) {super(context);// TODO Auto-generated constructor stub}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int mode = MeasureSpec.getMode(heightMeasureSpec);int size = MeasureSpec.getSize(heightMeasureSpec);Log.e("MyView","mode="+mode+"size="+size);super.onMeasure(widthMeasureSpec, heightMeasureSpec);}}
View源码中有一个measure方法,也就是测量,源码如下:
public final void measure(int widthMeasureSpec, int heightMeasureSpec) { if ((mPrivateFlags & FORCE_LAYOUT) == FORCE_LAYOUT || widthMeasureSpec != mOldWidthMeasureSpec || heightMeasureSpec != mOldHeightMeasureSpec) { // first clears the measured dimension flag mPrivateFlags &= ~MEASURED_DIMENSION_SET; if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_MEASURE); } // measure ourselves, this should set the measured dimension flag back onMeasure(widthMeasureSpec, heightMeasureSpec); // flag not set, setMeasuredDimension() was not invoked, we raise // an exception to warn the developer if ((mPrivateFlags & MEASURED_DIMENSION_SET) != MEASURED_DIMENSION_SET) { throw new IllegalStateException("onMeasure() did not set the" + " measured dimension by calling" + " setMeasuredDimension()"); } mPrivateFlags |= LAYOUT_REQUIRED; } mOldWidthMeasureSpec = widthMeasureSpec; mOldHeightMeasureSpec = heightMeasureSpec; }
不过发现measure这个方法被final修饰了,表示子类不能重写,但是我们仔细看源码发现其实真正测量方法是在onMeasure()方法中,那我们可以重写这个方法来实现我们要实现的效果,这就是为什么在MyView中重写了onMeasure()方法,
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); }上面是onMeasure()方法,最终还是通过setMeasuredDimension()方法实现其作用,但是分析到这里好像和我们想要知道的结果没关系,对吧,别急,现在看我们MyView中重写的onMeasure()方法代码,方法中有2个形参,int widthMeasureSpec, int heightMeasureSpec,这2个参数是父控件传递过来的,现在看一段代码
int mode = MeasureSpec.getMode(heightMeasureSpec);
我们进入源码看下这个方法怎么实现的,
public static int makeMeasureSpec(int size, int mode) { return size + mode; }我们发现其实宽度和高度并不是单一的由size决定,我们知道size就是控件的宽或者高,那么mode是什么呢?我们找找在哪定义了这个变量,发现并没有在源码中找到,因为这时我们传递进来的,那就没办法知道这个mode是什么了么?办法还是有的,可以查看makeMeasureSpec这个方法的解析,把鼠标放在这个方法上,按f2,我截图看看
MeasureSpec的值由specSize和specMode共同组成的,其中specSize记录的是大小,specMode记录的是规格
我们看到mode有三个值,定义在MeasureSpec类中,现在解释下这三个变量,
EXACTLY:
表示父视图希望子视图的大小应该是由specSize的值来决定的,系统默认会按照这个规则来设置子视图的大小,开发人员当然也可以按照自己的意愿设置成任意的大小
AT_MOST:
表示子视图最多只能是specSize中指定的大小,开发人员应该尽可能小得去设置这个视图,并且保证不会超过specSize。系统默认会按照这个规则来设置子视图的大小,开发人员当然也可以按照自己的意愿设置成任意的大小
UNSPECIFIED:
表示开发人员可以将视图按照自己的意愿设置成任意的大小,没有任何限制。这种情况比较少见,不太会用到
那到源码中找到MeasureSpec类,看看它三个变量,因为一般这样的值都是固定的,
public static class MeasureSpec { private static final int MODE_SHIFT = 30; private static final int MODE_MASK = 0x3 << MODE_SHIFT; /** * Measure specification mode: The parent has not imposed any constraint * on the child. It can be whatever size it wants. */ public static final int UNSPECIFIED = 0 << MODE_SHIFT; /** * Measure specification mode: The parent has determined an exact size * for the child. The child is going to be given those bounds regardless * of how big it wants to be. */ public static final int EXACTLY = 1 << MODE_SHIFT; /** * Measure specification mode: The child can be as large as it wants up * to the specified size. */ public static final int AT_MOST = 2 << MODE_SHIFT; /** * Creates a measure specification based on the supplied size and mode. * * The mode must always be one of the following: * <ul> * <li>{@link android.view.View.MeasureSpec#UNSPECIFIED}</li> * <li>{@link android.view.View.MeasureSpec#EXACTLY}</li> * <li>{@link android.view.View.MeasureSpec#AT_MOST}</li> * </ul> * * @param size the size of the measure specification * @param mode the mode of the measure specification * @return the measure specification based on size and mode */ public static int makeMeasureSpec(int size, int mode) { return size + mode; } /** * Extracts the mode from the supplied measure specification. * * @param measureSpec the measure specification to extract the mode from * @return {@link android.view.View.MeasureSpec#UNSPECIFIED}, * {@link android.view.View.MeasureSpec#AT_MOST} or * {@link android.view.View.MeasureSpec#EXACTLY} */ public static int getMode(int measureSpec) { return (measureSpec & MODE_MASK); } /** * Extracts the size from the supplied measure specification. * * @param measureSpec the measure specification to extract the size from * @return the size in pixels defined in the supplied measure specification */ public static int getSize(int measureSpec) { return (measureSpec & ~MODE_MASK); } /** * Returns a String representation of the specified measure * specification. * * @param measureSpec the measure specification to convert to a String * @return a String with the following format: "MeasureSpec: MODE SIZE" */ public static String toString(int measureSpec) { int mode = getMode(measureSpec); int size = getSize(measureSpec); StringBuilder sb = new StringBuilder("MeasureSpec: "); if (mode == UNSPECIFIED) sb.append("UNSPECIFIED "); else if (mode == EXACTLY) sb.append("EXACTLY "); else if (mode == AT_MOST) sb.append("AT_MOST "); else sb.append(mode).append(" "); sb.append(size); return sb.toString(); } }
这个类是View类中的一个内部类,发现其对应得值为0,1,2
那现在打印出MyView中onMeasure()中的mode是多少,因为源码中值都是向左移动了30,因此在获取mode后要向右移动30,这样的
int mode = MeasureSpec.getMode(heightMeasureSpec)>>>30;
这个mode值才和MeasureSpec类中定义的三个静态变量值有一个是相同的,也就是mode在0,1,2三个中其中一个了,
结果是1,那就明白了
view的测量就分析到此,
- android View绘制流程解析
- android view绘制流程解析
- Android View 绘制流程(Draw) 完全解析
- Android View 绘制流程(Draw) 完全解析
- Android View绘制流程与源码解析
- Android中View的绘制流程解析
- Android View绘制流程
- Android View绘制流程
- Android View绘制流程
- Android View绘制流程
- Android View绘制流程
- Android View绘制流程
- android View 绘制流程
- Android View绘制流程
- Android View绘制流程
- Android View绘制流程
- Android View绘制流程
- Android View绘制流程
- python问题:IndentationError:expected an indented block错误解决
- XML在C#中的应用(六)
- java关键字之assert
- sql的增,删,查,改
- 动态创建进度条对话框
- android View绘制流程解析
- nvl() 函数运行机制,优化案例
- 使用缓存Memcache存储更新微信access token
- MySQL使用information_schema获取锁表信息
- 大数据时代--维克托.迈尔——施恩伯格。。。笔记
- Android第一课程内容
- misc_register、 register_chrdev 的区别总结
- 第十一周项目六:1000内的回文数
- 如何使用android-support-v7-appcompat