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.
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.
I believe this could help you: Change:
<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>
to
<android.support.v7.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/home_rv_dashboard_cards"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layoutManager="LinearLayoutManager"/>
Then remove this lines from your code (where you initialize recyclerview):
mLayoutManager = new StaggeredGridLayoutManager(noOfColumns,
StaggeredGridLayoutManager.VERTICAL);
mLayoutManager.setGapStrategy(StaggeredGridLayoutManager
.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS);
mCardsRecyclerView.setLayoutManager(mLayoutManager);
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.