【问题标题】:Android : ViewPager with a dynamic height within a ScrollViewAndroid:在 ScrollView 中具有动态高度的 ViewPager
【发布时间】:2016-07-13 11:01:26
【问题描述】:

我有一个 ScrollView,它承载一个 ViewPager,它承载动态高度的片段。由于滚动处理,ScrollView 和 ViewPager 不能很好地结合在一起,我使用了来自 here 的自定义解决方案之一。但我目前得到的结果非常不正常。 ViewPager 中的第一个片段的高度始终为 0。有时片段不显示其内容,但是当我来回滚动并返回该片段时,可能会显示内容。

一些代码供您查看:

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

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

根据我的观察,我可以从上面提到的链接和我正在使用的链接中发现的唯一区别是该链接使用支持库 v4 中的 FragmentPagerAdapter,而我使用的是 v13(无法更改此到 v4,由于其他一些限制)。两个版本的支持库在我的使用方式上的主要区别是什么?

【问题讨论】:

  • 在片段膨胀时,内容是否随时可用,或者您是否进行任何网络调用以获取内容?
  • 这些片段中没有网络调用。他们只显示我提供给他们的数据。这两个片段只包含简单的 WebView,我给它们提供了 HTML 格式的文本。那里没有网络电话。
  • 啊!测量 WebView 高度可能会出现一些问题,你能发布一个示例 HTML 吗?我将尝试使用我的示例应用程序。
  • 目前,我使用的是简单的虚拟文本,仅 Lorem Ipsum 文本。但问题只出现在最左边的片段上。当我移动第二个选项卡时,我可以看到文本。当我尝试滑回第一个片段时,我可以看到其中的文本,但是一旦我完成幻灯片,第一个片段就会崩溃:|
  • 嗨!我的片段中的 onViewCreated() 在 ViewPager 中的 onMeasure() 之前被调用。也许这就是为什么第一个视图的高度为零的原因。对此有何见解? @AbhishekV 对不起,如果我打扰你太多了!

标签: android android-fragments android-viewpager android-scrollview


【解决方案1】:

这可能看起来很愚蠢,我不确定为什么会这样!如果有人知道为什么,请回答。

ViewPager 崩溃的问题在于它只是在每个Fragment 中包含一个WebView。我努力寻找解决方案并编写更多自定义ViewPagersScrollViews。就在我准备放弃的时候,我将WebView 包裹在LinearLayout 中,而0 位置的Fragment 并没有崩溃。现在运行顺利。

因此,如果有人在尝试此或类似解决方案时出现空白页,请尝试将其包装在 LinearLayout 或其他东西中。

希望这对某人有所帮助!

【讨论】:

  • 如果您在“super.onMeasure(widthMeasureSpec, heightMeasureSpec)”语句中返回新高度,请检查 onMeasure() 代码。基本上,heightMeasureSpec 应该有新的高度。这帮助我解决了类似的问题我遇到了。
  • 这个答案值得诺贝尔奖。我花了几个小时使用不同的解决方案,我的问题是,我有一个带有视图寻呼机的嵌套滚动视图,在视图寻呼机内部我有一个 Web 视图。谢啦!你拯救了我的一天!
  • 我试过了,它工作正常,但如果我的第三个片段也有 viewpager,它会失败,那么只有第三个片段崩溃了。