Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ConstraintLayout not dynamically changing Height with Wrap_Content inside RecyclerView

I have a StaggeredLayout RecyclerView that is filled with different cards using CardView, inside those cards there is normally a ConstraintLayout with all the fields empty. Everything has wrap_content as height.

My problem is that it seems the CardView height is being calculated before any data is filled and not updated afterwards. I have already tried directly invalidating, setting measureAllChildren to true, using requestLayout on the itemView and making so that the bottom items have a constraint to the bottom of the parent but the problem perseveres.

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/home_card_half_min_gutter">

<android.support.constraint.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:layout_gravity="center"
    android:padding="@dimen/content_spacing">


    <ImageView
        android:id="@+id/iv_icon"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginBottom="@dimen/content_spacing"
        android:contentDescription="@string/icon"
        app:layout_constraintBottom_toTopOf="@+id/guideline"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@drawable/ic_airplanemode_inactive_accent_dark_24dp"
        android:scaleType="fitCenter"/>

    <android.support.constraint.Guideline
        android:id="@+id/guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.35" />

    <TextView
        android:id="@+id/tv_item_1_label"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="0dp"
        android:textAppearance="@style/TextAppearance.AppCompat.Caption"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/guideline5"
        app:layout_constraintTop_toTopOf="@+id/guideline"
        tools:text="TextView" />

    <TextView
        android:id="@+id/tv_item_1"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/content_half_spacing"
        android:textAppearance="@style/Base.TextAppearance.AppCompat.Body1"
        app:layout_constraintLeft_toLeftOf="@+id/tv_item_1_label"
        app:layout_constraintRight_toLeftOf="@+id/guideline5"
        app:layout_constraintTop_toBottomOf="@+id/tv_item_1_label"
        tools:text="TextView" />

    <TextView
        android:id="@+id/tv_item_2_label"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:textAppearance="@style/TextAppearance.AppCompat.Caption"
        app:layout_constraintLeft_toLeftOf="@+id/guideline5"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="@+id/guideline"
        tools:text="TextView" />

    <TextView
        android:id="@+id/tv_item_2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/content_half_spacing"
        android:textAppearance="@style/Base.TextAppearance.AppCompat.Body1"
        app:layout_constraintLeft_toLeftOf="@+id/tv_item_2_label"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tv_item_2_label"
        tools:text="TextView" />

    <TextView
        android:id="@+id/tv_item_3_label"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/content_spacing"
        android:textAppearance="@style/TextAppearance.AppCompat.Caption"
        app:layout_constraintLeft_toLeftOf="@+id/tv_item_1"
        app:layout_constraintRight_toLeftOf="@+id/guideline5"
        app:layout_constraintTop_toBottomOf="@+id/tv_item_1"
        tools:text="TextView" />

    <TextView
        android:id="@+id/tv_item_3"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/content_half_spacing"
        android:textAppearance="@style/Base.TextAppearance.AppCompat.Body1"
        app:layout_constraintLeft_toLeftOf="@+id/tv_item_3_label"
        app:layout_constraintRight_toLeftOf="@+id/guideline5"
        app:layout_constraintTop_toBottomOf="@+id/tv_item_3_label"
        tools:text="TextView" />

    <TextView
        android:id="@+id/tv_item_4_label"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/content_spacing"
        android:textAppearance="@style/TextAppearance.AppCompat.Caption"
        app:layout_constraintLeft_toLeftOf="@+id/tv_item_2"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tv_item_2"
        tools:text="TextView" />

    <TextView
        android:id="@+id/tv_item_4"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/content_half_spacing"
        android:textAppearance="@style/Base.TextAppearance.AppCompat.Body1"
        app:layout_constraintLeft_toLeftOf="@+id/tv_item_4_label"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tv_item_4_label"
        tools:text="TextView" />

    <android.support.constraint.Guideline
        android:id="@+id/guideline5"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.5" />
</android.support.constraint.ConstraintLayout>

The fields are filled inside the method onBindViewHolder on the adapter as follows:

