In my Android app running on Android 5.1.1 I have a layout using a Toolbar
with a TabLayout
, and underneath is a ViewPager
. All of these are put together in a CoordinatorLayout
.
In the first page of the ViewPager
is a RecyclerView
serving CardView
items.
My problem is that my ViewPager
keeps getting resized in a way so that my CardView
list items are cropped.
My main layout looks basically like this:
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >
<android.support.design.widget.AppBarLayout
android:layout_height="wrap_content"
android:layout_width="match_parent" >
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<android.support.design.widget.TabLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.design.widget.CoordinatorLayout>
And the first fragment served by my ViewPager
looks like:
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >
<android.support.v7.widget.RecyclerView
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="8dp"/>
</FrameLayout>
This renders something that looks like this:
When clicking a button in my layout, I use startActivityForResult
to invoke another activity, and when returning to my activity sometimes suddenly my list is cropped so that only half of the first item is visible:
After swiping horizontally to another pager in the ViewPager
and then back, the problem disappears, so it does seem a re-layout has not been properly triggered. Pressing HOME and then resuming my activity does NOT resolve the problem though. Note that this happens even if I am not modifying my layout in any way, I am simply returning from a startActivityForResult call. And yes, it only happens sometimes... And I have no background threads running that could explain the apparent random behavior.
At first I thought it was the RecyclerView
that had shrunk, but using HierarchyViewer I was able to find that it was actually the entire ViewPager
that had shrunk to about half its original height.
I tried various hacks to get around this, including calling invalidate()
and requestLayout()
on my entire view hiearchy, but nothing seemed to help (although swiping to another page and back again fixes it). Also, those are not the kind of solutions I want to resort to... Then I tried changing my ViewPager height to wrap_content
, which did in fact solve this particular problem; after returning to my activity the first item in my RecyclerView
is never cropped, and I can scroll down to the other items. However, now instead the very last item of my list is always cropped, as can be seen in this screenshot where the list is scrolled all the way to the bottom:
Since I am now at a point where I don't really understand what's going on, I need some help. What should I really use as the layout_height for my ViewPager, and - above all - why? To me, match_parent
makes sense, but how should I be thinking here? Is there a rational reason my views got cropped when using match_parent
, or did I in fact encounter a bug in ViewPager
, RecyclerView
and/or CoordinatorLayout
? How do I make sure that my ViewPager
consistently fills the entire screen area below the AppBar, and that my RecyclerView
can be scrolled vertically to properly render all CardView
list items?
It turns out this is almost certainly a bug in CoordinatorLayout
or even more likely in AppBarLayout$ScrollingViewBehavior
. In an effort to create a MCVE I realized it was the fact that my sub-activity had an IME on screen that caused the shrinking of the ViewPager - when my activity is resumed after onActivityResult
, the ViewPager
is shrunk as a result of reduced screen real-estate from the IME, but is never expanded again despite the fact that the IME is no longer being shown and the fact that the CoordinatorLayout
is indeed expanded.
After debugging and stepping through onLayout
and onMeasure
of CoordinatorLayout
and ViewPager
I am now fairly sure that the CoordinatorLayout
does not properly propagate the change in size to its children.
I found that I can "fix" the problem by calling requestLayout
on my ViewPager
, but only if the call is sufficiently delayed (10 ms never works, 100 ms works most of the time):
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mViewPager.requestLayout();
}
}, 100);
}
This obviously isn't a robust solution, but after investigating some more it turns out I probably don't even need CoordinatorLayout
since I don't really have any advanced scrolling behavior. So my solution will be to simply go with a LinearLayout
or RelativeLayout
as my root view group instead. Nice and simple, no need to complicate things.
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<android.support.design.widget.AppBarLayout
android:layout_height="wrap_content"
android:layout_width="match_parent" >
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<android.support.design.widget.TabLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
I will however try to condense this into a simple example and file a bug with Google.
As to what I should use as the height for my ViewPager
, my original use of match_parent
still seems reasonable. The fact that wrap_content
solved the problem (but caused other problems) is probably just due to inconsistencies caused by the bug.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With