MATCH_PARENT if sibling view is larger, WRAP_CONTE

2020-08-12 01:31发布

问题:

I have two views in a layout. I'll call them View A and View B respectively.

┌──────┐
│┌─┐┌─┐│
││A││B││
│└─┘└─┘│
└──────┘

The height of parent layout (which includes View A and View B) is WRAP_CONTENT.

Here, the height of View B is WRAP_CONTENT. That is, its height can be changed with respect to its content.

What I want to do is

  1. Set View A's height to View B's height if View A's content is shorter than View B's content.
  2. Set View A's height to its own content's height if View A's content is taller than View B's content.

So,

① If content of View B is taller, then View A's height is set to View B's height.

       ┌──────┐      ┌──────┐
       │┌─┐┌─┐│      │┌─┐┌─┐│
       ││ ││ ││      ││A││ ││
I want ││A││B││, not │└─┘│B││.
       ││ ││ ││      │   │ ││
       │└─┘└─┘│      │   └─┘│
       └──────┘      └──────┘

② If content of View B is shorter, then View A's height is View A's own conent's height.

       ┌──────┐      ┌──────┐
       │┌─┐┌─┐│      │┌─┐┌─┐│
       ││ ││B││      ││A││B││
I want ││A│└─┘│, not │└─┘└─┘│.
       ││ │   │      └──────┘
       │└─┘   │
       └──────┘

If the parent is LinearLayout (Horizontal), setting View A's height to WRAP_CONTENT violates case 1, and setting View A's height to MATCH_PARENT violates case 2.

If the parent is RelativeLayout, setting View A to align both its parent's top and bottom violates RelativeLayout's condition: Note that you cannot have a circular dependency between the size of the RelativeLayout and the position of its children. For example, you cannot have a RelativeLayout whose height is set to WRAP_CONTENT and a child set to ALIGN_PARENT_BOTTOM.

How can I solve this problem?

回答1:

There's different ways to go about this, for example by creating your own custom ViewGroup, probably based on a horizontal LinearLayout. However, I recon that the most straightforward solution would be to dynamically set the requirements at runtime.

Consider the following layout with just two TextViews in a horizontal LinearLayout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/container"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:background="#f00"
    android:padding="10dp" >

    <TextView
        android:id="@+id/first"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#0f0"
        android:padding="5dp"
        android:text="One line" />

    <TextView
        android:id="@+id/second"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#00f"
        android:padding="5dp"
        android:text="Two\nlines"
        android:textColor="#fff" />

</LinearLayout>

Both wrap their height, which basically matches up to your first requirement.

Now requirement 2: for demonstration purposes, I've broken the second TextView's text up into two lines, in order to make it taller than the first one. To make the first TextView match it's height, all you have to do is this:

LinearLayout container = (LinearLayout) findViewById(R.id.container);
final TextView first = (TextView) findViewById(R.id.first);
final TextView second = (TextView) findViewById(R.id.second);

container.post(new Runnable() {
    @Override public void run() {
        int h1 = first.getMeasuredHeight();
        int h2 = second.getMeasuredHeight();
        if (h1 < h2) first.getLayoutParams().height = h2;

    }
});

The 'trick' is to post to the view's queue, ensuring the logic will not get executed until the children have been measured and have a valid height. For requirement 2, the height of the first view will only have to be changed if the second view is taller, which is exactly what the part in the run() method does.



回答2:

You need to set the dimensions of the views programmatically using layout params. Assuming that you are using a LinearLayout to hold the views this is what you would do:

//the views are called a and b
LinearLayout.LayoutParams aParams = a.getLayoutParams();
LinearLayout.LayoutParams bParams = b.getLayoutParams();
int aWidth = aParams.width;
int aHeight = aParams.height;
int bWidth = bParams.width;
int bHeight = bParams.height;
if (aHeight >= bHeight) {
    // do nothing
}
else {
    a.setLayoutParams(new LinearLayout.LayoutParams(aWidth, bHeight));
}

When you set their heights originally keep them as wrap_content in the xml, prior to this modification.



回答3:

This can be solved very easily, from just the axml.

Keep the parent layout's height as wrap_content and set every child view's height to be match_parent. This will cause the parent layout's height to wrap to the highest child, and every child view's height to stretch to the parents.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="horizontal"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content">
    <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="text" />
    <TextView
      android:layout_width="wrap_content"
      android:layout_height="match_parent"
      android:text="More\ntext" />
</LinearLayout>