Search code examples
androidandroid-layoutandroid-scrollviewhorizontalscrollview

Adjust HorizontalScrollView height based on childs' height


I have a HorizontalScrollView in which I programmatically add childs. The scroll view is pretty simple:

<HorizontalScrollView
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <LinearLayout
        android:orientation="horizontal"
        android:id="@+id/child_container"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

    </LinearLayout>
</HorizontalScrollView>

As said, in child_container I have to programmatically add child views. Child views are all inflated from this layout:

<LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:padding="@dimen/content_padding"
    android:orientation="vertical">

    <ImageView
        android:layout_width="150dp"
        android:layout_height="150dp" />

    <TextView
        android:id="@+id/text"
        style="@style/TextAppearance.AppCompat.Small"
        android:singleLine="false"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>

The problem is that the text in the TextView can be small (2 lines) or larger, up to 7-8 lines. How can I have this HorizontalScrollView to adapt its height based on the text?

In XML, I could just set android:lines="10" to the text view, but the actual maximum number of lines might change and I can't hardcode that value.

I tried lots of stuff at runtime, like requesting layouts and re-measuring, but I might be doing it wrong. Thank you.

LinearLayout l = (LinearLayout) findViewById(R.id.child_container);
for (String text : texts) {

    View childView = inflater.inflate(...);
    TextView textView = childView.findViewById(R.id.text);
    textView.setText(text);

    l.addView(childView);
}
// Here do something on l 
// (or on its parent, the HorizontalScrollView)
// to change its height

Solution

  • I ended up subclassing. Here's my outer layout:

    <com.app.WrapContentHorizontalScrollView
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    </com.app.WrapContentHorizontalScrollView>
    

    Here's my child layout:

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <ImageView
            android:layout_width="150dp"
            android:layout_height="150dp" />
    
        <TextView
            android:singleLine="false"
            android:layout_width="150dp"
            android:layout_weight="1"
            android:layout_height="0dp" />
    </LinearLayout>
    

    And here's my class:

    public class WrapContentHorizontalScrollView extends HorizontalScrollView {
    
        private LinearLayout directChild;
        private int targetHeight;
        private final static int UNSPECIFIED = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
    
        public WrapContentHorizontalScrollView(Context context) {
            this(context, null);
        }
    
        public WrapContentHorizontalScrollView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public WrapContentHorizontalScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            directChild = new LinearLayout(context, attrs, defStyleAttr);
            super.addView(directChild);
        }
    
        @Override
        public void addView(@NonNull View child) {
            if (directChild != null) {
                directChild.addView(child);
                child.measure(UNSPECIFIED, UNSPECIFIED);
                targetHeight = Math.max(targetHeight, child.getMeasuredHeight());
            }
        }
    
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            int newHeight = MeasureSpec.makeMeasureSpec(targetHeight, MeasureSpec.EXACTLY);
            super.onMeasure(widthMeasureSpec, newHeight);
        }
    
    }
    

    Hope it helps someone.