android布局调整父级而不调整子级的大小

时间:2014-05-25 14:27:27

标签: android android-layout width android-animation

TLDR版本:

当我调整父级的大小时:

enter image description here

说明:

在Android应用程序中,我有一个父布局,里面有多个相对布局。

<LinearLayout
    android:id="@+id/parent"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:gravity="center_vertical"
    android:orientation="horizontal" >

    <RelativeLayout
        android:id="@+id/child1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@color/transparent">

     </RelativeLayout>

    <RelativeLayout
        android:id="@+id/child2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@color/transparent">

     </RelativeLayout>
</LinearLayout>

此布局的子项中包含一些文本和图片。我正在做的是以下内容:

当用户点击按钮时,我想折叠父线性布局(动画宽度直到它为0)。我已经实现了这个功能:

public static void collapseHorizontally(final View v) {
    final int initialWidth = v.getMeasuredWidth();

    Animation a = new Animation()
    {
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            if(interpolatedTime == 1){
                v.setVisibility(View.GONE);
            }else{
                v.getLayoutParams().width = initialWidth - (int)(initialWidth * interpolatedTime);
                v.requestLayout();
            }
        }

        @Override
        public boolean willChangeBounds() {
            return true;
        }
    };

    // 1dp/ms
    long speed = (int)(initialWidth / v.getContext().getResources().getDisplayMetrics().density);
    a.setDuration(speed);
    v.startAnimation(a);
}

我在父视图上调用上面的函数。 这工作正常,父视图的宽度调整为0,然后我将其可见性设置为已消失。

我的问题

当父视图重新调整大小(折叠)时,子视图也会随之调整大小。我的意思是,当父元素的宽度到达其中一个子元素时,子元素也将使用它重新调整大小,这将导致其中的文本视图也重新调整大小。

以下是一些示例的截图:

enter image description here

在这里你可以看到初始布局,绿色的父布局和子布局(带有日期和文字和图标)。

然后,当我开始减小父布局的大小时,子布局大小也会受到影响,它将减小其中textview的大小,导致单词换行,如下面的两个图像所示: / p>

enter image description here enter image description here

正如您所注意到,当父级的宽度减小时,子项中的文本被包装。

有没有办法可以让子元素不用父视图调整大小,但保持不变,即使父视图大小正在减少?我需要chil元素的大小在整个大小调整动画期间保持固定,并且不会被父级调整,而是用它调整大小。

非常感谢您的帮助

4 个答案:

答案 0 :(得分:8)

您的问题来自几个问题。

  • 首先singleLine="false"上的TextViews参数&gt; 使用此参数,您无法要求它包装其内容,因为文本可以使用多行,因此不具有适当的宽度。如果有多行,则会填充其父级。
  • 第二:&#34; wrap_content&#34;的行为是否适合内容大小,除非它大于父级。在这种情况下,它将再次匹配父级大小。 (我们可以看到,从填充或边距移动,边界遵循父级大小。)

第二个问题可以通过使用固定大小来解决,但第一个问题并非如此:

您可以设置singleLine="true"android:width="wrap_content"以查看它在单行上运行。 (设置android:ellipsize="none"以隐藏&#39; ...&#39;四处移动) 但单行文字不是你需要的吗?

我已经使用TextView的扩展进行了测试,以避免这些视图调整大小,即使有多行:

public class NoResizeTextView extends TextView {

    int firstWidth = -1;
    int firstHeight = -1;

    public NoResizeTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        Layout layout = getLayout();
        if (layout != null) {

            if (firstWidth == -1)
                firstWidth = getMeasuredWidth();
            if (firstHeight == -1)
                firstHeight = getMeasuredHeight();

            setMeasuredDimension(firstWidth, firstHeight);
        }
    }

}

然后在布局中使用此TextView扩展:

       <com.guian.collapsetest.NoResizeTextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:ellipsize="none"
                android:singleLine="false"
                android:text="Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit" >
        </com.guian.collapsetest.NoResizeTextView>

那不是很美,而且我不建议过多地使用它,因为它可能会破坏Android的布局机制。但在你的特定情况下,我猜它确实起到了作用。

逻辑:

扩展视图以创建自定义视图时,您负责onMeasure的实施。这就是它的大小给你的看法。以及它是如何工作的:你计算一次大小(或者让默认函数为你做到而不是getMeasuredW/H)并保存它,然后在每个大小请求上,你只需返回相同的值所以你的视图大小不会改变。

限制:

正确的视图大小不会发生变化,在更改屏幕方向/父级大小时会有不良行为,或者当文本内部发生变化时(如Vikram所说)。 这就是我所说的&#34;打破了android布局机制&#34;。 如果需要动态更改文本,则必须扩展setText方法以允许它在此时调整大小...

答案 1 :(得分:1)

<Edit>支持要求(请参阅评论):

据我所知,以下是你所追求的。支持您的要求所需的一个变化是使用FrameLayout作为父容器。

enter image description here

现在的布局是:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fl"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white" >

    <RelativeLayout
        android:id="@+id/rl1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/holo_blue_bright" >

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
            android:textColor="@android:color/black"
            android:layout_marginTop="100dp" />

    </RelativeLayout>

    <RelativeLayout
        android:id="@+id/rl2"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/holo_blue_dark" >

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
            android:textColor="@android:color/black"
            android:layout_alignParentBottom="true"
            android:layout_marginBottom="200dp" />

    </RelativeLayout>

    <Button
        android:id="@+id/bClickToAnimate"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Click to Resize" />

</FrameLayout>

Java代码:

