androidandroid-fragmentsandroid-viewpager

Viewpager with dynamic height not working (always use the first fragment's height)


I have followed this and that answers, i also found this link.

I use those resources to do trial & errors. Now my custom ViewPager successfully measure its content, but only the first one displayed. FYI, my ViewPager holds some complex views, like ExpendableListView - thats why none of the code in those resources working perfectly, i need to modify the code by myself.

I have 4 fragments (content), so i use pager.setOffscreenPageLimit(3);

This is my custom ViewPager :

public class CustomViewPager extends ViewPager{

    public CustomViewPager (Context context) {
        super(context);
    }

    public CustomViewPager (Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        boolean wrapHeight = MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST;

        final View tab = getChildAt(0);
        int width = getMeasuredWidth();
        int tabHeight = tab.getMeasuredHeight();

        if (wrapHeight) {
            // Keep the current measured width.
            widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
        }

        int fragmentHeight = measureFragment(((Fragment) getAdapter().instantiateItem(this, getCurrentItem())).getView());
        heightMeasureSpec = MeasureSpec.makeMeasureSpec(tabHeight + fragmentHeight + (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, getResources().getDisplayMetrics()), MeasureSpec.AT_MOST);

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    public int measureFragment(View view) {
        if (view == null)
            return 0;

        view.measure(0, 0);
        return view.getMeasuredHeight();
    }
}

This is my CustomPagerTabStrip :

int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
android.view.ViewGroup.LayoutParams params =  getLayoutParams();
params.height = getMeasuredHeight();

And this is my XML :

        <RelativeLayout
            android:id="@+id/relativePager"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" >

            <com.xxx.yyy.util.CustomViewPager
                android:id="@+id/detailPager"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" >

                <com.xxx.yyy.util.CustomPagerTabStrip
                    android:id="@+id/detailTab"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:background="#161C28"
                    android:textColor="#FFFFFF" />
            </com.xxx.yyy.util.CustomViewPager>

        </RelativeLayout>

The example case :

If the first fragment's height is 100px, then all of other fragments will stay on 100px. So if the second fragment's height is 130px, it will be cut to 100px.

When i tried to debug, the onMeasure on my CustomViewPager always called 4 times when the Activity is created, but all of them only read the first fragment. The onMeasure never getting called again after that, even when i change (slide left/right) from one fragment to other.

Please help me out, i have spended lot of hours to get this thing working. Just ask me if you need more information, Thanks for your time.


Solution

  • Hi Blaze found some thing use full, following solution is working for me.

    Its include a custom scroll view and custom view pager. The purpose custom view pager is its calculate the height dynamically according to its children's height, its hieght is set to wrap content initially and the purpose of scroll view is handling the touch event , if the users scrolls the screen vertically it will not pass the touch even to scroll and thus user is able to scroll the page.

    Custom scroll view

    public class CustomScrollView extends ScrollView {
    
    private GestureDetector mGestureDetector;
    
    public CustomScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mGestureDetector = new GestureDetector(context, new YScrollDetector());
        setFadingEdgeLength(0);
    }
    
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return super.onInterceptTouchEvent(ev)
                && mGestureDetector.onTouchEvent(ev);
    }
    
    // Return false if we're scrolling in the x direction
    class YScrollDetector extends SimpleOnGestureListener {
        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2,
                float distanceX, float distanceY) {
            return (Math.abs(distanceY) > Math.abs(distanceX));
        }
    }
    }
    

    Custom view pager

    public class CustomPager extends ViewPager{
    
    public CustomPager (Context context) {
        super(context);
    }
    
    public CustomPager (Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    
    @Override
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    
        boolean wrapHeight = MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST;
    
        final View tab = getChildAt(0);
        int width = getMeasuredWidth();
        int tabHeight = tab.getMeasuredHeight();
    
        if (wrapHeight) {
            // Keep the current measured width.
            widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
        }
    
        int fragmentHeight = measureFragment(((Fragment) getAdapter().instantiateItem(this, getCurrentItem())).getView());
        heightMeasureSpec = MeasureSpec.makeMeasureSpec(tabHeight + fragmentHeight + (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, getResources().getDisplayMetrics()), MeasureSpec.AT_MOST);
    
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
    
    public int measureFragment(View view) {
        if (view == null)
            return 0;
    
        view.measure(0, 0);
        return view.getMeasuredHeight();
    }
    }
    

    Layout file

    <com.example.demo2.activity.widget.CustomScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >
    
        <TextView
            style="@style/text"
            android:text="Text 1" />
    
        <View style="@style/text_divider" />
    
        <TextView
            style="@style/text"
            android:text="Text 2" />
    
    
            <com.example.demo2.activity.widget.CustomPager
              android:id="@+id/myViewPager"
              android:layout_width="match_parent"
              android:layout_height="wrap_content" />
    
        <View style="@style/text_divider" />
    
        <TextView
            style="@style/text"
            android:text="Text 4" />
    
        <View style="@style/text_divider" />
    
        <TextView
            style="@style/text"
            android:text="Text 5" />
    
        <View style="@style/text_divider" />
    
        <TextView
            style="@style/text"
            android:text="Text 6" />
    
        <View style="@style/text_divider" />
    
        <TextView
            style="@style/text"
            android:text="Text 7" />
    
        <View style="@style/text_divider" />
    
        <TextView
            style="@style/text"
            android:text="Text 8" />
    
        <View style="@style/text_divider" />
    
        <TextView
            style="@style/text"
            android:text="Text 9" />
    
        <View style="@style/text_divider" />
    
        <TextView
            style="@style/text"
            android:text="Text 10" />
    </LinearLayout>
    

    Activity

    public class MainActivity extends FragmentActivity {
    
    
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        setContentView(R.layout.activity_main);
    
        MyPagerAdapter pageAdapter = new MyPagerAdapter(getSupportFragmentManager());
        ViewPager pager = (ViewPager)findViewById(R.id.myViewPager);
        pager.setAdapter(pageAdapter);
    
    }
    
    
    
    }
    

    Fragment adapter

    fragments with different heights are added to the viewpager, FragmentBlue,Green etc are just fragments used to add to the viewpager you can use your own fragments, one thing to remember is their outer layout should be same in my case I used liner layout for all

    public class MyPagerAdapter extends FragmentPagerAdapter {
    
    private List<Fragment> fragments;
    
    public MyPagerAdapter(FragmentManager fm) {
        super(fm);
        this.fragments = new ArrayList<Fragment>();
        fragments.add(new FragmentBlue());
        fragments.add(new FragmentGreen());
        fragments.add(new FragmentPink());
        fragments.add(new FragmentRed());
    }
    
    @Override
    public Fragment getItem(int position) {
        return fragments.get(position);
    }
    
    @Override
    public int getCount() {
        return fragments.size();
    }
    }