Android Layout inflate分析(2) - ViewGroup
2016-02-03 13:17
507 查看
UI控件
ViewGroup
ViewGroup实现了ViewManager和ViewParent两个接口。@UiThread public abstract class ViewGroup extends View implements ViewParent, ViewManager {
ViewManager
public interface ViewManager { public void addView(View view, ViewGroup.LayoutParams params); public void updateViewLayout(View view, ViewGroup.LayoutParams params); public void removeView(View view); }
在inflate的过程中,主要用到的是构造和addView。第一个View参数不用说了,要加入的子View。另外一个重要的参数是ViewGroup.LayoutParams. 这个参数的主要用途是指定子View的位置。
ViewGroup.LayoutParams
ViewGroup.LayoutParams的基本属性
作为一个基本类,它的主要作用是指定子View的宽和高。除了直接指定大小之外,它还接受两个值:MATCH_PARENT(老的名字叫FILL_PARENT)和WRAP_CONTENT. 这大家都太熟悉了,就不多说了。
下面抽象一下,常量和宽高,是我们熟悉的部分。
6770 public static class LayoutParams { ... 6777 @SuppressWarnings({"UnusedDeclaration"}) 6778 @Deprecated 6779 public static final int FILL_PARENT = -1; ... 6786 public static final int MATCH_PARENT = -1; ... 6793 public static final int WRAP_CONTENT = -2; ... 6804 public int width; 6815 public int height;
下面是布局动画的,先放在这里,用到再说。
/** * Used to animate layouts. */ public LayoutAnimationController.AnimationParameters layoutAnimationParameters;
ViewGroup.LayoutParams的构造方法
别看下面都是又是主题,又是绕来绕去的高大上方法。本质上,ViewGroup.LayoutParams就是宽和高两个域。这两个值赋正确了,其它的就都不用管。值可以是具体的pixel值,也可以是MATCH_PARENT或者WRAP_CONTENT两个常量。我们把代码中的几个构造方法的顺序调整一下,先看说人话的。
第一个是最正宗的赋值型构造,两个值一赋就OK。
public LayoutParams(int width, int height) { this.width = width; this.height = height; }
再看下拷贝构造方法:
/** * Copy constructor. Clones the width and height values of the source. * * @param source The layout params to copy from. */ public LayoutParams(LayoutParams source) { this.width = source.width; this.height = source.height; }
然后再看说文言的,这个得转几道手,看几个其它类的方法:
6840 public LayoutParams(Context c, AttributeSet attrs) { 6841 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout); 6842 setBaseAttributes(a, 6843 R.styleable.ViewGroup_Layout_layout_width, 6844 R.styleable.ViewGroup_Layout_layout_height); 6845 a.recycle(); 6846 }
首先来看这个Context.obtainStyledAttributes,从主题中读取值。先获取当前上下文的主题,然后调用主题类的obtainStyledAttributes.
530 public final TypedArray obtainStyledAttributes( 531 AttributeSet set, @StyleableRes int[] attrs) { 532 return getTheme().obtainStyledAttributes(set, attrs, 0, 0); 533 }
我们移步/frameworks/base/core/java/android/content/res/Resources.java,看看Theme中的obtainStyledAttributes的实现,我们删节一下,一共也没几句逻辑:
1593 public TypedArray obtainStyledAttributes(AttributeSet set, 1594 @StyleableRes int[] attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) { 1595 final int len = attrs.length; 1596 final TypedArray array = TypedArray.obtain(Resources.this, len); 1597 ... 1602 final XmlBlock.Parser parser = (XmlBlock.Parser)set; 1603 AssetManager.applyStyle(mTheme, defStyleAttr, defStyleRes, 1604 parser != null ? parser.mParseState : 0, attrs, array.mData, array.mIndices); 1605 1606 array.mTheme = this; 1607 array.mXml = parser; ... 1638 return array; 1639 }
然后我们转战TypedArray.obtain:
43 static TypedArray obtain(Resources res, int len) { 44 final TypedArray attrs = res.mTypedArrayPool.acquire(); 45 if (attrs != null) { 46 attrs.mLength = len; 47 attrs.mRecycled = false; 48 49 final int fullLen = len * AssetManager.STYLE_NUM_ENTRIES; 50 if (attrs.mData.length >= fullLen) { 51 return attrs; 52 } 53 54 attrs.mData = new int[fullLen]; 55 attrs.mIndices = new int[1 + len]; 56 return attrs; 57 } 58 59 return new TypedArray(res, 60 new int[len*AssetManager.STYLE_NUM_ENTRIES], 61 new int[1+len], len); 62 }
得到了TypedArray结果之后,再通过setBaseAttributes将值设置好。上面已经反复强调了,在ViewGroup.LayoutParams一共就只有宽和高两个参数,不管怎么复杂地折腾,最终落实的一定是这两个值。
6881 /** 6882 * Extracts the layout parameters from the supplied attributes. 6883 * 6884 * @param a the style attributes to extract the parameters from 6885 * @param widthAttr the identifier of the width attribute 6886 * @param heightAttr the identifier of the height attribute 6887 */ 6888 protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) { 6889 width = a.getLayoutDimension(widthAttr, "layout_width"); 6890 height = a.getLayoutDimension(heightAttr, "layout_height"); 6891 }
MarginLayoutParams
ViewGroup.LayoutParams只有宽和高两个参数,简单是极简了。下面我们给它周围加个白边。一共6个变量,上下左右4个边距,加上起始和结束2个边距。6969 public static class MarginLayoutParams extends ViewGroup.LayoutParams { 6970 /** 6971 * The left margin in pixels of the child. Margin values should be positive. 6972 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value 6973 * to this field. 6974 */ 6975 @ViewDebug.ExportedProperty(category = "layout") 6976 public int leftMargin; 6977 6978 /** 6979 * The top margin in pixels of the child. Margin values should be positive. 6980 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value 6981 * to this field. 6982 */ 6983 @ViewDebug.ExportedProperty(category = "layout") 6984 public int topMargin; 6985 6986 /** 6987 * The right margin in pixels of the child. Margin values should be positive. 6988 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value 6989 * to this field. 6990 */ 6991 @ViewDebug.ExportedProperty(category = "layout") 6992 public int rightMargin; 6993 6994 /** 6995 * The bottom margin in pixels of the child. Margin values should be positive. 6996 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value 6997 * to this field. 6998 */ 6999 @ViewDebug.ExportedProperty(category = "layout") 7000 public int bottomMargin; 7001 7002 /** 7003 * The start margin in pixels of the child. Margin values should be positive. 7004 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value 7005 * to this field. 7006 */ 7007 @ViewDebug.ExportedProperty(category = "layout") 7008 private int startMargin = DEFAULT_MARGIN_RELATIVE; 7009 7010 /** 7011 * The end margin in pixels of the child. Margin values should be positive. 7012 * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value 7013 * to this field. 7014 */ 7015 @ViewDebug.ExportedProperty(category = "layout") 7016 private int endMargin = DEFAULT_MARGIN_RELATIVE;
ViewGroup的构造
前三个都是陪太子读书的,一共是4个参数,前三个是给1个参数,2个参数,3个参数时其它给空参数时的调用。560 public ViewGroup(Context context) { 561 this(context, null); 562 } 563 564 public ViewGroup(Context context, AttributeSet attrs) { 565 this(context, attrs, 0); 566 } 567 568 public ViewGroup(Context context, AttributeSet attrs, int defStyleAttr) { 569 this(context, attrs, defStyleAttr, 0); 570 }
其余就下面这一个,它一共有3步,我们分别分析。
572 public ViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 573 super(context, attrs, defStyleAttr, defStyleRes); 574 initViewGroup(); 575 initFromAttributes(context, attrs, defStyleAttr, defStyleRes); 576 }
initViewGroup
这个好,基本上都是设一些属性582 private void initViewGroup() { 583 // ViewGroup doesn't draw by default 584 if (!debugDraw()) { 585 setFlags(WILL_NOT_DRAW, DRAW_MASK); 586 } 587 mGroupFlags |= FLAG_CLIP_CHILDREN; 588 mGroupFlags |= FLAG_CLIP_TO_PADDING; 589 mGroupFlags |= FLAG_ANIMATION_DONE; 590 mGroupFlags |= FLAG_ANIMATION_CACHE; 591 mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE; 592 593 if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) { 594 mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS; 595 } 596 597 setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS); 598 599 mChildren = new View[ARRAY_INITIAL_CAPACITY]; 600 mChildrenCount = 0; 601 602 mPersistentDrawingCache = PERSISTENT_SCROLLING_CACHE; 603 }
initFromAttributes
605 private void initFromAttributes( 606 Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
又到了我们熟悉的context.obtainStyledAttributes,下面就是分门别类放东西,就不多说了。
607 final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ViewGroup, defStyleAttr, 608 defStyleRes); 609 610 final int N = a.getIndexCount(); 611 for (int i = 0; i < N; i++) { 612 int attr = a.getIndex(i); 613 switch (attr) { 614 case R.styleable.ViewGroup_clipChildren: 615 setClipChildren(a.getBoolean(attr, true)); 616 break; 617 case R.styleable.ViewGroup_clipToPadding: 618 setClipToPadding(a.getBoolean(attr, true)); 619 break; 620 case R.styleable.ViewGroup_animationCache: 621 setAnimationCacheEnabled(a.getBoolean(attr, true)); 622 break; 623 case R.styleable.ViewGroup_persistentDrawingCache: 624 setPersistentDrawingCache(a.getInt(attr, PERSISTENT_SCROLLING_CACHE)); 625 break; 626 case R.styleable.ViewGroup_addStatesFromChildren: 627 setAddStatesFromChildren(a.getBoolean(attr, false)); 628 break; 629 case R.styleable.ViewGroup_alwaysDrawnWithCache: 630 setAlwaysDrawnWithCacheEnabled(a.getBoolean(attr, true)); 631 break; 632 case R.styleable.ViewGroup_layoutAnimation: 633 int id = a.getResourceId(attr, -1); 634 if (id > 0) { 635 setLayoutAnimation(AnimationUtils.loadLayoutAnimation(mContext, id)); 636 } 637 break; 638 case R.styleable.ViewGroup_descendantFocusability: 639 setDescendantFocusability(DESCENDANT_FOCUSABILITY_FLAGS[a.getInt(attr, 0)]); 640 break; 641 case R.styleable.ViewGroup_splitMotionEvents: 642 setMotionEventSplittingEnabled(a.getBoolean(attr, false)); 643 break; 644 case R.styleable.ViewGroup_animateLayoutChanges: 645 boolean animateLayoutChanges = a.getBoolean(attr, false); 646 if (animateLayoutChanges) { 647 setLayoutTransition(new LayoutTransition()); 648 } 649 break; 650 case R.styleable.ViewGroup_layoutMode: 651 setLayoutMode(a.getInt(attr, LAYOUT_MODE_UNDEFINED)); 652 break; 653 case R.styleable.ViewGroup_transitionGroup: 654 setTransitionGroup(a.getBoolean(attr, false)); 655 break; 656 case R.styleable.ViewGroup_touchscreenBlocksFocus: 657 setTouchscreenBlocksFocus(a.getBoolean(attr, false)); 658 break; 659 } 660 } 661 662 a.recycle(); 663 }
ViewGroup的父类 - View类
@UiThread public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {
这个类又是一个要求UiThread的类.
View类的构造方法,就是被ViewGroup类用super调用的那个,我们省略一些细节,看看它的结构:
3897 public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { 3898 this(context); 3899 3900 final TypedArray a = context.obtainStyledAttributes( 3901 attrs, com.android.internal.R.styleable.View, defStyleAttr, defStyleRes); 3902 ... 3948 3949 final int N = a.getIndexCount(); 3950 for (int i = 0; i < N; i++) { 3951 int attr = a.getIndex(i); 3952 switch (attr) { 3953 case com.android.internal.R.styleable.View_background: 3954 background = a.getDrawable(attr); 3955 break; ...
我们把细节省略掉之后,跟ViewGroup刚才我们看到的构造函数已经非常像了。还是获取一个TypedArray对象,然后分门别类处理这些属性。
相关文章推荐
- LinearLayout 动态添加TextView控件
- Android 高手进阶,自己定义圆形进度条
- Android Jni开发之自动化编译javah
- 【既见树木又见森林系列(一)】Android系统的启动过程
- android studio编程时引入外部字体样式
- Android屏幕适配方案:产品级的解决方案
- Android 百度地图 SDK v3.0.0 (一)
- Android Studio安装插件
- 生成及查看数字签名 android.keystore
- Android利用BitMap获得图片像素数据的方法
- 如何对 Android 库进行依赖管理?
- Android Spinner 详解
- android取得所在位置的经纬度
- Android_ListView实现商品倒计时(解决时间错乱问题)
- Android ListView 控件总结
- Android 6.0 MTP(一) 框架
- Android仿百度福袋红包界面
- 给 Android 开发者的 RxJava 详解
- Android Bander设计与实现 - 设计篇
- Android应用开发性能优化完全分析