Android:Viewpager 和 FragmentStatePageAdapter

Posted

技术标签:

【中文标题】Android:Viewpager 和 FragmentStatePageAdapter【英文标题】:Android: Viewpager and FragmentStatePageAdapter 【发布时间】:2011-10-17 23:46:48 【问题描述】:

我正在设计一个允许用户在 ViewPager 中的多个页面之间切换的应用程序。我一直在努力弄清楚如何在屏幕上不再可见时从页面中删除 Fragment 实例,将其缓存(例如,HashMap),然后将其还原,以便当用户翻回该页面,其中的视图和其他所有内容将处于与删除之前相同的状态。例如,我的第一个页面是一个登录屏幕,它使该特定页面上的某些布局元素在成功登录时可见/不可见。当我向前翻了足够多的页面然后翻回第一页时,布局被重置。对于我的另一个页面来说,这成为一个更大的问题,其中包含一个巨大的水平/垂直滚动数据网格,我在初始化时使用后台线程进行绘制。我使用进度对话框来通知用户加载进度,每次我必须加载它时都会变得非常烦人。

所以我做了一些研究......

我浏览了 FragmentStatePageAdapter 的源代码,在 destroyItem() 回调中,被删除的 Fragment 实例的状态被保存到一个 ArrayList 中。当在 instantiateItem() 回调中创建 Fragment 的新实例时,如果项目的实例尚不存在(它们通过使用 ArrayList 来跟踪这一点),则会创建一个新的 Fragment 实例并保存其状态使用相应的 Fragment.SavedState 数据进行初始化。不幸的是,这些数据不包括视图所处的状态,尽管我注意到对于具有 GridView/ListView 的页面,视图的状态以某种方式恢复(如果我滚动到某个随机位置,翻了几页然后回来,它不会被重置)。

根据 API:

保存的状态不能包含对其他片段的依赖—— 那就是它不能使用 putFragment(Bundle, String, Fr​​agment) 来存储 片段引用,因为当这个引用可能无效时 保存状态稍后使用。同样,片段的目标和结果 代码不包含在此状态中。

作为一个 android 新手,我不太确定我是否理解最后一句话。

话虽如此,有没有办法缓存视图状态?如果没有,我想我会继续将所有片段页面留在内存中。

【问题讨论】:

【参考方案1】:

我遇到了同样的问题,通过实现这两个功能解决了

    public void onSaveInstanceState (Bundle outState)
    public void onActivityCreated (Bundle savedInstanceState)

关于我想保存的片段。在第一个函数中,您应该在 Bundle 中保存恢复视图所需的日期(在我的情况下,我有一堆微调器,所以我使用了 一个 int 数组来保存它们的位置)。第二个函数是在恢复片段时调用的,用于实现恢复过程。

我希望这会有所帮助。我还让我的适配器从 FragmentStatePageAdapter 继承,但我不确定这是强制性的。

【讨论】:

需要继承自FragmentStatePagerAdapter,如果只使用FragmentPagerAdapter则不会调用onSaveInstanceState。【参考方案2】:

main.xml 列表

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_
    android:layout_>
    <TextView android:text="Page 1" android:id="@+id/textViewHeader"
        android:layout_ android:layout_
        android:gravity="center" android:padding="10dip" android:textStyle="bold"></TextView>
    <android.support.v4.view.ViewPager
        android:layout_ android:layout_
        android:id="@+id/viewPager" />
</LinearLayout>

设置 ViewPager

ViewPager viewPager = (ViewPager) findViewById(R.id.viewPager);
MyPagerAdapter adapter = new MyPagerAdapter(this);
viewPager.setAdapter(adapter);

PagerAdapter

@Override
public void destroyItem(View view, int arg1, Object object) 
         ((ViewPager) view).removeView((View)object);

@Override
public int getCount() 
          return views.size();

@Override
public Object instantiateItem(View view, int position) 
           View view = views.get(position);
           ((ViewPager) view).addView(view);
           return view;

@Override
public boolean isViewFromObject(View view, Object object) 
           return view == object;

查看这里了解更多详情view pager example

【讨论】:

【参考方案3】:

查看各种文档片段,我最好的猜测是您创建的视图没有附加 ID。假设片段的保存状态是从 Fragment.onSaveInstanceState 创建的,那么片段将自动保存任何具有 id 的视图状态。如果您从布局文件创建它们,您可能有一个与您的 ListView/GridView 关联的默认 ID。您还可以通过调用 setId 将 id 与视图关联。

此外,对于您的自定义填充片段,您可能还需要在 onSaveInstanceState 中执行一些自定义操作。

【讨论】:

【参考方案4】:

这是我如何在 PagerAdapter 中实现缓存的示例。填充缓存后,所有未来的视图请求都从缓存中提供,只有数据被替换。

public class TestPageAdapter extends PagerAdapter

private int MAX_SIZE = 3;
private ArrayList<SoftReference<View>> pageCache = new ArrayList<SoftReference<View>>(3);


public TestPageAdapter(Context context)
    // do some initialization


@Override
public int getCount() 
    // number of pages


private void addToCache(View view)
    if (pageCache.size() < MAX_SIZE)
        pageCache.add(new SoftReference<View>(view));
     else 
        for(int n = (pageCache.size()-1); n >= 0; n--) 
            SoftReference<View> cachedView = pageCache.get(n);
            if (cachedView.get() == null)
                pageCache.set(n, new SoftReference<View>(view));
                return;
            
        
    


private View fetchFromCache()
    for(int n = (pageCache.size()-1);  n>= 0; n--) 
        SoftReference<View> reference = pageCache.remove(n);
        View view = reference.get();
        if (view != null) 
            return view;
        
    
    return null;


@Override
public Object instantiateItem(View collection, int position) 
    View view = fetchFromCache();
    if (view == null) 
        // not in cache, inflate manually
        LayoutInflater inflater = (LayoutInflater) collection.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = inflater.inflate(R.layout.page, null);
     
    setData(view, position);
    ((ViewPager) collection).addView(view, 0);
    return view;         


private void setData(View view, int position)
    // set page data (images, text ....)


public void setPrimaryItem(ViewGroup container, int position, Object object) 
    currentItem = (View)object;


public View getCurrentItem() 
    return currentItem;


@Override
public boolean isViewFromObject(View view, Object object) 
    return view == ((View) object);


@Override
public void destroyItem(View collection, int arg1, Object view) 
    ((ViewPager) collection).removeView((View) view);
    addToCache((View) view);



【讨论】:

【参考方案5】:

我在使用 PagerSlidingTabStrip 并使用 FragmentPagerAdapter 的实例时也遇到了这个问题,切换到 FragmentStatePagerAdapter 确实有效。

然后我使用 onSaveInstanceState() 来保存状态

【讨论】:

以上是关于Android:Viewpager 和 FragmentStatePageAdapter的主要内容,如果未能解决你的问题,请参考以下文章

ViewPager 以编程方式设置当前页面

关于Fragment的懒加载问题

Android ------ ViewPager1和ViewPager2的使用

Android ------ ViewPager1和ViewPager2的使用

Android ------ ViewPager1和ViewPager2的使用

Android ------ ViewPager1和ViewPager2的使用