Updated 26 October 2021
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.
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.
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.
If you have more details or questions, you can reply to the received confirmation email.
Back to Home
27 comments
thanx
Thanks for your concern but we never faced this kind of issue in any of our apps.
The reason for your issues most probably is that you must have overridden the onResume() method of your activity. If that’s the case then you need to explicitly set up the view page and adapter again in onResume() method in order to avoid this issue.
If your problem is not solved with this approach, then please share your code and i will look into it.
My XML layout has format
LinearLayout
FrameLayout( this layout used to player play video)
ScrollView
LinearLayout
RelativeLayout ( call is layout1)
RelativeLayout
Tablayout
Viewpager
When I apply your solution, view pager scroll is OK, but layout1 lost a part on top, and can’t display
Someone can give to me solution
Thanks all.
can you provide your source code, so that we can have a look into your code and predict how this can be done?
Generally, if this kind of complex layout architecture is followed then you might have to maintain all the layouts (their width & height) in your view render(Activity/ Fragment) class.
Your code is working nice.
If I use this code in my app which is commercial,
Do I consider any license?
Thanks for the appreciating words.
No, this particular piece of information is not under any license.
You can use it anywhere you want.
Personally, I have not used the ViewPager2 till now.
But if something similar is troubling you then just make WrapContentViewPager extend ViewPager2, and then in the remaining article replace all the accourences of the view pager with viewPager2 and this should work.
If this does not works out, then please do share teh exact code with me, so that we can look and update you accordingly.
Regards,
Anchit
This is great solution.
I am using WrapContentViewPager but it’s not updating height of viewPager on Tab change.
I have Tablayout with viewpager inside bottomsheetFragment.
If this is already there, then we will need to have a look in your exact code.
Could you please help me on resolving and understand the cause of this issue? Appreciate your response. Thanks
Error log from Crashlytics:
{{Fatal Exception: java.lang.IllegalArgumentException: No view found for id 0x7f090338 (com.x.y.z:id/pager) for fragment FinalFragment
{c762f8e}
(b82eb4bd-1a41-4ac3-bcfc-3290674e7c54) id=0x7f090338}
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:315)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1187)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1356)
at androidx.fragment.app.FragmentManager.moveFragmentToExpectedState(FragmentManager.java:1434)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1497)
at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:447)
at androidx.fragment.app.FragmentManager.executeOps(FragmentManager.java:2169)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1992)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1947)
at androidx.fragment.app.FragmentManager.execSingleAction(FragmentManager.java:1818)
at androidx.fragment.app.BackStackRecord.commitNowAllowingStateLoss(BackStackRecord.java:303)
at androidx.fragment.app.FragmentStatePagerAdapter.finishUpdate(FragmentStatePagerAdapter.java:270)
at androidx.viewpager.widget.ViewPager.populate(ViewPager.java:1244)
at androidx.viewpager.widget.ViewPager.populate(ViewPager.java:1092)
at androidx.viewpager.widget.ViewPager.onMeasure(ViewPager.java:1622)
at com.dexels.sportlinked.util.ui.WrapContentViewPager.onMeasure(WrapContentViewPager.java:28)
at android.view.View.measure(View.java:27131)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7951)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at android.view.View.measure(View.java:27131)
at androidx.recyclerview.widget.RecyclerView$LayoutManager.measureChildWithMargins(RecyclerView.java:9384)
at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1653)
at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1587)
at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:665)
at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:4134)
at androidx.recyclerview.widget.RecyclerView.onMeasure(RecyclerView.java:3540)
at android.view.View.measure(View.java:27131)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7951)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1552)
at android.widget.LinearLayout.measureHorizontal(LinearLayout.java:1204)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:723)
at android.view.View.measure(View.java:27131)
at androidx.swiperefreshlayout.widget.SwipeRefreshLayout.onMeasure(SwipeRefreshLayout.java:641)
at android.view.View.measure(View.java:27131)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7951)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1552)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:842)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:721)
at android.view.View.measure(View.java:27131)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7951)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at android.view.View.measure(View.java:27131)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7951)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at android.view.View.measure(View.java:27131)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7951)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1552)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:842)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:721)
at android.view.View.measure(View.java:27131)
at androidx.drawerlayout.widget.DrawerLayout.onMeasure(DrawerLayout.java:1119)
at android.view.View.measure(View.java:27131)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7951)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at androidx.appcompat.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:146)
at android.view.View.measure(View.java:27131)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7951)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at android.view.View.measure(View.java:27131)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7951)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at android.view.View.measure(View.java:27131)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7951)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1552)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:842)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:721)
at android.view.View.measure(View.java:27131)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7951)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at com.android.internal.policy.DecorView.onMeasure(DecorView.java:1173)
at android.view.View.measure(View.java:27131)
at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:4187)
at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:2936)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:3204)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2618)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:9971)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1010)
at android.view.Choreographer.doCallbacks(Choreographer.java:809)
at android.view.Choreographer.doFrame(Choreographer.java:744)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:995)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:246)
at android.app.ActivityThread.main(ActivityThread.java:8633)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)}}
As per the logs, the issue seems to be some fragment being removed and the code is being executed after that as well.
Please double check the code for execution of some access to the view with id pager after the fragment being detached or killed.
If still you need some help, then please do provide the code to me so that i can check that in complete details.