Expand TextView with wrap_content until the neighbor view reaches the end of the parent

后端 未结 7 653
遇见更好的自我
遇见更好的自我 2020-12-13 14:26

I need to achieve the following layout:

\"enter

I have two TextViews in a rela

相关标签:
7条回答
  • 2020-12-13 14:30

    I have faced this kind of usecase in our app. In our app,there are 3 Views. ImageView | TextView | ImageView. The two ImageViews must be visible in the layout.The TextView should be ellipsized at the end. And I came up with a custom ViewGroup.

    public class PriorityLayoutHorizontal extends ViewGroup
    {
    
        private ArrayList<Integer> mPriorityHighChildren;
    
        public PriorityLayoutHorizontal(Context context)
        {
            this(context, null);
        }
    
        public PriorityLayoutHorizontal(Context context, AttributeSet attrs)
        {
            this(context, attrs, 0);
        }
    
        public PriorityLayoutHorizontal(Context context, AttributeSet attrs, int defStyleAttr)
        {
            super(context, attrs, defStyleAttr);
    
            TypedArray typedArray = null;
            try
            {
                typedArray = context.obtainStyledAttributes(attrs, R.styleable.PriorityLayoutHorizontal, defStyleAttr, 0);
                final int id = typedArray.getResourceId(R.styleable.PriorityLayoutHorizontal_priorityHigh, -1);
                if(id != -1)
                {
                    final int[] highPriorityChildren = getResources().getIntArray(id);
                    setPriorityHigh(highPriorityChildren);
                }
            }
            finally
            {
                if(typedArray != null)
                {
                    typedArray.recycle();
                }
            }
        }
    
        public void setPriorityHigh(int... priorityHigh)
        {
            Integer[] priorityHighInteger = new Integer[priorityHigh.length];
            int i = 0;
            for(int value : priorityHigh)
            {
                priorityHighInteger[i++] = Integer.valueOf(value);
            }
            mPriorityHighChildren = new ArrayList<Integer>(Arrays.asList(priorityHighInteger));
        }
    
        int mMaxHeight = 0;
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
        {
            final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
            int widthUsed = 0;
            int heightUsed = 0;
            final int childCount = getChildCount();
    
            for(int childPosition : mPriorityHighChildren)
            {
                final View childView = getChildAt(childPosition);
                if(childView.getVisibility() != View.GONE)
                {
                    measureChildWithMargins(childView, widthMeasureSpec, widthUsed, heightMeasureSpec, heightUsed);
                    widthUsed += getMeasuredWidthWithMargins(childView);
                    heightUsed = Math.max(getMeasuredHeightWithMargins(childView), heightUsed);
                }
            }
    
            for(int childPosition = 0; childPosition < childCount; childPosition++)
            {
                if(! mPriorityHighChildren.contains(Integer.valueOf(childPosition)))
                {
                    final View childView = getChildAt(childPosition);
                    if(childView.getVisibility() != View.GONE)
                    {
                        measureChildWithMargins(childView, widthMeasureSpec, widthUsed, heightMeasureSpec, heightUsed);
                        widthUsed += getMeasuredWidthWithMargins(childView);
                        heightUsed = Math.max(getMeasuredHeightWithMargins(childView), heightUsed);
                    }
                }
            }
    
            mMaxHeight = heightUsed;
    
            int heightSize = heightUsed + getPaddingTop() + getPaddingBottom();
            setMeasuredDimension(widthSize, heightSize);
        }
    
        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b)
        {
            final int paddingLeft = getPaddingLeft();
            final int paddingTop = getPaddingTop();
    
            int spaceUsed = paddingLeft;
            for (int childPosition = 0; childPosition < getChildCount(); childPosition++)
            {
                final View childView = getChildAt(childPosition);
                if(childView.getVisibility() != View.GONE)
                {
                    final int top = (mMaxHeight / 2) - (childView.getMeasuredHeight() / 2);
                    layoutView(childView, spaceUsed, paddingTop + top, childView.getMeasuredWidth(), childView.getMeasuredHeight());
                    spaceUsed += getWidthWithMargins(childView);
                }
            }
        }
    
        private int getWidthWithMargins(View child)
        {
            final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
            return child.getWidth() + lp.leftMargin + lp.rightMargin;
        }
    
        private int getHeightWithMargins(View child)
        {
            final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
            return child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
        }
    
        private int getMeasuredWidthWithMargins(View child)
        {
            final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
            return child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
        }
    
        private int getMeasuredHeightWithMargins(View child)
        {
            final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
            return child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
        }
    
        private void layoutView(View view, int left, int top, int width, int height)
        {
            MarginLayoutParams margins = (MarginLayoutParams) view.getLayoutParams();
            final int leftWithMargins = left + margins.leftMargin;
            final int topWithMargins = top + margins.topMargin;
    
            view.layout(leftWithMargins, topWithMargins, leftWithMargins + width, topWithMargins + height);
        }
    
        @Override
        public LayoutParams generateLayoutParams(AttributeSet attrs)
        {
            return new MarginLayoutParams(getContext(), attrs);
        }
    
        @Override
        protected LayoutParams generateDefaultLayoutParams()
        {
            return new MarginLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        }
    }
    

    Declare a custom attribute, in values/attrs.xml

    <declare-styleable name="PriorityLayoutHorizontal">
            <attr name="priorityHigh" format="reference"/>
    </declare-styleable>
    

    Using this layout, you can give high priority to the Views that should be visible in the layout definitely. Remaining one TextView will be drawn based on the remaining width in the screen.

    To use this layout:

        <com.example.component.PriorityLayoutHorizontal
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:orientation="horizontal"
                        app:priorityHigh="@array/priority_array"
                        >
    
                        <TextView
                            android:id="@+id/contact_name"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:singleLine="true"
                            />
    
                        <ImageView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:src="@drawable/your_drawable"
                            />
    
    </com.example.component.PriorityLayoutHorizontal>
    

    And define the priority_array in values/arrays.xml

    <integer-array name="priority_array">
            <item>1</item>
    </integer-array>
    

    Here, we have mentioned the priority for the View "1". That is the second TextView in your layout. So the second TextView will be visible always, and the first TextView will be ellipsized.

    P.S: this is a generic solution. i.e. this can be used with different Views, and more than 2 Views in a line. You can use this layout, or a custom ViewGroup specific to your usecase could be written.

    0 讨论(0)
  • 2020-12-13 14:33

    I set the maxwidth of left textview base of the width of right textview, take a looking my code. hope this will help.

    TextView leftText ;
    TextView rightText;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        leftText = (TextView) findViewById(R.id.leftTextView);
        rightText = (TextView) findViewById(R.id.rightTextView);
    
    
        measureView(leftText);
        measureView(rightText);
    
        leftText.setMaxWidth(getWidth() - rightText.getMeasuredWidth());
    
    }
    
    private void measureView(View view) {
        ViewGroup.LayoutParams params = view.getLayoutParams();
        int childWidth = ViewGroup.getChildMeasureSpec(0, view.getPaddingLeft() + view.getPaddingRight(), params.width);
        int childHeight = ViewGroup.getChildMeasureSpec(0, view.getPaddingBottom() + view.getPaddingTop(), params.height);
        view.measure(childWidth, childHeight);
    
    }
    
    private int getWidth() {
        WindowManager wm = (WindowManager)getSystemService(Context.WINDOW_SERVICE);
        Display display = wm.getDefaultDisplay();
        return display.getWidth();
    }
    

    and the xml is:

    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    
    <TextView
        android:id="@+id/leftTextView"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:singleLine="true"
        android:text="abadabadabadabadabadabadabadabadabadabaasdfasdfasdfadabadabadabadabadabadabadabadabadabadabaasdfasdfasdfadabadabadabadabadabadabadabadabadabadabaasdfasdfasdfad"/>
    
    <TextView
        android:id="@+id/rightTextView"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:textColor="#adc391"
        android:singleLine="true"
        android:text="adsfasd"/>
    
    </LinearLayout>
    

    this is the screenshot after running: enter image description here

    0 讨论(0)
  • 2020-12-13 14:36

    The Layout you are expected can be created with help of layout_weight attirbute which comes with LinearLayout. So you may need to use LinearLayout with weightSumand layout_weight (passing to its childview) attributes.

    Here's a layout which divides your width of layout in four parts which divides in to 3:1 between both Textview. Intacting right TextView making left TextView displays text as much as possible it can show without disurbing right TextView.

    Please feel free to try out with variant weightSum and layout_weight combination as per your need.

    Layout:

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:weightSum="4" >
    
        <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:singleLine="true"
            android:text="StackOverflow StackOverflow StackOverflow "
            />
    
        <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="right"
            android:layout_weight="3"
            android:maxLines="1"
            android:text="TextView"
            android:textColor="@android:color/white" />
    </LinearLayout>
    
    0 讨论(0)
  • 2020-12-13 14:38

    I suggest wrapping two TextView's into LinearLayout. Then apply android:weightSum="1" to this LinearLayout and apply android:layout_weight="1" to the child TextView that must extend.

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:weightSum="1">
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:ellipsize="end"
            android:maxLines="1"
            android:padding="8dp"
            android:text="blabla bla bla bla bla bla bla bla bla bla bla bla bla "/>
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="8dp"
            android:text="static text"/>
    
    </LinearLayout>
    

    Remember to set the two atributes this it to work perfectly:

    android:ellipsize="end"
    android:maxLines="1"
    

    and LinearLayout must have android:layout_width="wrap_content"

    If you would like this ViewGroup to take whole space, wrap it with another ViewGroup:

    <RelativeLayout
       android:layout_height="wrap_content"
       android:layout_width="match_parent">
    
       <!--The previous view group-->
    
    </RelativeLayout>
    
    0 讨论(0)
  • 2020-12-13 14:41

    Here is a layout that will do what you'd like it to:

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="left">
    
        <TextView
        android:id="@+id/leftTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toLefOf="@+id/rightTextView"
        android:maxLines="1"
        android:textSize="18sp" />
    
        <TextView
        android:id="@+id/rightTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:textSize="18sp" />
    
    </RelativeLayout>
    

    The key parts here are the gravity of the relative layout, and aligning the right textview as alignParentRight with the left textview attached to that on the left.

    0 讨论(0)
  • 2020-12-13 14:45

    Here comes a ConstraintLayout solution.

    The magic trick is Chains.

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="20dp">
    
        <TextView
            android:id="@+id/leftTv"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:ellipsize="end"
            android:maxLines="1"
            android:textColor="#333333"
            android:textSize="18sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/rightTv"
            app:layout_constraintHorizontal_bias="0.0"
            app:layout_constraintHorizontal_chainStyle="packed"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintWidth_default="wrap"
            tools:text="Text view that is very long and will not fit the" />
    
        <TextView
            android:id="@+id/rightTv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="16dp"
            android:layout_marginLeft="16dp"
            android:textColor="#21b38a"
            android:textSize="16sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toEndOf="@+id/leftTv"
            app:layout_constraintTop_toTopOf="parent"
            tools:text="Text View" />
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    
    0 讨论(0)
提交回复
热议问题