本文原創, 轉載請註明出處:http://blog.csdn.net/qinjuningphp
在以前一篇博文中<< Android中View繪製流程以及invalidate()等相關方法分析>>,簡單的闡述 了Android View html
繪製流程的三個步驟,即:java
一、 measure過程 --- 測量過程node
二、 layout 過程 --- 佈局過程
三、 draw 過程 --- 繪製過程
要想對Android 中View這塊深刻理解,對這三個步驟地學習是必不可少的 。
今天,我着重講解下以下三個內容:android
一、 measure過程安全
二、WRAP_CONTENT、MATCH_PARENT/FILL_PARENT屬性的原理說明app
三、xml佈局文件解析成View樹的流程分析。框架
但願對你們能有幫助。- - 分析版本基於Android 2.3 。ide
一、WRAP_CONTENT、MATCH_PARENT/FILL_PARENT
初入Android殿堂的同窗們,對這三個屬性必定又愛又恨。愛的是使用起來挺爽地---照葫蘆畫瓢便可,恨的函數
倒是時常混淆這幾個屬性地意義,須要三思然後行。在帶着你們重溫下這幾個屬性的用法吧(但願我沒有囉嗦)。
這三個屬性都用來適應視圖的水平或垂直大小,一個以視圖的內容或尺寸爲基礎的佈局比精確地指定視圖範圍
更加方便。
① fill_parent
設置一個視圖的佈局爲fill_parent將強制性地使視圖擴展至父元素大小。
② match_parent
Android 中match_parent和fill_parent意思同樣,但match_parent更貼切,因而從2.2開始兩個詞均可以
用,但2.3版本後建議使用match_parent。
③ wrap_content
自適應大小,強制性地使視圖擴展以便顯示其所有內容。以TextView和ImageView控件爲例,設置爲
wrap_content將完整顯示其內部的文本和圖像。佈局元素將根據內容更改大小。
可不要重複造輪子,以上摘自<<Android fill_parent、wrap_content和match_parent的區別>>。
固然,咱們能夠設置View的確切寬高,而不是由以上屬性指定。
- android:layout_weight="wrap_content"
- android:layout_weight="match_parent"
- android:layout_weight="fill_parent"
- android:layout_weight="100dip"
接下來,咱們須要轉換下視角,看看ViewGroup.LayoutParams類及其派生類。
二、ViewGroup.LayoutParams類及其派生類
2.一、 ViewGroup.LayoutParams類說明
Android API中以下介紹:
LayoutParams are used by views to tell their parents how they want to be laid out.
意思大概是說: View經過LayoutParams類告訴其父視圖它想要地大小(即,長度和寬度)。
所以,每一個View都包含一個ViewGroup.LayoutParams類或者其派生類,View類依賴於ViewGroup.LayoutParams。
路徑:frameworks\base\core\java\android\view\View.java
- public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {
- ...
-
-
-
-
-
-
-
- protected ViewGroup.LayoutParams mLayoutParams;
- ...
- }
2.二、 ViewGroup.LayoutParams源碼分析
路徑位於:frameworks\base\core\java\android\view\ViewGroup.java
- public abstract class ViewGroup extends View implements ViewParent, ViewManager {
- ...
- public static class LayoutParams {
-
-
-
-
-
-
- @Deprecated
- public static final int FILL_PARENT = -1;
-
-
-
-
-
- public static final int MATCH_PARENT = -1;
-
-
-
-
-
- public static final int WRAP_CONTENT = -2;
-
-
-
-
-
- public int width;
-
-
-
-
-
- public int height;
-
-
-
- public LayoutAnimationController.AnimationParameters layoutAnimationParameters;
-
-
-
-
-
- public LayoutParams(Context c, AttributeSet attrs) {
- TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout);
- setBaseAttributes(a,
- R.styleable.ViewGroup_Layout_layout_width,
- R.styleable.ViewGroup_Layout_layout_height);
- a.recycle();
- }
-
-
-
-
-
- public LayoutParams(int width, int height) {
- this.width = width;
- this.height = height;
- }
-
-
-
-
-
- public LayoutParams(LayoutParams source) {
- this.width = source.width;
- this.height = source.height;
- }
-
-
-
-
- LayoutParams() {
- }
-
-
-
-
-
-
-
- protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
- width = a.getLayoutDimension(widthAttr, "layout_width");
- height = a.getLayoutDimension(heightAttr, "layout_height");
- }
- }
咱們發現FILL_PARENT/MATCH_PARENT值爲 -1 ,WRAP_CONETENT值爲-2,是否是有點詫異? 將值
設置爲負值的目的是爲了區別View的具體值(an exact size) 老是大於0的。
ViewGroup子類能夠實現自定義LayoutParams,自定義LayoutParams提供了更好地擴展性,例如LinearLayout
就有LinearLayout. LayoutParams自定義類(見下文)。整個LayoutParams類家族仍是挺複雜的。
ViewGroup.LayoutParams及其經常使用派生類的類圖(部分類圖)以下:

