How can I make work ViewPager with RecyclerView inside NestedScrollView
Asked Answered
R

3

9

I am using NestedScrollView with ViewPager. NestedScrollView has a LinearLayout inside with some TextViews, TabLayout and ViewPager at the end. TextViews occupy most of the space and for ViewPager left a little space. ViewPager using two fragments, in one of them there is a few TextViews and ImageViews and in another fragment there is a RecyclerView.

When I set ViewPager's height to WRAP_CONTENT it takes only space that left and I can't scroll to see the rest of the first fragment, and the second fragment scoll inside little ViewPager.

When I set ViewPager's height to 1000dp for example, I am able to scroll down on first fragment, but the second fragment still scrolling inside little ViewPager. And after I scroll in fragment with RecyclerView scoll in the first Fragment not working anymore.

How can I fix scrolling problem and make ViewPager work with WRAP_CONTENT?

Here is my code:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 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="match_parent">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary" />

    </android.support.design.widget.AppBarLayout>

    <android.support.v4.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        app:layout_scrollFlags="scroll"
        android:fillViewport="true">

        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@color/mainBackground"
            android:orientation="vertical">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="SOME TEXT" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="SOME TEXT" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="SOME TEXT" />

            <android.support.design.widget.TabLayout
                android:id="@+id/tabLayout"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />

            <android.support.v4.view.ViewPager
                android:id="@+id/viewPager"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />

        </LinearLayout>

    </android.support.v4.widget.NestedScrollView>

</android.support.design.widget.CoordinatorLayout>
Ruyter answered 8/10, 2015 at 9:0 Comment(1)
Hi, did you get this to work? I've tried fillViewport but it is truncating the nested scrollview to viewport height and the recycler is nested-scrolling. By disabling nested scroll, even the nestedscrollview isn't scrolling (which is strange). Please share how, if you could make it work.Unctuous
M
11

I think you should override ViewPager like this:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    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 h = child.getMeasuredHeight();
        if (h > height) height = h;
    }
    heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
Mingle answered 11/1, 2017 at 9:43 Comment(2)
Did you intend to call super.onMeasure(...) twice?Kozak
This solution won't work properly as it's measuring all children. In the case, the ViewPager will have the same height of the highest child. You should measure only the current view and use requestLayout() in the onPageSelected(int position)Deipnosophist
E
3

@StevenZhang's solution almost worked for me but there was a problem when two tabs have different heights, the smaller tab takes the height of the bigger tab.

Based on this answer about how to make a dynamic height ViewPager I made a solution in Kotlin

Custom View Pager

class CustomViewPager: ViewPager {
    constructor(context: Context) : super(context)
    constructor(context : Context, attrs : AttributeSet) : super(context, attrs)

    private var currentView : View? = null

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {

        currentView?.let {
            it.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED))
            val h = it.measuredHeight
            val height = if (h > 0) h else 0
            val newHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
            super.onMeasure(widthMeasureSpec, newHeightMeasureSpec)
            return
        }

        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    }

    fun measureCurrentView(currentView: View?) {
        this.currentView = currentView
        requestLayout()
    }
}

In your custom PagerAdapter override setPrimaryItem

private var currentPosition = -1

override fun setPrimaryItem(container: ViewGroup, position: Int, `object`: Any) {
    super.setPrimaryItem(container, position, `object`)
    if (position != currentPosition) {
      val fragment = `object` as Fragment
      val pager = container as CustomViewPager
      if (fragment.view != null) {
         currentPosition = position
         pager.measureCurrentView(fragment.view)
      }
   }
}

You may also need to set isNestedScrollingEnabled of your RecyclerView to false

recyclerView.isNestedScrollingEnabled = false
Ennead answered 15/5, 2019 at 19:3 Comment(1)
What if we need viewpager 2?Alfonsoalfonzo
T
1

maybe you can try :

 NestedScrollView scrollView = (NestedScrollView) findViewById (R.id.nest_scrollview);
    scrollView.setFillViewport (true);

such as:https://mcmap.net/q/179897/-viewpager-in-a-nestedscrollview

Tonl answered 28/10, 2015 at 7:37 Comment(1)
this makes the viewPager visible, but isn't enough for recycler scrollingBlackhead

© 2022 - 2024 — McMap. All rights reserved.