动态更新ViewPager?

我无法更新ViewPager中的内容。

一个问题:在FragmentPagerAdapter类中,方法instantiateItem()和getItem()的关系和正确用法是什么?

我只使用getItem()来实例化并返回我的片段:

@Override
public Fragment getItem(int position) {
    return new MyFragment(context, paramters);
}

这运作良好(但据说我不能改变内容)。

所以我发现:ViewPager PagerAdapter没有更新视图

特别是关于方法instantiateItem()的讨论:

“我的方法是对instantiateItem()方法中的任何实例化视图使用setTag()方法”

所以现在我想要实现instantiateItem()。 但我不知道我必须返回那里(返回是Object)以及与getItem(int position)的关系是什么?

目前我使用getItem来实例化片段,这是错误的吗? 但是,那么我必须将片段放入实例变量中,还是不要实现getItem()......? 我只是不明白这一点。

我试着阅读参考文献:

  • public abstract Fragment getItem(int position)

    返回与指定位置关联的碎片。

  • 公共对象instantiateItem(ViewGroup容器,int位置)

    创建给定位置的页面。 适配器负责将视图添加到此处给出的容器,尽管它只能确保在从finishUpdate(ViewGroup)返回时完成此操作。 参数

    容器包含将在其中显示页面的视图。 位置要实例化的页面位置。

    返回

    返回表示新页面的Object。 这不需要是一个视图,但可以是页面的其他容器。

  • ......但我仍不明白他们是如何相关的以及我必须做什么。

    这是我的代码。 我正在使用支持包v4。

    ViewPagerTest

    public class ViewPagerTest extends FragmentActivity {
        private ViewPager pager;
        private MyFragmentAdapter adapter; 
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.pager1);
    
            pager = (ViewPager)findViewById(R.id.slider);
    
            String[] data = {"page1", "page2", "page3", "page4", "page5", "page6"};
    
            adapter = new MyFragmentAdapter(getSupportFragmentManager(), 6, this, data);
            pager.setAdapter(adapter);
    
            ((Button)findViewById(R.id.button)).setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    reload();
                }
            });
        }
    
        private void reload() {
            String[] data = {"changed1", "changed2", "changed3", "changed4", "changed5", "changed6"};
            //adapter = new MyFragmentAdapter(getSupportFragmentManager(), 6, this, data);
            adapter.setData(data);
            adapter.notifyDataSetChanged();
            pager.invalidate();
    
            //pager.setCurrentItem(0);
        }
    }
    

    MyFragmentAdapter

    class MyFragmentAdapter extends FragmentPagerAdapter {
        private int slideCount;
        private Context context;
        private String[] data;
    
        public MyFragmentAdapter(FragmentManager fm, int slideCount, Context context, String[] data) {
            super(fm);
            this.slideCount = slideCount;
            this.context = context;
            this.data = data;
        }
    
        @Override
        public Fragment getItem(int position) {
            return new MyFragment(data[position], context);
        }
    
        @Override
        public int getCount() {
            return slideCount;
        }
    
        public void setData(String[] data) {
            this.data = data;
        }
    
        @Override
        public int getItemPosition(Object object) {
            return POSITION_NONE;
        }
    }
    

    MyFragment

    public final class MyFragment extends Fragment {
        private String text;
    
        public MyFragment(String text, Context context) {
            this.text = text;
        }
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.slide, null);
            ((TextView)view.findViewById(R.id.text)).setText(text);
    
            return view;
        }
    }
    

    这里也有人有类似的问题,没有答案...

    http://www.mail-archive.com/android-developers@googlegroups.com/msg200477.html


    当使用FragmentPagerAdapter或FragmentStatePagerAdapter时,最好仅处理getItem()而不要触碰instantiateItem()isViewFromObject()instantiateItem() - destroyItem() - isViewFromObject()接口是FragmentPagerAdapter用来实现更简单的getItem()接口的低级接口。

    在进入这个之前,我应该澄清一点

    如果要切换显示的实际片段,则需要避免使用FragmentPagerAdapter并使用FragmentStatePagerAdapter。

    这个答案的早期版本在使用FragmentPagerAdapter作为例子时犯了一个错误 - 这是行不通的,因为FragmentPagerAdapter在第一次显示之后从不销毁它。

    我不建议您链接的帖子中提供的setTag()findViewWithTag()解决方法。 正如你发现的,使用setTag()findViewWithTag()不能用于分段,所以它不是一个好的匹配。

    正确的解决方案是重写getItemPosition() 。 当notifyDataSetChanged()被调用,调用ViewPager getItemPosition()在其适配器的所有项目,看他们是否需要移动到不同的位置或删除。

    默认情况下, getItemPosition()返回POSITION_UNCHANGED ,这意味着“这个对象没问题,不要销毁它或删除它。” 返回POSITION_NONE通过改为说“这个对象不再是我正在显示的项目,将其删除”来解决问题。 所以它具有删除和重新创建适配器中的每个项目的效果。

    这是一个完全合法的修复! 此修复程序使notifyDataSetChanged的行为与没有查看回收的常规Adapter类似。 如果您执行此修复程序并且性能令人满意,那么您就可以参加比赛了。 任务完成。

    如果你需要更好的性能,你可以使用一个更好的getItemPosition()实现。 下面是一个寻呼机从字符串列表中创建碎片的例子:

    ViewPager pager = /* get my ViewPager */;
    // assume this actually has stuff in it
    final ArrayList<String> titles = new ArrayList<String>();
    
    FragmentManager fm = getSupportFragmentManager();
    pager.setAdapter(new FragmentStatePagerAdapter(fm) {
        public int getCount() {
            return titles.size();
        }
    
        public Fragment getItem(int position) {
            MyFragment fragment = new MyFragment();
            fragment.setTitle(titles.get(position));
            return fragment;
        }
    
        public int getItemPosition(Object item) {
            MyFragment fragment = (MyFragment)item;
            String title = fragment.getTitle();
            int position = titles.indexOf(title);
    
            if (position >= 0) {
                return position;
            } else {
                return POSITION_NONE;
            }
        }
    });
    

    通过该实施,只显示显示新标题的片段。 显示仍在列表中的标题的任何片段将被移动到其在列表中的新位置,而具有不再在列表中的标题的片段将被销毁。

    如果片段没有被重新创建,但是需要更新呢? 对活动片段的更新最好由片段本身处理。 毕竟,这是有一个片段的优点 - 它是它自己的控制器。 一个片段可以将一个监听器或观察者添加到onCreate()另一个对象,然后在onDestroy()中将其删除,从而自行管理更新。 您不必像在ListView或其他AdapterView类型的适配器中那样将所有更新代码放在getItem()

    最后一件事 - 只是因为FragmentPagerAdapter不销毁一个片段并不意味着getItemPosition在FragmentPagerAdapter中完全没用。 您仍然可以使用此回调在ViewPager中对您的片段进行重新排序。 不过,它永远不会从FragmentManager中完全删除它们。


    而不是从getItemPosition()返回POSITION_NONE并导致完整视图重新创建,请执行以下操作:

    //call this method to update fragments in ViewPager dynamically
    public void update(UpdateData xyzData) {
        this.updateData = xyzData;
        notifyDataSetChanged();
    }
    
    @Override
    public int getItemPosition(Object object) {
        if (object instanceof UpdateableFragment) {
            ((UpdateableFragment) object).update(updateData);
        }
        //don't return POSITION_NONE, avoid fragment recreation. 
        return super.getItemPosition(object);
    }
    

    你的片段应该实现UpdateableFragment接口:

    public class SomeFragment extends Fragment implements
        UpdateableFragment{
    
        @Override
        public void update(UpdateData xyzData) {
             // this method will be called for every fragment in viewpager
             // so check if update is for this fragment
             if(forMe(xyzData)) {
               // do whatever you want to update your UI
             }
        }
    }
    

    和界面:

    public interface UpdateableFragment {
       public void update(UpdateData xyzData);
    }
    

    你的数据类:

    public class UpdateData {
        //whatever you want here
    }
    

    我遇到了这个问题并最终在今天解决了这个问题,所以我写下了我所学到的内容,希望对于Android的ViewPager和我的更新有所帮助。 我在API级别17中使用FragmentStatePagerAdapter ,目前只有2个片段。 我认为一定有不对的地方,请纠正我,谢谢。 在这里输入图像描述

  • 序列化数据必须加载到内存中。 这可以使用CursorLoader / AsyncTask / Thread来完成。 是否自动加载取决于您的代码。 如果你使用的是CursorLoader ,它会自动加载,因为有一个注册数据观察者。

  • 在调用viewpager.setAdapter(pageradapter) ,不断调用适配器的getCount()来构建片段。 因此,如果数据被加载, getCount()可以返回0,因此不需要为显示的数据创建虚拟片段。

  • 加载数据后,由于getCount()仍为0,因此适配器不会自动构建片段,因此我们可以将实际加载的数据编号设置为由getCount()返回,然后调用适配器的notifyDataSetChanged()ViewPager开始通过内存中的数据创建片段(只是前两个片段)。 它在返回notifyDataSetChanged()之前完成。 然后ViewPager拥有你需要的正确的片段。

  • 如果数据库和内存中的数据都更新(通过写入),或者仅更新内存中的数据(写回),或者只更新数据库中的数据。 在最后两种情况下,如果数据不是从数据库自动加载到内存(如上所述)。 ViewPager和寻呼机适配器只处理内存中的数据。

  • 所以当内存中的数据更新时,我们只需要调用适配器的notifyDataSetChanged() 。 既然已经创建了片段,适配器的onItemPosition()将之前被称为notifyDataSetChanged()返回,没有什么需要做getItemPosition()然后数据被更新。

  • 链接地址: http://www.djcxy.com/p/68137.html

    上一篇: Update ViewPager dynamically?

    下一篇: Fragment or Support Fragment?