DataExample example = mItems.get(position);
Card4ViewHolder vhCon = (Card4ViewHolder) holder;

vhCon.mIcon.setImageResource(R.drawable.ic_data);

vhCon.mItem1Label.setText(R.string.A_label);
vhCon.mItem1.setText(example.getA());

vhCon.mItem2Label.setText(R.string.B_label);
vhCon.mItem2.setText(example.getB());

vhCon.mItem3Label.setText(R.string.C_label);
vhCon.mItem3.setText(example.getC());

vhCon.mItem4Label.setText(R.string.D_label);
vhCon.mItem4.setText(example.getD());

The RecyclerView is initialized as follows:

DisplayMetrics displayMetrics = res.getDisplayMetrics();
float pixelWidth =  displayMetrics.widthPixels;
int noOfColumns = (int) (pixelWidth / res.getDimension(R.dimen.home_card_min_width));
mLayoutManager = new StaggeredGridLayoutManager(noOfColumns,
        StaggeredGridLayoutManager.VERTICAL);
mLayoutManager.setGapStrategy(StaggeredGridLayoutManager
        .GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS);
mHomeCardAdapter = new HomeCardAdapter(activity);
mCardsRecyclerView.setAdapter(mHomeCardAdapter);
mCardsRecyclerView.setLayoutManager(mLayoutManager);

and the layout which it is inside of is a fragment as defined here:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/home_rv_dashboard_cards"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</ScrollView>

Scrolling and so on works without a problem, the size of the RecyclerView is also updated on the fly.

EDIT: I already tried changing the StaggeredLayout to a LinearLayoutManager and even removing the CardView altogether. After these tests I seemed to confirm that the problem is in the ConstraintLayout itself.

like image 444
Raeglan Avatar asked May 10 '17 15:05

Raeglan


People also ask

Does recyclerview have a flaw with constraintlayout?

I figured out that RecyclerView has a flaw with ConstraintLayout. Even if you set the top and bottom constraints for the recyclerView and set its height to "wrap_content", it goes out of screen when the items are filled up and the max height it acquired is that of screen height.

What is a constraintlayout?

T he ConstraintLayout is a very powerful component that allows you to build complex layouts while retaining a flat hierarchy. In most cases, we have only a static layout that easily fits on one screen. Nevertheless, we often come to the point where we have layouts that can dynamically expand their sizes.

Why is my recyclerview not scrollable?

but the RecyclerView is cropped (rather than resized), therefore making it not scrollable. I tried setting the RecyclerView's layout_height to a fixed value, but it was still not scrollable I add more items to the recyclerView and reproduce the problem. This is because the recyclerView's height is larger than the include layout has.

How to create Android app with recycler view and bar layout?

Step 1 − Create a new project in Android Studio, go to File ⇒ New Project and fill all required details to create a new project. Step 2 − Add the following code to res/layout/activity_main.xml. In the above code, we have taken app bar layout and recycler view. Step 3 − Add the following code to src/MainActivity.java


1 Answers

After searching for quite some time, I realized that the problem was a big oversight of my part.

On the ImageView we see that the height is defined by the Constraints, and the constraint bottom is the guideline:

<ImageView
    ...
    android:layout_width="0dp"
    android:layout_height="0dp"
    ...
    app:layout_constraintBottom_toTopOf="@+id/guideline"
    ... />

The guideline itself is has a percentage position based on the final height of the card.

<android.support.constraint.Guideline
    android:id="@+id/guideline"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    app:layout_constraintGuide_percent="0.35" />

So what I got here was a case of mutual dependencies, the height of the whole card is defined by the items inside and the height of the Image is percentage of that height. What I think happens is that the height of the card is looked up based on the combined heights of what is inside(0dp for the image) and then the image is scaled and placed moving everything down with it.

TLDR: I oversaw a mutual dependency and setting the size of the image veforehand fixes the problem.

like image 85
Raeglan Avatar answered Oct 09 '22 12:10

Raeglan