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对象,然后分门别类处理这些属性。