在背景图像中使用光学边界时的额外填充

时间:2014-08-11 08:19:42

标签: android android-layout

https://developer.android.com/about/versions/android-4.3.html#UI中引入的光学边界功能似乎为父布局添加了额外的填充设置。 在下面的屏幕截图中,可以看到3个示例片段:

  1. 具有默认设置layoutMode =" clipBounds"
  2. 的片段
  3. 具有layoutMode =" opticalBounds"
  4. 的片段
  5. 具有layoutMode =" opticalBounds"的片段,但背景图片是简单的颜色。
  6. 片段#2在根布局中有一个额外的填充。我的目标是删除那个填充,以便我可以放置& TextView完全基于它的内部光学边界来布局。

    如何删除第二个片段创建的内部填充?我已经尝试将填充设置为0dp或" -11dp"但这不是我希望做的。

    感谢您的任何建议。

    代码:

    片段1:

    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
                 android:orientation="vertical"
                 android:paddingLeft="20dp"
                 android:paddingTop="20dp">
    
        <TextView
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:background="@drawable/shadow_with_stroke_gray"
            android:text="lorem ipsum etc"
            android:padding="20dp"
            />
    </FrameLayout>
    

    片段2:

    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
                 android:orientation="vertical"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
                 android:layoutMode="opticalBounds"
                 android:paddingLeft="20dp"
                 android:paddingTop="20dp">
    
        <TextView
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:background="@drawable/shadow_with_stroke_gray"
            android:text="lorem ipsum etc"
            android:padding="20dp"
            />
    </FrameLayout>
    

    片段3

    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
                 android:orientation="vertical"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
                 android:layoutMode="opticalBounds"
                 android:paddingLeft="20dp"
                 android:paddingTop="20dp">
    
        <TextView
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:background="#ffffcc"
            android:text="lorem ipsum etc"
            android:padding="20dp"
            />
    </FrameLayout>
    

    活动布局

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  xmlns:tools="http://schemas.android.com/tools"
                  android:id="@+id/container"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent"
                  android:padding="40dp">
    
        <fragment
            android:id="@+id/fragment1"
            android:layout_width="312dp"
            android:layout_height="match_parent"
            class="opticalboundstest.WithoutOpticalBoundsFragment"/>
    
        <Space
            android:layout_width="21dp"
            android:layout_height="match_parent"/>
    
        <fragment
            android:id="@+id/fragment2"
            android:layout_width="312dp"
            android:layout_height="match_parent"
            class="opticalboundstest.WithOpticalBoundsFragment"/>
    
        <Space
            android:layout_width="21dp"
            android:layout_height="match_parent"/>
    
        <fragment
            android:id="@+id/fragment3"
            android:layout_width="312dp"
            android:layout_height="match_parent"
            class="opticalboundstest.WithWorkingOpticalBoundsFragment"/>
    </LinearLayout>
    

    Simulator screenshot

    正在使用的阴影图片:

    Shadow 9-Patch image

2 个答案:

答案 0 :(得分:0)

  

片段#2在根布局中有一个额外的填充

实际上,它不是填充。蓝线和红线之间的间隙称为光学边界(或光学插入物)。 Here你可以看到:

  

所有布局元素也尊重其子视图的光学边界,根据其中视图的光学边界调整自己的边界。

查看ViewGroup中的computeOpticalInsets()

Insets computeOpticalInsets() {
    if (isLayoutModeOptical()) {
        int left = 0;
        int top = 0;
        int right = 0;
        int bottom = 0;
        for (int i = 0; i < mChildrenCount; i++) {
            View child = getChildAt(i);
            if (child.getVisibility() == VISIBLE) {
                Insets insets = child.getOpticalInsets();
                left =   Math.max(left,   insets.left);
                top =    Math.max(top,    insets.top);
                right =  Math.max(right,  insets.right);
                bottom = Math.max(bottom, insets.bottom);
            }
        }
        return Insets.of(left, top, right, bottom);
    } else {
        return Insets.NONE;
    }
}

因此,父母的光学界限是他孩子最大的光学范围。

  

我的目标是删除那个填充,以便我可以放置&amp;完全基于它的内部光学边界来布局TextView。

您不能完全基于它的内部光学边界放置TextView,因为它的父级可以有其他具有光学边界的子级。 你为什么想要消除这个差距?它对用户不可见

答案 1 :(得分:0)

一种可能的方法是使用Java反射完全禁用Insets。

/**
 * Helper class for the optical bounds layout mode.
 */
public class Bounds {
    private static final String TAG = Bounds.class.getName();

    /**
     * Disables insets for a given {@link android.view.View} or {@link android.view.ViewGroup}.
     * This method should be called during the construction phase of the <code>target</code> object.
     *
     * @param target Target object to disable the layout insets.
     * @return Whether the operation succeeded.
     */
    public static boolean disableLayoutInsets(final View target) {
        try {
            final Field mLayoutInsets = View.class.getDeclaredField("mLayoutInsets");
            mLayoutInsets.setAccessible(true);

            final Class c = Class.forName("android.graphics.Insets");
            final Field f = c.getDeclaredField("NONE");

            mLayoutInsets.set(target, f.get(target));
        } catch (NoSuchFieldException | ClassNotFoundException | IllegalAccessException e) {
            Log.e(TAG, "Exception", e);
            return false;
        }

        return true;
    }
}