3

I have a ScrollView which hosts a ViewPager which hosts Fragments of dynamic height. Since a ScrollView and ViewPager don't go well together due to the scroll handling, I used one of the custom solution from here. But I am currently getting really abnormal results. The first fragment in the ViewPager always gets a height of 0. Also sometimes the fragments don't show their content but when I scroll back and forth and come back to that fragment, content might show up.

Some code for you to look at :

The custom ViewPager :

import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

/**
 * Created by Narayan Acharya on 12/07/2016.
 */
public class DynamicHeightWrappingViewPager extends ViewPager {
    private View mCurrentView;

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

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (mCurrentView == null) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            return;
        }

        int height = 0;
        mCurrentView.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
        int h = mCurrentView.getMeasuredHeight();
        if (h > height) height = h;
        heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
        Log.d("ViewPager Measure", h + ":" + heightMeasureSpec);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    public void measureCurrentView(View currentView) {
        mCurrentView = currentView;
        requestLayout();
    }
}

The custom ScrollView :

import android.content.Context;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.widget.ScrollView;

/**
 * Created by Narayan Acharya on 12/07/2016.
 */
public class ChildrenHeightAdaptingScrollView extends ScrollView {
    private GestureDetector mGestureDetector;

    public ChildrenHeightAdaptingScrollView(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 GestureDetector.SimpleOnGestureListener {
        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2,
                                float distanceX, float distanceY) {
            return (Math.abs(distanceY) > Math.abs(distanceX));
        }
    }
}

The PagerAdapter :

import android.app.Fragment;
import android.app.FragmentManager;
import android.support.v13.app.FragmentPagerAdapter;
import android.util.Log;
import android.view.ViewGroup;

/.. Some more project specific imports here../
/**
 * Created by Narayan Acharya on 22/06/2016.
 */
public class EventsPagerAdapter extends FragmentPagerAdapter {

    private Event event;
    private int PAGE_COUNT = 2;
    private int mCurrentPosition = -1;
    private String tabTitles[] = new String[]{"INFO", "FAQs"};

    public EventsPagerAdapter(FragmentManager fm, Event event) {
        super(fm);
        this.event = event;
    }

    @Override
    public Fragment getItem(int position) {
        switch (position) {
            case 0:
                return EventInfoFragment.getInstance(event);
            case 1:
                return EventFAQsFragment.getInstance(event);
            default:
                return null;
        }
    }

    @Override
    public int getCount() {
        return PAGE_COUNT;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        // Generate title based on item position
        return tabTitles[position];
    }

    @Override
    public void setPrimaryItem(ViewGroup container, int position, Object object) {
        super.setPrimaryItem(container, position, object);
        if (position != mCurrentPosition) {
            Fragment fragment = (Fragment) object;
            DynamicHeightWrappingViewPager pager = (DynamicHeightWrappingViewPager) container;
            if (fragment != null && fragment.getView() != null) {
                mCurrentPosition = position;
                pager.measureCurrentView(fragment.getView());
                Log.d("Requested Measure for", position + " " + fragment.getClass().getSimpleName());
            }
        }
    }
}

As per my observations, the only difference I could spot in the code from the link I mentioned above and the one I am using is that the link uses FragmentPagerAdapter from support library v4 while I am using from the v13(Cannot change this to v4, due to some other restrictions). What are the major differences between the two versions of support library for how I am using it?

Community
  • 1
  • 1
Narayan Acharya
  • 1,459
  • 1
  • 18
  • 33
  • Are the contents are readily available in the fragment when it is inflated or are you making any network calls to fetch the contents? – Abhishek V Jul 14 '16 at 05:04
  • No network calls in those fragments. They only display data that I provide them with. The two fragments only contain simple WebViews and I give them text that is formatted in HTML. NO network calls in there. – Narayan Acharya Jul 14 '16 at 07:24
  • Ah! It could be some issues with measuring WebView height, Can you post a sample HTML? I will try out with my sample app. – Abhishek V Jul 14 '16 at 07:38
  • Currently, I am using simple dummy text, Lorem Ipsum text only. But the issue only comes with the left most fragment. When I move the second tab I can see the text. When I try to slide back to the first fragment, I can see the text in there, but as soon as I finish the slide, the first fragment simply collapses :| – Narayan Acharya Jul 14 '16 at 07:41
  • Hi! The onViewCreated() in my fragment is getting called before the onMeasure() in the ViewPager. Maybe that is why the height of the first view is coming out to be zero. Any insights on this? @AbhishekV Sorry if I am bothering you a bit too much! – Narayan Acharya Jul 14 '16 at 12:03
  • I am bit busy now, will look into it soon. – Abhishek V Jul 14 '16 at 12:46

1 Answers1

4

This might seem like really stupid and I am not sure why this works exactly! If anyone knows why, please do answer.

The problem with the ViewPager collapsing was that it simply contained a WebView in each Fragment. I toiled hard trying to find solutions and writing more custom ViewPagers and ScrollViews. Just when I was about to give up I wrapped the WebView in a LinearLayout and the Fragment at position 0 did not collapse. It works smoothly now.

So if anyone is getting blank pages while trying this or similar solutions please try to wrap it in a LinearLayout or something.

Hope this helps someone!

Vasily Kabunov
  • 6,511
  • 13
  • 49
  • 53
Narayan Acharya
  • 1,459
  • 1
  • 18
  • 33
  • Check in onMeasure() code if you are returning new height in the "super.onMeasure(widthMeasureSpec, heightMeasureSpec)" statement.. basically heightMeasureSpec should have the new height.. This helped me solve similar problem I faced. – Govind Nov 23 '16 at 09:24
  • 2
    This answer deserves a nobel prize. I spend hours with differents solutions my issue was, I have a nested scroll view with a view pager and inside the view pager I have a web view. Thanks man! Your saved my day! – AndroidRuntimeException Mar 28 '19 at 18:20
  • I tried it, It works fine, but it fails in one case if my Third fragment having viewpager also, then only third fragmnet collapsed. – Yash Bhardwaj Aug 05 '20 at 02:56