Home > database >  Android Row: How to make first and third views' widths equal to the widest of them, with the mi
Android Row: How to make first and third views' widths equal to the widest of them, with the mi

Time:11-05

I am designing a title row that has the item title in the middle, with a status description on the left and expiration date description on the right. The title should be centered at all times and expand width until it runs into either of the side views, at which time it should get ellipsized. I'm having trouble figuring out how to keep the title view centered while allowing for the two side views to have different sizes, while also trying to show as much of the text as possible in cases where the side views have only two or three characters, or as many as 20.

This is NOT a basic question of having the center view fill the space. The cases will outline the additional requirements I have that make it more tricky.

I tried two main approaches:

  1. LinearLayout

I started with a horizontal LinearLayout. The first and third TextViews had widths of WRAP_CONTENT while the center title view had a width of 0dp and weight of 1, allowing it to fill the middle section of the row. The issue here was that the first and third views wouldn't have the same width with WRAP_CONTENT, so the title view would be shifted one way or the other. See here how the title is shifted to the left towards the smaller text:

WRAP_CONTENT on sides with title filling

After that, I assigned a weightSum of 20 to the LinearLayout, gave weights of 4 to the first and third views, a weight of 12 to the middle view, and width of 0dp for all 3. This worked better but wouldn't allow for longer text in the end views, even when there was plenty of room in the row for the view to expand:

Using layout_weight and 0dp width for all 3 views

Changing the views to use layout_weight and widths of WRAP_CONTENT didn't work either. Weight and WRAP_CONTENT

  1. ConstraintLayout

I switch the row to a ConstraintLayout to try that as well. I started this route with the end views using WRAP_CONTENT as their width and the title view using 0dp. The title view had its start linked to the end of the first view, and its end linked to the start of the last view. Of course, this would shift the title view towards whichever end text was shorter. Changing the end views to a width of 0dp yielded the same result.

ConstraintLayout with sides on WRAP_CONTENT and center of 0dp

From here I got closer. I linked the middle view's start and end to the parent LinearLayout so that it stretched the entire width. Then I linked the first view's end to the middle view's start, and the last view's start to the middle view's end, and set both of their widths to 0dp. By setting the middle view's width to 0dp and adding a marginStart and marginEnd of 64dp, I allowed some space for the first and last views to be shown. Still, this was like the LinearLayout solution with weights, as the gaps on each side of the middle view was of the fixed 64dp size, and didn't allow for expansion.

ConstraintLayout with middle view width of 0dp

My last effort was so close! Changing the middle view's width to WRAP_CONTENT, and start and end both linked to the parent, allowed the end views to be of equal widths, and fully expanded. That is, UNTIL the title view became very long. With a width of WRAP_CONTENT, the middle view would push the end views off the screen with a very long text, even with a min_width set on the end views.

ConstraintLayout with middle view width of WRAP_CONTENT

Changing the end views to have widths of WRAP_CONTENT didn't work either.

ConstraintLayout with all views WRAP_CONTENT

What I want to accomplish is most like this last try, except that I want the middle view to stop expanding and become ellipsized instead of pushing the end views out. Any other ideas to try?

CodePudding user response:

The problem is if you want the first and third views equal width I don't think it is possible with just standard xml layouts as it's too late once a view has been sized, it won't be resized again without extra code.

Something like TableLayout has the extra code to resize all the rows to match, one it's sized them individually.

But you can to this yourself programmatically

e.g

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        LinearLayout mainLayout = findViewById(R.id.content);

        // Wait until mainLayout has it's size calculated
        ViewTreeObserver.OnGlobalLayoutListener mGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {

                // Don't listen anymore
                mainLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);       

                TextView col1 = new TextView(getApplicationContext());
                TextView col2 = new TextView(getApplicationContext());
                TextView col3 = new TextView(getApplicationContext());

                col1.setText("Much Long");
                col1.setBackgroundColor(Color.CYAN);
                col3.setText("Long");
                col3.setBackgroundColor(Color.CYAN);
                col3.setGravity(Gravity.RIGHT);

                col2.setText("Really Really Really Really Really Really Really Really Really Long");
                col2.setGravity(Gravity.CENTER_HORIZONTAL);
                col2.setSingleLine(true);
                // Set it to have Ellipsizes if too long
                TextUtils.TruncateAt truncate = TextUtils.TruncateAt.END;
                col2.setEllipsize(truncate);

                int measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); // As big as the view wants to be
                // Measure all the views
                col1.measure(measureSpec, measureSpec);
                col2.measure(measureSpec, measureSpec);
                col3.measure(measureSpec, measureSpec);


                // work which side is bigger
                int maxWidth = Math.max(col1.getMeasuredWidth(), col3.getMeasuredWidth());
                int parentWidth = mainLayout.getWidth();
                // Probably should have some checks to make sure centre width does not go negative
                int centreWidth = parentWidth - (2 * maxWidth);

                col1.setWidth(maxWidth);
                col2.setWidth(centreWidth);
                col3.setWidth(maxWidth);

                mainLayout.addView(col1);
                mainLayout.addView(col2);
                mainLayout.addView(col3);

                }
        };

        mainLayout.getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener);
    }
}

Produces some examples

Long Left Short Right Short

The centre of the middle column is always at the centre of the screen

  • Related