dacwe
dacwe

Reputation: 43504

Layout overflow components

I have a problem when my strings in my TextViews grow and overflows, sometimes hiding the text of another TextView or starting to disappear.

I would like it to work like this (I know this looks a lot like swings FlowLayout but not quite since it's alignment isn't fixed, one component is left and the other right aligned):

 -------------------------------------
|[TextView0]               [TextView1]|
 -------------------------------------

And when the text grows:

    TextView0 grows:                        TextView1 grows:
 -------------------------------------    -------------------------------------
|[TextView0 TextView0 TextView0]      |  |[TextView0]                          |
|                          [TextView1]|  |      [TextView1 TextView1 TextView1]|
 -------------------------------------    -------------------------------------

I have tried ReleativeLayout and LinearLayout but never got it to work:

  1. RelativeLayout:

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" >
    
        <TextView
            android:id="@+id/TextView0"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:text="TextView0" />
    
        <TextView
            android:id="@+id/TextView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:text="TextView1" />
    </RelativeLayout>
    

    When the text the start to overlap which I tried to fix with TextView0 being toLeftOf="TextView1". But in that case TextView0 disappears.

  2. LinearLayout:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >
    
        <TextView
            android:id="@+id/TextView0"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="TextView0" />
    
        <TextView
            android:id="@+id/TextView1"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:gravity="right"
            android:text="TextView1" />
    </LinearLayout>
    

    Here the TextViews start to interfere and when TextView0 gets the full width TextView1 disappears (its width goes towards zero).

Upvotes: 1

Views: 2911

Answers (3)

dacwe
dacwe

Reputation: 43504

I made an attempt to create a layout for this purpose.

It works like I discuss in the original question but feel free to update the answer if you make it better!

public class KeyValueLayout extends ViewGroup {

    public KeyValueLayout(Context context) {
        super(context);
    }

    public KeyValueLayout(Context context, AttributeSet attributeSet) {
        super(context, attributeSet);
    }

    public KeyValueLayout(Context context, AttributeSet attributeSet, 
                          int defStyle) {
        super(context, attributeSet, defStyle);
    }

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

        int paddRight = getPaddingRight();
        int paddLeft = getPaddingLeft();
        int paddBottom = getPaddingBottom();
        int paddWidth = paddRight + paddLeft;

        int sizeWidth  = MeasureSpec.getSize(widthMeasureSpec) - paddWidth;
        int sizeHeight = MeasureSpec.getSize(heightMeasureSpec) - paddWidth;

        int modeWidth  = MeasureSpec.getMode(widthMeasureSpec);
        int modeHeight = MeasureSpec.getMode(heightMeasureSpec);

        int modeW = modeWidth == MeasureSpec.EXACTLY 
                        ? MeasureSpec.AT_MOST : modeWidth;
        int modeH = modeHeight == MeasureSpec.EXACTLY 
                        ? MeasureSpec.AT_MOST : modeHeight;

        // where the next view should be placed
        int lineYPosition = 0;

        for (int i = 0; i < getChildCount(); ) {

            View key = getChildAt(i++);

            key.measure(
                    MeasureSpec.makeMeasureSpec(sizeWidth, modeW),
                    MeasureSpec.makeMeasureSpec(sizeHeight, modeH)
            );

            LayoutParams keyLp = (LayoutParams) key.getLayoutParams();
            keyLp.setPosition(paddLeft, lineYPosition);

            if (i >= getChildCount()) 
                break;

            View val = getChildAt(i++);

            val.measure(
                    MeasureSpec.makeMeasureSpec(sizeWidth, modeW),
                    MeasureSpec.makeMeasureSpec(sizeHeight, modeH)
            );

            LayoutParams valLp = (LayoutParams) val.getLayoutParams();

            int valXPosition = paddLeft + sizeWidth - val.getMeasuredWidth();

            // check if both fit on the same line
            if (key.getMeasuredWidth() + val.getMeasuredWidth() <= sizeWidth) {

                valLp.setPosition(valXPosition, lineYPosition);

                lineYPosition += Math.max(key.getMeasuredHeight(), 
                                          val.getMeasuredHeight());
            } else {

                // not enough room, make some space 
                lineYPosition += key.getMeasuredHeight();

                valLp.setPosition(valXPosition, lineYPosition);

                lineYPosition += val.getMeasuredHeight();
            }
        }

        int controlMaxLength = sizeWidth + paddRight; 
        int controlMaxThickness = lineYPosition + paddBottom;

        int resolvedWidth = resolveSize(controlMaxLength, widthMeasureSpec);
        int resolvedHeight =resolveSize(controlMaxThickness,heightMeasureSpec);
        this.setMeasuredDimension(resolvedWidth, resolvedHeight);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        final int count = getChildCount();
        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);
            LayoutParams lp = (LayoutParams) child.getLayoutParams();
            child.layout(lp.x, lp.y, 
                         lp.x + child.getMeasuredWidth(), 
                         lp.y + child.getMeasuredHeight());
        }
    }

    @Override
    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
        return p instanceof LayoutParams;
    }

    @Override
    protected LayoutParams generateDefaultLayoutParams() {
        return new LayoutParams(LayoutParams.WRAP_CONTENT, 
                                LayoutParams.WRAP_CONTENT);
    }

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attributeSet) {
        return new LayoutParams(getContext(), attributeSet);
    }

    @Override
    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
        return new LayoutParams(p);
    }

    public static class LayoutParams extends ViewGroup.LayoutParams {

        private int x;
        private int y;

        public LayoutParams(Context context, AttributeSet attributeSet) {
            super(context, attributeSet);
        }

        public LayoutParams(int width, int height) {
            super(width, height);
        }

        public LayoutParams(ViewGroup.LayoutParams layoutParams) {
            super(layoutParams);
        }

        public void setPosition(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }
}

Example usage:

<nu.wen.android.layout.KeyValueLayout
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="10dp" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/example_string_long"
        android:textStyle="bold" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/example_string_short" />
</nu.wen.android.layout.KeyValueLayout>

Upvotes: 0

JafarKhQ
JafarKhQ

Reputation: 8734

One of the best lib for that Android-FlowLayout
its simple, easy to use and awesome.

Upvotes: 1

trungdinhtrong
trungdinhtrong

Reputation: 2674

The short answer is "yes" - because it is just software, anything can be done :). However, I don't think you can achieve it with any standard layout.

So my best bet is that you create your own View class and then layout things in whatever way you want. You may want to read this (if you have not) as a starting point:

http://developer.android.com/training/custom-views/create-view.html

Upvotes: 0

Related Questions