android : framelayout 研究

 感觉FrameLayout很神秘,是因为用到它的地方少,一直觉得它鸡肋,原来是自己无知。最近需要实现一些layer的效果,就用到了它。它的用法很简单,这里就不多说了,这里就说说它的原理吧。

FrameLayout类里面没有什么东西,主要说的还是它的自身的布局参数FrameLayout.LayoutParams,布局参数类继承MarginLayoutParams。看名词就知道,就是控制view的外边距的,FrameLayout.LayoutParams本身自己定义的参数只有一个gravity。

好了,既然有了布局参数,那就会有空间的一些属性吧。首先FrameLayout也是一个View,所以他必然有Padding(view的内边距)相关属性。这里我们重点关注它的onMeasure与onLayout:

onMeasure意思就是计算出该ViewGroup的布局大小及孩子View的布局大小。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
protected void onMeasure( int widthMeasureSpec, int heightMeasureSpec) {
         int count = getChildCount();
  
         final boolean measureMatchParentChildren =
                 MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
                 MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
         mMatchParentChildren.clear();
  
         int maxHeight = 0 ;
         int maxWidth = 0 ;
         int childState = 0 ;
  
         for ( int i = 0 ; i < count; i++) {
             final View child = getChildAt(i);
             if (mMeasureAllChildren || child.getVisibility() != GONE) {
                 measureChildWithMargins(child, widthMeasureSpec, 0 , heightMeasureSpec, 0 );
                 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                 maxWidth = Math.max(maxWidth,
                         child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
                 maxHeight = Math.max(maxHeight,
                         child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
                 childState = combineMeasuredStates(childState, child.getMeasuredState());
                 if (measureMatchParentChildren) {
                     if (lp.width == LayoutParams.MATCH_PARENT ||
                             lp.height == LayoutParams.MATCH_PARENT) {
                         mMatchParentChildren.add(child);
                     }
                 }
             }
         }
  
         // Account for padding too
         maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
         maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();
  
         // Check against our minimum height and width
         maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
         maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
  
         // Check against our foreground's minimum height and width
         final Drawable drawable = getForeground();
         if (drawable != null ) {
             maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
             maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
         }
  
         setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
                 resolveSizeAndState(maxHeight, heightMeasureSpec,
                         childState << MEASURED_HEIGHT_STATE_SHIFT));
  
         count = mMatchParentChildren.size();
         if (count > 1 ) {
             for ( int i = 0 ; i < count; i++) {
                 final View child = mMatchParentChildren.get(i);
  
                 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
                 int childWidthMeasureSpec;
                 int childHeightMeasureSpec;
                  
                 if (lp.width == LayoutParams.MATCH_PARENT) {
                     childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth() -
                             getPaddingLeftWithForeground() - getPaddingRightWithForeground() -
                             lp.leftMargin - lp.rightMargin,
                             MeasureSpec.EXACTLY);
                 } else {
                     childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
                             getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
                             lp.leftMargin + lp.rightMargin,
                             lp.width);
                 }
                  
                 if (lp.height == LayoutParams.MATCH_PARENT) {
                     childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() -
                             getPaddingTopWithForeground() - getPaddingBottomWithForeground() -
                             lp.topMargin - lp.bottomMargin,
                             MeasureSpec.EXACTLY);
                 } else {
                     childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
                             getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
                             lp.topMargin + lp.bottomMargin,
                             lp.height);
                 }
  
                 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
             }
         }
     }
简单的说父容器的width就是所有孩子View的width取最大值加View外边距和它的内边距:child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin+getPaddingLeftWithForeground() + getPaddingRightWithForeground();如果ViewGroup有图片,还有跟图片比较取较大值,容器的height也是同样的道理。FrameLayout孩子View的布局大小跟普通View大小确定是一样的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
protected void onLayout( boolean changed, int left, int top, int right, int bottom) {
         final int count = getChildCount();
  
         final int parentLeft = getPaddingLeftWithForeground();
         final int parentRight = right - left - getPaddingRightWithForeground();
  
         final int parentTop = getPaddingTopWithForeground();
         final int parentBottom = bottom - top - getPaddingBottomWithForeground();
  
         mForegroundBoundsChanged = true ;
          
         for ( int i = 0 ; i < count; i++) {
             final View child = getChildAt(i);
             if (child.getVisibility() != GONE) {
                 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
  
                 final int width = child.getMeasuredWidth();
                 final int height = child.getMeasuredHeight();
  
                 int childLeft;
                 int childTop;
  
                 int gravity = lp.gravity;
                 if (gravity == - 1 ) {
                     gravity = DEFAULT_CHILD_GRAVITY;
                 }
  
                 final int layoutDirection = getResolvedLayoutDirection();
                 final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
                 final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
  
                 switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
                     case Gravity.LEFT:
                         childLeft = parentLeft + lp.leftMargin;
                         break ;
                     case Gravity.CENTER_HORIZONTAL:
                         childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
                         lp.leftMargin - lp.rightMargin;
                         break ;
                     case Gravity.RIGHT:
                         childLeft = parentRight - width - lp.rightMargin;
                         break ;
                     default :
                         childLeft = parentLeft + lp.leftMargin;
                 }
  
                 switch (verticalGravity) {
                     case Gravity.TOP:
                         childTop = parentTop + lp.topMargin;
                         break ;
                     case Gravity.CENTER_VERTICAL:
                         childTop = parentTop + (parentBottom - parentTop - height) / 2 +
                         lp.topMargin - lp.bottomMargin;
                         break ;
                     case Gravity.BOTTOM:
                         childTop = parentBottom - height - lp.bottomMargin;
                         break ;
                     default :
                         childTop = parentTop + lp.topMargin;
                 }
  
                 child.layout(childLeft, childTop, childLeft + width, childTop + height);
             }
         }
     }
从代码中可以看出,孩子View左上角坐标的X值就是ViewGroup的左内边距加上view的外边距,Y值同理。

哎,怎么感觉自己写着写着,还把问题说复杂了点,感觉还是看代码能明白。

还有,我在2.2版本的代码上,gravity与margin属性必须同时设置,margin才能发挥作用,如果只有margin属性而没有设置gravity,就没有效果。如果有谁知道,欢迎指教哈。

在上一篇中说了下android里drag and drop 一个View,当时那个imageView放在一个LinearLayout里面,事实上放在FrameLayout同样适用,因为他们的布局参数都继承 MarginLayoutParams类,所以通过margin控制他们的布局位置同样在FrameLayout使用。事实上 GridLayout,LinearLayout,RelativeLayout,FrameLayout的布局参数都是继承 MarginLayoutParams的。

原文:http://www.cnblogs.com/slider/archive/2011/12/30/2302413.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值