如何使我的ViewPager一次只加载一页,即设置setOffscreenPageLimit(0);?

44

我知道可以给 setOffscreenPageLimit(int) 的最低数字是1。但是由于内存问题,我需要一次只加载一页。

我是否必须使用旧版本的tabhost等?还是有什么方法/技巧可以让我的viewPager一次只加载一页?

我的适配器扩展了具有ViewHolder模式的BaseAdapter。


请查看以下链接:https://dev59.com/s3XYa4cB1Zd3GeqP2BMs#31531724 - Yuhan Zhang
请查看此答案。它解决了我的问题。 - Gopinath
9个回答

42

我曾经遇到同样的问题,但我找到了解决方案:

步骤:

1)首先从this链接下载CustomViewPager类。

2)按照以下方式使用该类:

在Java中:

CustomViewPager mViewPager;
mViewPager = (CustomViewPager) findViewById(R.id.swipePager);
mViewPager.setOffscreenPageLimit(0);

在 XML 中:

<com.yourpackagename.CustomViewPager 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/swipePager"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

现在每次只会加载一个页面。

P.S: 根据问题的要求,我已经发布了 Viewpager 的解决方案。我还没有尝试过 TabLayout,如果我找到任何解决方案,我会更新答案。

在这个文件中,使用了 KeyEventCompat,但是由于 KeyEnentCompat 类在 API 级别 26.0.0 中已被弃用,因此 Android Studio 可能无法找到它,所以您需要将 KeyEventCompat 替换为 event。更多详情请查看 https://developer.android.com/sdk/support_api_diff/26.0.0-alpha1/changes/android.support.v4.view.KeyEventCompat


5
无法通过TabLayout进行设置,请指教。 - RaRa
2
@RRR_1173,使用任何一般的ViewPager例子,只需将您的ViewPager替换为CustomViewPager即可。您可以在此处找到一般示例:http://www.androidhive.info/2013/10/android-tab-layout-with-swipeable-views-1/您只需要提供CustomClass即可。 - Ronak Thakkar
3
TabLayout无法接受自定义类,因此不能使用“tabLayout.setupWithViewPager(customeViewPager)”这行代码。 - RaRa
2
谢谢你,你救了我的命!!!你应该得到奥斯卡!!我已经努力让它工作了5天!!! - AkshayT
2
我试图扩展CustomViewPager,而不是ViewPager, 但在CustomViewPager中无法解决setPageTransformer方法, 那么如何使用CustomViewPager实现垂直滑动? - Mahesh Cheliya
显示剩余21条评论

19
据我所知,使用ViewPager时不可能实现这一点。至少在您希望页面可以轻松滑动时是不可能的。
因此,解释非常简单:
当您在两个页面之间滑动时,存在一个点,此时两个页面都需要可见,因为在该点上,如果其中一个页面甚至不存在,则无法在两个页面之间滑动。
有关更多信息,请参见此问题:ViewPager.setOffscreenPageLimit(0) doesn't work as expected CommonsWare 在他的 answer 的评论中提供了很好的解释。

2
如果我不想使用ViewPager的滑动功能怎么办?我正在使用NonSwipeableViewPager,就像这样https://github.com/nuuneoi/TheCheeseLibrary/blob/master/src/main/java/com/inthecheesefactory/thecheeselibrary/view/NonSwipeableViewPager.java - John
1
那么就不要使用ViewPager...只需使用带有TabLayout的FragmentPager或FragmentStatePagerAdapter即可。 - Kushan

13

由于内存问题,我需要逐个加载页面。

这意味着您正在遭受OutOfMemoryError错误。

是的,或者使用FragmentTabHost或选项卡操作栏。

不行,因为简单地说,ViewPager需要同时加载多个页面以进行滑动动画。您可以通过使用ViewPager并滑动来查看此情况。

或者,您可以尝试解决感知到的内存问题。假设这个应用程序与您今天早些时候在报告的相同,您只使用了7MB的堆空间。如果剩余的堆高度碎片化,那么这将仅导致OutOfMemoryError。有一些内存管理策略(例如从外部源创建位图的BitmapOptions上的inBitmap),可以帮助解决此类碎片化问题。

我的适配器使用ViewHolder模式扩展了BaseAdapter。

BaseAdapter用于AdapterView而非ViewPager


6
我有一个答案。上述的 setUserVisibleHint() 方法已经被弃用,您可以使用 setMaxLifecycle() 方法。为了仅加载当前可见的片段,您需要在viewpager适配器中将行为设置为 BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT,即在构造函数中设置。用 onResume() 方法处理片段。
这样,您就可以一次只加载一个片段到 viewpager 中。
public static class MyAdapter extends FragmentStatePagerAdapter {
public MyAdapter(FragmentManager fm) {
    super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
}

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

@Override
public Fragment getItem(int position) {
    return ArrayListFragment.newInstance(position);
}

在 Kotlin 中:

class MyAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm,BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT )

