Android:在ScrollView中具有动态高度的ViewPager

3
我有一个ScrollView,里面放了一个ViewPager,ViewPager中包含高度不固定的Fragment。由于ScrollView和ViewPager之间滚动处理不太匹配,我使用了这里提供的自定义解决方案。但是,我目前得到了非常异常的结果。ViewPager中的第一个Fragment始终具有0的高度。有时,Fragment可能没有显示其内容,但是当我来回滚动并返回到该Fragment时,内容可能会出现。

以下是一些代码:

自定义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();
    }
}

自定义 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());
            }
        }
    }
}

据我的观察,我发现上面提到的链接中的代码与我正在使用的代码唯一的区别就是该链接中使用了support library v4的FragmentPagerAdapter,而我使用的是v13版(由于其他限制,无法更改为v4)。在我使用它们的方式方面,这两个版本的支持库主要有哪些不同之处?

当片段被填充时,其内容是否已经可用,或者您是否进行任何网络调用来获取内容? - Abhishek V
这些片段中没有网络调用。它们只显示我提供的数据。这两个片段仅包含简单的WebViews,我会给它们格式化为HTML的文本。其中没有网络调用。 - Narayan Acharya
啊!可能是测量 WebView 高度的问题,你能发一份 HTML 示例吗?我会用我的示例应用程序试一下。 - Abhishek V
目前,我只使用简单的虚拟文本,即Lorem Ipsum文本。但问题仅出现在最左侧的片段上。当我移动第二个选项卡时,我可以看到文本。当我尝试滑回第一个片段时,我可以看到里面的文本,但是一旦我完成滑动,第一个片段就会简单地折叠 :| - Narayan Acharya
嗨!我的片段中的onViewCreated()在ViewPager中的onMeasure()之前被调用。也许这就是为什么第一个视图的高度为零的原因。对此有何见解?@AbhishekV 如果我打扰你太多了,对不起! - Narayan Acharya
我现在有点忙,很快会看一下。 - Abhishek V
1个回答

4
这可能看起来很愚蠢,我不确定为什么这样做会有效!如果有人知道原因,请回答一下。
ViewPager崩溃的问题在于它只是在每个Fragment中包含了一个WebView。我费尽心思地寻找解决方案并编写更多自定义ViewPagers和ScrollViews。就在我即将放弃时,我将WebView包装在LinearLayout中,位置0的Fragment没有崩溃。现在它可以平稳运行了。
所以,如果有人在尝试此类解决方案时遇到空白页面,请尝试将其包装在LinearLayout或其他容器中。
希望这能帮助到某些人!

在onMeasure()代码中检查是否在"super.onMeasure(widthMeasureSpec, heightMeasureSpec)"语句中返回了新的高度.. 基本上heightMeasureSpec应该具有新的高度.. 这帮助我解决了我遇到的类似问题。 - Govind
2
这个答案值得诺贝尔奖。我花了几个小时尝试不同的解决方案,我的问题是,我有一个嵌套的滚动视图,其中包含一个视图页面和一个网页视图。谢谢你!你救了我的一天! - AndroidRuntimeException
我试过了,它运行得很好,但是在一个情况下它失败了,如果我的第三个片段也有viewpager,那么只有第三个片段会崩溃。 - Yash Bhardwaj

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接