該類圖是在太龐大了,你們有興趣的去看看Android API吧。
前面咱們說過,每一個View都包含一個ViewGroup.LayoutParams類或者其派生類,下面咱們的疑問是Android框架
中時如何爲View設置其LayoutParams屬性的。
有兩種方法會設置View的LayoutParams屬性:
1、 直接添加子View時,常見於以下幾種方法:ViewGroup.java
-
- void addView(View child, int index)
-
-
- void addView(View child, int width, int height)
-
- void addView(View child, ViewGroup.LayoutParams params)
三個重載方法的區別只是添加View時構造LayoutParams對象的方式不一樣而已,稍後咱們探尋一下它們的源碼。
2、 經過xml佈局文件指定某個View的屬性爲:android:layout_heigth=」」以及android:layout_weight=」」 時。
總的來講,這兩種方式都會設定View的LayoutParams屬性值----指定的或者Default值。
方式1流程分析:
直接添加子View時,比較容易理解,咱們先來看看這種方式設置LayoutParams的過程:
路徑:\frameworks\base\core\java\android\view\ViewGroup.java
- public abstract class ViewGroup extends View implements ViewParent, ViewManager {
- ...
-
-
-
-
-
-
-
-
- public void addView(View child) {
- addView(child, -1);
- }
-
-
-
-
-
-
-
-
-
- public void addView(View child, int index) {
- LayoutParams params = child.getLayoutParams();
- if (params == null) {
- params = generateDefaultLayoutParams();
- if (params == null) {
- throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
- }
- }
- addView(child, index, params);
- }
-
-
-
-
-
-
- public void addView(View child, int width, int height) {
-
- final LayoutParams params = generateDefaultLayoutParams();
- params.width = width;
- params.height = height;
- addView(child, -1, params);
- }
-
-
-
-
-
-
- public void addView(View child, LayoutParams params) {
- addView(child, -1, params);
- }
-
-
-
-
-
-
-
- public void addView(View child, int index, LayoutParams params) {
- ...
-
-
-
- requestLayout();
- invalidate();
- addViewInner(child, index, params, false);
- }
-
-
-
-
-
-
-
- protected LayoutParams generateDefaultLayoutParams() {
-
-
- return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
- }
- private void addViewInner(View child, int index, LayoutParams params,
- boolean preventRequestLayout) {
-
- if (!checkLayoutParams(params)) {
- params = generateLayoutParams(params);
- }
-
- if (preventRequestLayout) {
- child.mLayoutParams = params;
- } else {
- child.setLayoutParams(params);
- }
-
- ...
- }
- ...
- }
主要功能就是在添加子View時爲其構建了一個LayoutParams對象。但更重要的是,ViewGroup的子類能夠重載
上面的幾個方法,返回特定的LayoutParams對象,例如:對於LinearLayout而言,則是LinearLayout.LayoutParams
對象。這麼作地目的是,能在其餘須要它的地方,能夠將其強制轉換成LinearLayout.LayoutParams對象。
LinearLayout重寫函數地實現爲:
- public class LinearLayout extends ViewGroup {
- ...
- @Override
- public LayoutParams generateLayoutParams(AttributeSet attrs) {
- return new LinearLayout.LayoutParams(getContext(), attrs);
- }
- @Override
- protected LayoutParams generateDefaultLayoutParams() {
-
- if (mOrientation == HORIZONTAL) {
- return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
- } else if (mOrientation == VERTICAL) {
- return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
- }
- return null;
- }
- @Override
- protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
- return new LayoutParams(p);
- }
-
-
-
-
-
-
- public static class LayoutParams extends ViewGroup.MarginLayoutParams {
-
-
-
-
-
-
- @ViewDebug.ExportedProperty(category = "layout")
- public float weight;
-
-
-
-
-
- public int gravity = -1;
-
-
-
- public LayoutParams(Context c, AttributeSet attrs) {
- super(c, attrs);
- TypedArray a =c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.LinearLayout_Layout);
- weight = a.getFloat(com.android.internal.R.styleable.LinearLayout_Layout_layout_weight, 0);
- gravity = a.getInt(com.android.internal.R.styleable.LinearLayout_Layout_layout_gravity, -1);
-
- a.recycle();
- }
-
-
-
- public LayoutParams(int width, int height) {
- super(width, height);
- weight = 0;
- }
-
-
-
-
-
-
-
-
-
-
- public LayoutParams(int width, int height, float weight) {
- super(width, height);
- this.weight = weight;
- }
- public LayoutParams(ViewGroup.LayoutParams p) {
- super(p);
- }
- public LayoutParams(MarginLayoutParams source) {
- super(source);
- }
- }
- ...
- }
LinearLayout.LayoutParams類繼承至ViewGroup.MarginLayoutParams類,添加了對android:layout_weight以及
android:layout_gravity這兩個屬性的獲取和保存。並且它的重寫函數返回的都是LinearLayout.LayoutParams
類型。這樣,咱們能夠再對子View進行其餘操做時,能夠將將其強制轉換成LinearLayout.LayoutParams對象進行
使用。
例如,LinearLayout進行measure過程,使用了LinearLayout.LayoutParam對象,有以下代碼:
- public class LinearLayout extends ViewGroup {
- ...
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-
- if (mOrientation == VERTICAL) {
- measureVertical(widthMeasureSpec, heightMeasureSpec);
- } else {
- measureHorizontal(widthMeasureSpec, heightMeasureSpec);
- }
- }
-
-
-
-
-
-
-
-
-
-
-
- void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
- mTotalLength = 0;
- ...
-
- for (int i = 0; i < count; ++i) {
- final View child = getVirtualChildAt(i);
- ...
-
-
-
- LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
- ...
- }
- ...
- }
超類ViewGroup.LayoutParams強制轉換爲了子類LinearLayout.LayoutParams,由於LinearLayout的每一個
」直接「子View的LayoutParams屬性都是LinearLayout.LayoutParams類型,所以能夠安全轉換。
PS : Android 2.3源碼Launcher2中也實現了自定義的LayoutParams類,在IDLE界面的每一個View至少包含以下
信息:所在X方向的單元格索引和高度、所在Y方向的單元格索引和高度等。
路徑: packages\apps\Launcher2\src\com\android\launcher2\CellLayout.java
- public class CellLayout extends ViewGroup {
- ...
- public static class LayoutParams extends ViewGroup.MarginLayoutParams {
-
-
-
- public int cellX;
-
-
-
- public int cellY;
-
-
-
- public int cellHSpan;
-
-
-
- public int cellVSpan;
- ...
- public LayoutParams(Context c, AttributeSet attrs) {
- super(c, attrs);
- cellHSpan = 1;
- cellVSpan = 1;
- }
-
- public LayoutParams(ViewGroup.LayoutParams source) {
- super(source);
- cellHSpan = 1;
- cellVSpan = 1;
- }
-
- public LayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan) {
- super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
- this.cellX = cellX;
- this.cellY = cellY;
- this.cellHSpan = cellHSpan;
- this.cellVSpan = cellVSpan;
- }
- ...
- }
- ...
- }
對該自定義CellLayout.LayoutParams類的使用能夠參考LinearLayout.LayoutParams類,我也再也不贅述了。
方法2流程分析:
使用屬性android:layout_heigth=」」以及android:layout_weight=」」 時,爲某個View設置LayoutParams值。
其實這種賦值方法其實也如同前面那種,只不過它須要一個前期孵化過程---須要利用XML解析將佈局文件
解析成一個完整的View樹,可別小看它了,全部Xxx.xml的佈局文件都須要解析成一個完整的View樹。下面,
咱們就來仔細走這個過程,重點關注以下兩個方面
①、xml佈局是如何解析成View樹的 ;
②、android:layout_heigth=」」和android:layout_weight=」」的解析。
PS: 一直以來,我都想固然android:layout_heigth以及android:layout_weight這兩個屬性的解析過程是在
View.java內部完成的,但當我真正去找尋時,卻一直沒有在View.java類或者ViewGroup.java類找到。直到一位
網友的一次提問,才發現它們的藏身之地。
三、佈局文件解析流程分析
解析佈局文件時,使用的類爲LayoutInflater。 關於該類的使用請參考以下博客:
<android中LayoutInflater的使用 >>
主要有以下API方法:
public View inflate (XmlPullParser parser, ViewGroup root, boolean attachToRoot)
public View inflate (int resource, ViewGroup root)
public View inflate (int resource, ViewGroup root, boolean attachToRoot)
這三個類主要迷惑之處在於地三個參數attachToRoot,便是否將該View樹添加到root中去。具體可看這篇博客:
<<關於inflate的第3個參數>>
固然還有LayoutInflater的inflate()的其餘重載方法,你們能夠自行了解下。
我利用下面的例子給你們走走這個流程 :
- public class MainActivity extends Activity {
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.main);
-
-
- LayoutInflater layoutInflater = (LayoutInflater)getSystemService();
- View root = layoutInflater.inflate(R.layout.main, null);
- }
- }
Step 1、得到LayoutInflater的引用。
路徑:\frameworks\base\core\java\android\app\ContextImpl.java
-
-
-
-
- class ContextImpl extends Context {
- if (WINDOW_SERVICE.equals(name)) {
- return WindowManagerImpl.getDefault();
- } else if (LAYOUT_INFLATER_SERVICE.equals(name)) {
- synchronized (mSync) {
- LayoutInflater inflater = mLayoutInflater;
-
- if (inflater != null) {
- return inflater;
- }
-
- mLayoutInflater = inflater = PolicyManager.makeNewLayoutInflater(getOuterContext());
- return inflater;
- }
- } else if (ACTIVITY_SERVICE.equals(name)) {
- return getActivityManager();
- }...
- }
繼續去PolicyManager查詢對應函數,看看內部實現。
路徑:frameworks\base\core\java\com\android\internal\policy\PolicyManager.java
- public final class PolicyManager {
- private static final String POLICY_IMPL_CLASS_NAME = "com.android.internal.policy.impl.Policy";
- private static final IPolicy sPolicy;
- static {
-
- try {
- Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
- sPolicy = (IPolicy)policyClass.newInstance();
- }
- ...
- }
- ...
- public static LayoutInflater makeNewLayoutInflater(Context context) {
- return sPolicy.makeNewLayoutInflater(context);
- }
- }
IPolicy接口的實現對爲Policy類。路徑:/frameworks/base/policy/src/com/android/internal/policy/impl/Policy.java
-
-
- public class Policy implements IPolicy{
- ...
- public PhoneLayoutInflater makeNewLayoutInflater(Context context) {
-
- return new PhoneLayoutInflater(context);
- }
- }
-
- public class PhoneLayoutInflater extends LayoutInflater {
- ...
-
-
-
-
-
-
-
-
-
- public PhoneLayoutInflater(Context context) {
- super(context);
- }
- ...
- }
LayoutInflater是個抽象類,實際上咱們返回的是PhoneLayoutInflater類,但解析過程的操做基本上是在
LayoutInflater中完成地。
Step 2、調用inflate()方法去解析佈局文件。
- public abstract class LayoutInflater {
- ...
- public View inflate(int resource, ViewGroup root) {
-
- return inflate(resource, root, root != null);
- }
-
- public View inflate(int resource, ViewGroup root, boolean attachToRoot) {
-
-
- XmlResourceParser parser = getContext().getResources().getLayout(resource);
- try {
- return inflate(parser, root, attachToRoot);
- } finally {
- parser.close();
- }
- }
- }
-
-
-
-
-
-
- public interface XmlResourceParser extends XmlPullParser, AttributeSet {
-
-
-
-
- public void close();
- }
咱們得到了一個當前應用程序環境的XmlResourceParser對象,該對象的主要做用就是來解析xml佈局文件的。
XmlResourceParser類是個接口類,更多關於XML解析的,你們能夠參考下面博客:
<<android之XmlResourceParser類使用實例>>
<<android解析xml文件的方式(其一)>>
<<android解析xml文件的方式(其二)>>
<<android解析xml文件的方式(其三)>>
Step 3 、真正地開始解析工做 。
- public abstract class LayoutInflater {
- ...
-
-
-
-
-
- public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
- synchronized (mConstructorArgs) {
- final AttributeSet attrs = Xml.asAttributeSet(parser);
- Context lastContext = (Context)mConstructorArgs[0];
- mConstructorArgs[0] = mContext;
- View result = root;
-
- try {
-
- int type;
- while ((type = parser.next()) != XmlPullParser.START_TAG &&
- type != XmlPullParser.END_DOCUMENT) {
-
- }
- ...
- final String name = parser.getName();
- if (TAG_MERGE.equals(name)) {
- if (root == null || !attachToRoot) {
- throw new InflateException("<merge /> can be used only with a valid "
- + "ViewGroup root and attachToRoot=true");
- }
-
- rInflate(parser, root, attrs);
- } else {
-
-
- View temp = createViewFromTag(name, attrs);
-
- ViewGroup.LayoutParams params = null;
-
- if (root != null) {
-
-
- params = root.generateLayoutParams(attrs);
- if (!attachToRoot) {
-
-
- temp.setLayoutParams(params);
- }
- }
-
-
- rInflate(parser, temp, attrs);
-
-
-
- if (root != null && attachToRoot) {
- root.addView(temp, params);
- }
-
-
- if (root == null || !attachToRoot) {
- result = temp;
- }
- }
- }
- ...
- return result;
- }
- }
-
-
-
-
- View createViewFromTag(String name, AttributeSet attrs) {
-
- if (name.equals("view")) {
- name = attrs.getAttributeValue(null, "class");
- }
- try {
- View view = (mFactory == null) ? null : mFactory.onCreateView(name,
- mContext, attrs);
-
- if (view == null) {
-
- if (-1 == name.indexOf('.')) {
- view = onCreateView(name, attrs);
- } else {
- view = createView(name, null, attrs);
- }
- }
- return view;
- }
- ...
- }
-
- public final View createView(String name, String prefix, AttributeSet attrs) {
- Constructor constructor = sConstructorMap.get(name);
- Class clazz = null;
-
-
-
-
- try {
- if (constructor == null) {
-
- clazz = mContext.getClassLoader().loadClass(prefix != null ? (prefix + name) : name);
- ...
- constructor = clazz.getConstructor(mConstructorSignature);
- sConstructorMap.put(name, constructor);
- } else {
-
- if (mFilter != null) {
- ...
- }
- }
-
- Object[] args = mConstructorArgs;
- args[1] = attrs;
- return (View) constructor.newInstance(args);
- }
- ...
- }
-
- }
這段代碼的做用是獲取xml佈局文件的root View,作了以下兩件事情
一、獲取xml佈局的View實例,經過createViewFromTag()方法獲取,該方法會判斷節點名是API 控件
仍是自定義控件,繼而調用合適的方法去實例化View。
二、判斷root以及attachToRoot參數,從新設置root View值以及temp變量的LayoutParams值。
若是仔細看着段代碼,不知你們內心有沒有疑惑:當root爲null時,咱們的temp變量的LayoutParams值是爲
null的,即它不會被賦值?有個View的LayoutParams值爲空,那麼,在系統中不會報異常嗎?見下面部分
代碼:
-
- public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
- synchronized (mConstructorArgs) {
- ...
- try {
-
- ...
- if (TAG_MERGE.equals(name)) {
- ...
- } else {
-
-
- View temp = createViewFromTag(name, attrs);
- ViewGroup.LayoutParams params = null;
-
-
- if (root != null) {
-
-
- params = root.generateLayoutParams(attrs);
- if (!attachToRoot) {
-
-
- temp.setLayoutParams(params);
- }
- }
- ...
- }
- }
- ...
- }
- }
關於這個問題的詳細答案,我會在後面講到。這兒我簡單說下,任何View樹的頂層View被添加至窗口時,
通常調用WindowManager.addView()添加至窗口時,在這個方法中去作進一步處理。即便,LayoutParams
值爲空,UI框架每次measure()時都忽略該View的LayoutParams值,而是直接傳遞MeasureSpec值至View樹。
接下來,咱們關注另一個函數,rInflate(),該方法會遞歸調用每一個View下的子節點,以當前View做爲根View
造成一個View樹。
-
-
-
-
-
- private void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs)
- throws XmlPullParserException, IOException {
-
- final int depth = parser.getDepth();
- int type;
-
- while (((type = parser.next()) != XmlPullParser.END_TAG ||
- parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
-
- if (type != XmlPullParser.START_TAG) {
- continue;
- }
- final String name = parser.getName();
-
- if (TAG_REQUEST_FOCUS.equals(name)) {
- parseRequestFocus(parser, parent);
- } else if (TAG_INCLUDE.equals(name)) {
- if (parser.getDepth() == 0) {
- throw new InflateException("<include /> cannot be the root element");
- }
- parseInclude(parser, parent, attrs);
- } else if (TAG_MERGE.equals(name)) {
- throw new InflateException("<merge /> must be the root element");
- } else {
-
- final View view = createViewFromTag(name, attrs);
- final ViewGroup viewGroup = (ViewGroup) parent;
-
- final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
- rInflate(parser, view, attrs);
- viewGroup.addView(view, params);
- }
- }
- parent.onFinishInflate();
- }
值得注意的是,每次addView前都調用了viewGroup.generateLayoutParams(attrs)去構建一個LayoutParams
實例,而後在addView()方法中爲其賦值。參見以下代碼:ViewGroup.java
- public abstract class ViewGroup extends View implements ViewParent, ViewManager {
- ...
-
- public LayoutParams generateLayoutParams(AttributeSet attrs) {
- return new LayoutParams(getContext(), attrs);
- }
- public static class LayoutParams {
- ...
- public LayoutParams(Context c, AttributeSet attrs) {
- TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout);
- setBaseAttributes(a,
- R.styleable.ViewGroup_Layout_layout_width,
- R.styleable.ViewGroup_Layout_layout_height);
- a.recycle();
- }
- protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
- width = a.getLayoutDimension(widthAttr, "layout_width");
- height = a.getLayoutDimension(heightAttr, "layout_height");
- }
-
- }
好吧 ~~ 咱們仍是探尋根底,去TypeArray類的getLayoutDimension()看看。
路徑:/frameworks/base/core/java/android/content/res/TypedArray.java
- public class TypedArray {
- ...
-
-
-
-
-
-
-
-
-
-
-
-
- public int getLayoutDimension(int index, String name) {
- index *= AssetManager.STYLE_NUM_ENTRIES;
- final int[] data = mData;
-
- final int type = data[index+AssetManager.STYLE_TYPE];
- if (type >= TypedValue.TYPE_FIRST_INT
- && type <= TypedValue.TYPE_LAST_INT) {
- return data[index+AssetManager.STYLE_DATA];
- } else if (type == TypedValue.TYPE_DIMENSION) {
- return TypedValue.complexToDimensionPixelSize(
- data[index+AssetManager.STYLE_DATA], mResources.mMetrics);
- }
-
-
- throw new RuntimeException(getPositionDescription()
- + ": You must supply a " + name + " attribute.");
- }
- ...
- }
從上面得知,
咱們將View的AttributeSet屬性傳遞給generateLayoutParams()方法,讓其構建合適地
LayoutParams對象,而且初始化屬性值weight和height。同時咱們也得知 佈局文件中的View包括自定義View
必須加上屬性layout_weight和layout_height,不然會報異常。
Step 3 主要作了以下事情:
首先,得到了了佈局文件地root View,即佈局文件中最頂層的View。
其次,經過遞歸調用,咱們造成了整個View樹以及設置了每一個View的LayoutParams對象。
總結:經過對佈局文件的解析流程的學習,也就是轉換爲View樹的過程,咱們明白瞭解析過程的箇中奧妙,以及
設置ViewLayoutParams對象的過程。可是,咱們這兒只是簡單的浮光掠影,更深層次的內容但願你們能深刻學習。
原本是準備接下去往下寫的,但無奈貼出來的代碼太多,文章有點長並且本身也有點凌亂了,所以決定作兩篇
博客發表吧。下篇內容包括以下方面:
一、MeasureSpec類說明 ;
二、measure過程當中如何正確設置每一個View的長寬 ;
三、UI框架正確設置頂層View的LayoutParams對象,對Activity而言,頂層View則是DecorView,
其餘的皆是普通View了。