Recently, while working with the ViewPager, I found out that sometimes the Android ViewPager with WRAPCONTENT height flag is not visible. The reason for this is perhaps that the wrap_content flag needed height from the child view and the child view height is still not known.
The best way to check this is to fix the height to some hardcoded value say “200dp” and check if the view is visible.
Now if the view is visible and it gets your work done you don’t need to follow this blog anymore as you have achieved your purpose. But, if you still want to have a viewpager which has height set by the wrap_content flag in your XML file, then you should continue reading.
I believe that the purpose of this blog is very clear to you by now. So I will just come to the approach I have used.
APPROACH
- Create A custom View class that extends the View Pager class.(say WrapContentViewPager).
- Create default constructors for your class as in the View Pager class, so that you can directly call super with these and create a proper view for your custom view and use it in xml as easily as you would use the ViewPager tag for creating the viewpager view.
- Create a Function that will add a simple page Change listener to your custom view and then call this function in the default constructors you just created in above step.
- Override the onMeasure Method of the ViewPager class in your custom View class and get the child’s measured height and then set in the height params and call the super function.
CODE
WrapContentViewPager class :-
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
public class WrapContentViewPager extends ViewPager { public WrapContentViewPager(Context context) { super(context); initPageChangeListener(); } public WrapContentViewPager(Context context, AttributeSet attrs) { super(context, attrs); initPageChangeListener(); } private void initPageChangeListener() { addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { @Override public void onPageSelected(int position) { requestLayout(); } }); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { View child = getChildAt(getCurrentItem()); if (child != null) { child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); int h = child.getMeasuredHeight(); heightMeasureSpec = MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY); } super.onMeasure(widthMeasureSpec, heightMeasureSpec); } } |
Use of this class in your XML File:-
1 2 3 4 5 |
<com.example.myApplication.WrapContentViewPager android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:color/white" /> |
The Above code will just change the height of the Viepager as per the current child. but the main power of the viewPager is to prepare the view for the previous and next item as well so if you want you can get this also and this again is nothing but iterating the viewPager as per the child in the memory so we just need to have a for loop in onMeasure method of the viewPager and iterate each child View.
CODE
EnhancedWrapContentViewPager class:-
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
public class EnhancedWrapContentViewPager extends ViewPager{ public EnhancedWrapContentViewPager (Context context) { super(context); } public EnhancedWrapContentViewPager (Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int mode = MeasureSpec.getMode(heightMeasureSpec); // Unspecified means that the ViewPager is in a ScrollView WRAP_CONTENT. // At Most means that the ViewPager is not in a ScrollView WRAP_CONTENT. if (mode == MeasureSpec.UNSPECIFIED || mode == MeasureSpec.AT_MOST) { // super has to be called in the beginning so the child views can be initialized. super.onMeasure(widthMeasureSpec, heightMeasureSpec); int height = 0; for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); int childMeasuredHeight = child.getMeasuredHeight(); if (childMeasuredHeight > height) { height = childMeasuredHeight; } } heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); } // super has to be called again so the new specs are treated as exact measurements super.onMeasure(widthMeasureSpec, heightMeasureSpec); } } |
Use of this class in your XML File:-
1 2 3 4 5 |
<com.example.myApplication.EnhancedWrapContentViewPager android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:color/white" /> |
That’s it, you can run this code and check it yourself.