FrameLayout fl;
RelativeLayout rl1;
RelativeLayout rl2;
Button b;
boolean isOverlapping;
int toTranslate;

@Override
protected void onCreate(Bundle savedInstanceState) {
    ....

    rl1.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

        @Override
        public void onGlobalLayout() {

           rl2.getViewTreeObserver().removeOnGlobalLayoutListener(this);

            // Get width of the viewgroup
            int width = rl1.getWidth();

            // We will keep the second viewgroup outside bounds - we need
            // FrameLayout for this     
            FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) rl2.getLayoutParams();
            lp.leftMargin = width;
            lp.width = width;

            rl2.setLayoutParams(lp);

            // Amount to translate
            toTranslate = width;

        }
    });

    b.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {

            // Viewgroup `rl2` is currently not overlapping
            // Translate `rl2` over `rl1`
            if (!isOverlapping) {
                ObjectAnimator oa = ObjectAnimator.ofFloat(rl2, 
                                               "translationX", 0, -toTranslate);
                oa.setDuration(2000L);
                oa.setInterpolator(new AccelerateInterpolator(2.0f));
                oa.start();
            } else {

                // Viewgroup `rl2` is currently overlapping
                // Translate `rl2` off `rl1`
                ObjectAnimator oa = ObjectAnimator.ofFloat(rl2, 
                                               "translationX", -toTranslate, 0);
                oa.setDuration(2000L);
                oa.setInterpolator(new AccelerateInterpolator(2.0f));
                oa.start();
            }
        }
    });
}

</Edit>

嗯,实际上这很简单。在这里,看看:

您只需要以下内容:

// Animate property `right` - final width is zero
ObjectAnimator oa = ObjectAnimator.ofInt(YOUR_VIEWGROUP, "right", 
                                            YOUR_VIEWGROUP.getWidth(), 0);

// Set animation duration - 5 seconds
oa.setDuration(5000L);

// Set interpolator
oa.setInterpolator(new AccelerateInterpolator(2.0f));

// Start animation
oa.start();

如果您的应用minSdkVersion低于11,请使用NineOldAndroids库来支持&gt; api 8。

我使用的是一个相当简单的布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="@android:color/white" >

    <Button
        android:id="@+id/bClickToAnimate"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Click to Resize" />

    <RelativeLayout
        android:id="@+id/rlToAnimate"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/holo_blue_bright" >

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Lorem ipsum dolor sit amet, consectetur 
                              adipisicing elit, sed do eiusmod tempor 
                              incididunt ut labore et dolore magna aliqua."
            android:textColor="@android:color/black"
            android:layout_centerVertical="true" />

    </RelativeLayout>

</LinearLayout>

正在设置动画的视图组为rlToAnimate

答案 2 :(得分:0)

你可以使用一个ImageView作为布局上方的叠加层,所以相反缩小布局,你只是用带有黑色背景的ImageView覆盖布局,它将从左边滑动,之后完成你可以设置每个消失了。

  1. 让ImageView与匹配父attr和可见性消失。
  2. 当用户点击该按钮时,将ImageView visibilty设置为可见,并设置为从左侧滑入的动画。
  3. OnAnimationEnd将视图设置为继续使用ImageView和其他布局,或者根据需要调整布局大小。

答案 3 :(得分:0)

我在过去处理过相同的要求,这就是我解决它的方法,我相信这是实现它的最简单最灵活的方式。这是经过测试并使用您的xml和动画代码。 两个简单的步骤应该可以解决问题:

  1. 使用FrameLayout环绕您的父布局 - 这将是您实际调整大小的布局。
  2. 在进行任何实际的大小调整之前,我们需要通过为其提供固定大小(以像素为单位)来强制R.id.parent布局保持恒定大小。在运行时,在布局传递之后,我们更新layoutParams以设置固定大小。我们在布局传递后执行此操作,因此我们知道我们设置的固定大小是基于系统对所需大小的计算(即在完成所有匹配父/包内容布局计算之后)。
  3. 以下是布局的外观(仅添加了FrameLayout):

    <FrameLayout
    android:id="@+id/parentContainer"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    
    <LinearLayout
        android:id="@+id/parent"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#0f0"
        android:gravity="center_vertical"
        android:orientation="horizontal" >
    
        <RelativeLayout
            android:id="@+id/child1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@color/transparent">
    
        </RelativeLayout>
    
        <RelativeLayout
            android:id="@+id/child2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@color/transparent">
    
        </RelativeLayout>
    </LinearLayout>
    

    现在你需要在活动的onCreate方法中添加几行代码(如果根据你的代码更有意义,可以在其他地方添加。但可能是onCreate):

    final View fixedSizeLayout = findViewById(R.id.parent);
    
        // Here we take the requested calculated size of the layout (as calculated by the layout pass)
        // and apply it as a fixed size to the layout
        fixedSizeLayout.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
    
            @Override
            public void onGlobalLayout() {
                if (fixedSizeLayout.getWidth() > 0) {
                    LayoutParams params = fixedSizeLayout.getLayoutParams();
                    // this will be different on the first time because the layout width will be a constant (for match_parent) and not actual size
                    if (params.width != fixedSizeLayout.getWidth()) {
                        // Setting a fixed size in px
                        params.width = fixedSizeLayout.getWidth();
                        fixedSizeLayout.setLayoutParams(params);
                        fixedSizeLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                    }
                }
            }
        });
    

    请注意,您要调整大小的视图是FrameLayout(R.id.parentContainer) - 虽然此视图随动画变小,但是布局R.id.parent将保持其常量大小。