也可以在同样的方式下与已经废弃的FragmentPagerAdapter一起使用。

你能详细解释一下吗? - iSrinivasan27
你的回答对我很有帮助,请再多解释一些。 - DeePanShu
这个答案是唯一有用的答案,我们不需要覆盖任何弃用的方法或创建自定义视图。 - Kartik

3

使用这种方法,您可以在带有视图分页器的选项卡布局中一次加载一个页面。

  @Override
    public void onResume() {
        super.onResume();
        if (getUserVisibleHint() && !isVisible) {
            Log.e("~~onResume: ", "::onLatestResume");
           //your code
        }
        isVisible = true;
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);

        if (isVisibleToUser && isVisible) {
            Handler handler = new Handler();
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                   //your code
                }
            }, 500);

        }
    }

`


2

重写setUserVisibleHint方法,并在每个片段中添加以下代码的postDelayed函数。

override fun setUserVisibleHint(isVisibleToUser: Boolean) {
    if (isVisibleToUser)
        Handler().postDelayed({
            if (activity != null) {
                // Do you stuff here 
            }
        }, 200)
    super.setUserVisibleHint(isVisibleToUser)
}

我可以通过这种方式进行管理,现在它对我来说运行良好。

有任何示例吗? - Vishva Vijay

1

首先,复制SmartFragmentStatePagerAdapter.java文件,该文件提供了在ViewPager中智能缓存已注册片段的功能。它通过重写instantiateItem()方法并在内部缓存任何已创建的片段来实现这一点。这解决了需要访问ViewPager中当前项目的常见问题。

现在,我们希望在声明适配器时从上面复制的SmartFragmentStatePagerAdapter进行扩展,以便利用状态页面更好的内存管理:

public abstract class SmartFragmentStatePagerAdapter extends FragmentStatePagerAdapter {
    // Sparse array to keep track of registered fragments in memory
    private SparseArray<Fragment> registeredFragments = new SparseArray<Fragment>();

    public SmartFragmentStatePagerAdapter(FragmentManager fragmentManager) {
        super(fragmentManager);
    }

    // Register the fragment when the item is instantiated
    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Fragment fragment = (Fragment) super.instantiateItem(container, position);
        registeredFragments.put(position, fragment);
        return fragment;
    }

    // Unregister when the item is inactive
    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        registeredFragments.remove(position);
        super.destroyItem(container, position, object);
    }

    // Returns the fragment for the position (if instantiated)
    public Fragment getRegisteredFragment(int position) {
        return registeredFragments.get(position);
    }
}


// Extend from SmartFragmentStatePagerAdapter now instead for more dynamic ViewPager items
    public static class MyPagerAdapter extends SmartFragmentStatePagerAdapter {
    private static int NUM_ITEMS = 3;

        public MyPagerAdapter(FragmentManager fragmentManager) {
            super(fragmentManager);
        }

        // Returns total number of pages
        @Override
        public int getCount() {
            return NUM_ITEMS;
        }

        // Returns the fragment to display for that page
        @Override
        public Fragment getItem(int position) {
            switch (position) {
            case 0: // Fragment # 0 - This will show FirstFragment
                return FirstFragment.newInstance(0, "Page # 1");
            case 1: // Fragment # 0 - This will show FirstFragment different title
                return FirstFragment.newInstance(1, "Page # 2");
            case 2: // Fragment # 1 - This will show SecondFragment
                return SecondFragment.newInstance(2, "Page # 3");
            default:
                return null;
            }
        }

        // Returns the page title for the top indicator
        @Override
        public CharSequence getPageTitle(int position) {
            return "Page " + position;
        }

    }

0

实际上您不需要自定义 ViewPager。

我也遇到过同样的问题,我的解决办法是这样的:

  • setOffscreenPageLimit() 设为 1。
  • 利用片段的 onResumeonPause 生命周期方法。

在这些生命周期方法中初始化和释放内存。


-2

我知道这是一个旧帖子,但我遇到了这个问题,并找到了一个很好的解决方法,如果您正在加载片段。简单地通过重写setUserVisibleHint()来检查用户是否看到该片段,然后加载数据。

    @Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (isVisibleToUser) {
        getData(1, getBaseUrl(), getLink());
    }
}

2
一个片段可以是可见的,但尚未初始化,这种解决方案会导致问题。 - Avamander
你可以随时添加一个布尔检查并从oncreate方法中重新调用它。我已经使用这个解决方案超过一个月了,应用程序运行得很好。 - Ahmed Awad

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