zoukankan      html  css  js  c++  java
  • android : framelayout 研究

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

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

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

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

    onMeasure
     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大小确定是一样的。

      

    onLayout
     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的。


  • 相关阅读:
    Js使用WScript.Shell对象执行.bat文件和cmd命令
    wscript运行js文件
    Linux基础tree命令
    使用libssh2库实现支持密码参数的ssh2客户端
    zlib库剖析(1):实现概览
    Linux设置编译器环境变量
    开源的zip_unzip库
    黑客入门之单机游戏外挂
    linux定时任务的设置 crontab 配置指南
    Linux crontab定时执行任务 命令格式与详细例子
  • 原文地址:https://www.cnblogs.com/slider/p/2302413.html
Copyright © 2011-2022 走看看