动态更新ViewPager?

本文翻译自:Update ViewPager dynamically?

I can't update the content in ViewPager. 我无法更新ViewPager中的内容。

What is the correct usage of methods instantiateItem() and getItem() in FragmentPagerAdapter class? FragmentPagerAdapter类中的方法instantiateItem()和getItem()的正确用法是什么?

I was using only getItem() to instantiate and return my fragments: 我只使用getItem()实例化并返回我的片段:

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

This worked well. 这很好用。 Except I can't change the content. 除了我无法改变内容。

So I found this: ViewPager PagerAdapter not updating the View 所以我发现了这个: ViewPager PagerAdapter没有更新View

"My approach is to use the setTag() method for any instantiated view in the instantiateItem() method" “我的方法是对instantiateItem()方法中的任何实例化视图使用setTag()方法”

Now I want to implement instantiateItem() to do that. 现在我想实现instantiateItem()来做到这一点。 But I don't know what I have to return (the type is Object) and what is the relation with getItem(int position)? 但是我不知道我要返回什么(类型是Object)以及与getItem(int position)的关系是什么?

I read the reference : 我读了参考资料

  • public abstract Fragment getItem (int position) public abstract Fragment getItem(int position)

    Return the Fragment associated with a specified position. 返回与指定位置关联的片段。

  • public Object instantiateItem (ViewGroup container, int position) public Object instantiateItem(ViewGroup容器,int位置)

    Create the page for the given position. 创建给定位置的页面。 The adapter is responsible for adding the view to the container given here, although it only must ensure this is done by the time it returns from finishUpdate(ViewGroup). 适配器负责将视图添加到此处给出的容器,但它必须确保在从finishUpdate(ViewGroup)返回时完成此操作。 Parameters 参数

    container The containing View in which the page will be shown. 容器包含将在其中显示页面的View。 position The page position to be instantiated. position要实例化的页面位置。

    Returns 返回

    Returns an Object representing the new page. 返回表示新页面的Object。 This does not need to be a View, but can be some other container of the page. 这不需要是View,但可以是页面的其他容器。

but I still don't get it. 但我仍然没有得到它。

Here's my code. 这是我的代码。 I'm using support package v4. 我正在使用支持包v4。

ViewPagerTest 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 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 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;
    }
}

Here is also somebody with a similar problem, no answers http://www.mail-archive.com/android-developers@googlegroups.com/msg200477.html 这里也有类似问题的人,没有答案http://www.mail-archive.com/android-developers@googlegroups.com/msg200477.html


#1楼

参考:https://stackoom.com/question/jWSm/动态更新ViewPager


#2楼

When using FragmentPagerAdapter or FragmentStatePagerAdapter, it is best to deal solely with getItem() and not touch instantiateItem() at all. 使用FragmentPagerAdapter或FragmentStatePagerAdapter时,最好只处理getItem()而不是触及instantiateItem() The instantiateItem() - destroyItem() - isViewFromObject() interface on PagerAdapter is a lower-level interface that FragmentPagerAdapter uses to implement the much simpler getItem() interface. 在PagerAdapter上的instantiateItem() - destroyItem() - isViewFromObject()接口是FragmentPagerAdapter用来实现更简单的getItem()接口的低级接口。

Before getting into this, I should clarify that 在进入这个之前,我应该澄清一下

if you want to switch out the actual fragments that are being displayed, you need to avoid FragmentPagerAdapter and use FragmentStatePagerAdapter. 如果要切换显示的实际片段,则需要避免使用FragmentPagerAdapter并使用FragmentStatePagerAdapter。

An earlier version of this answer made the mistake of using FragmentPagerAdapter for its example - that won't work because FragmentPagerAdapter never destroys a fragment after it's been displayed the first time. 这个答案的早期版本错误地将FragmentPagerAdapter用于它的示例 - 这将无法工作,因为FragmentPagerAdapter在第一次显示片段后从不销毁片段。

I don't recommend the setTag() and findViewWithTag() workaround provided in the post you linked. 我不建议您链接的帖子中提供的setTag()findViewWithTag()变通方法。 As you've discovered, using setTag() and findViewWithTag() doesn't work with fragments, so it's not a good match. 正如您所发现的,使用setTag()findViewWithTag()不能使用片段,因此它不是很好的匹配。

The right solution is to override getItemPosition() . 正确的解决方案是覆盖getItemPosition() When notifyDataSetChanged() is called, ViewPager calls getItemPosition() on all the items in its adapter to see whether they need to be moved to a different position or removed. notifyDataSetChanged()被调用,调用ViewPager getItemPosition()在其适配器的所有项目,看他们是否需要移动到不同的位置或删除。

By default, getItemPosition() returns POSITION_UNCHANGED , which means, "This object is fine where it is, don't destroy or remove it." 默认情况下, getItemPosition()返回POSITION_UNCHANGED ,这表示“此对象很好,不要销毁或删除它”。 Returning POSITION_NONE fixes the problem by instead saying, "This object is no longer an item I'm displaying, remove it." 返回POSITION_NONE修复问题,而是说:“此对象不再是我正在显示的项目,将其删除。” So it has the effect of removing and recreating every single item in your adapter. 因此它具有删除和重新创建适配器中每个项目的效果。

This is a completely legitimate fix! 这是一个完全合法的修复! This fix makes notifyDataSetChanged behave like a regular Adapter without view recycling. 此修复使notifyDataSetChanged的行为类似于常规适配器而没有视图回收。 If you implement this fix and performance is satisfactory, you're off to the races. 如果您实施此修复程序并且性能令人满意,那么您将参加比赛。 Job done. 任务完成。

If you need better performance, you can use a fancier getItemPosition() implementation. 如果您需要更好的性能,可以使用更高级的getItemPosition()实现。 Here's an example for a pager creating fragments off of a list of strings: 以下是分页器从字符串列表中创建片段的示例:

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

With this implementation, only fragments displaying new titles will get displayed. 通过此实现,将仅显示显示新标题的片段。 Any fragments displaying titles that are still in the list will instead be moved around to their new position in the list, and fragments with titles that are no longer in the list at all will be destroyed. 显示仍在列表中的标题的任何片段将被移动到列表中的新位置,并且将销毁具有不再在列表中的标题的片段。

What if the fragment has not been recreated, but needs to be updated anyway? 如果片段尚未重新创建,但仍需要更新,该怎么办? Updates to a living fragment are best handled by the fragment itself. 生命片段的更新最好由片段本身处理。 That's the advantage of having a fragment, after all - it is its own controller. 毕竟这是拥有片段的优势 - 它是它自己的控制器。 A fragment can add a listener or an observer to another object in onCreate() , and then remove it in onDestroy() , thus managing the updates itself. 片段可以将监听器或观察者添加到onCreate()另一个对象,然后在onDestroy()中将其删除,从而自行管理更新。 You don't have to put all the update code inside getItem() like you do in an adapter for a ListView or other AdapterView types. 您不必像在ListView或其他AdapterView类型的适配器中那样将所有更新代码放在getItem()

One last thing - just because FragmentPagerAdapter doesn't destroy a fragment doesn't mean that getItemPosition is completely useless in a FragmentPagerAdapter. 最后一件事 - 只是因为FragmentPagerAdapter没有销毁片段并不意味着getItemPosition在FragmentPagerAdapter中完全没用。 You can still use this callback to reorder your fragments in the ViewPager. 您仍然可以使用此回调来重新排序ViewPager中的片段。 It will never remove them completely from the FragmentManager, though. 但它永远不会从FragmentManager中完全删除它们。


#3楼

在代码中的notifyDataSetChanged()之后,在notifyDataSetChanged()上尝试destroyDrawingCache()


#4楼

Instead of returning POSITION_NONE from getItemPosition() and causing full view recreation, do this: 而不是从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);
}

Your fragments should implement UpdateableFragment interface: 您的片段应该实现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
         }
    }
}

and the interface: 和界面:

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

Your data class: 你的数据类:

public class UpdateData {
    //whatever you want here
}

#5楼

I have encountered this problem and finally solved it today, so I write down what I have learned and I hope it is helpful for someone who is new to Android's ViewPager and update as I do. 我遇到了这个问题,今天终于解决了,所以我写下了我所学到的东西,希望对Android的ViewPager有所帮助并像我一样更新。 I'm using FragmentStatePagerAdapter in API level 17 and currently have just 2 fragments. 我在API级别17中使用FragmentStatePagerAdapter ,目前只有2个片段。 I think there must be something not correct, please correct me, thanks. 我认为必定有些不正确的地方,请指正,谢谢。 在此输入图像描述

  1. Serialized data has to be loaded into memory. 必须将序列化数据加载到内存中。 This can be done using a CursorLoader / AsyncTask / Thread . 这可以使用CursorLoader / AsyncTask / Thread Whether it's automatically loaded depends on your code. 它是否自动加载取决于您的代码。 If you are using a CursorLoader , it's auto-loaded since there is a registered data observer. 如果您使用CursorLoader ,它会自动加载,因为有一个注册的数据观察者。

  2. After you call viewpager.setAdapter(pageradapter) , the adapter's getCount() is constantly called to build fragments. 调用viewpager.setAdapter(pageradapter) ,将不断调用适配器的getCount()来构建片段。 So if data is being loaded, getCount() can return 0, thus you don't need to create dummy fragments for no data shown. 因此,如果正在加载数据, getCount()可以返回0,因此您不需要为没有显示的数据创建虚拟片段。

  3. After the data is loaded, the adapter will not build fragments automatically since getCount() is still 0, so we can set the actually loaded data number to be returned by getCount() , then call the adapter's notifyDataSetChanged() . 加载数据后,适配器将不会自动构建片段,因为getCount()仍为0,因此我们可以设置getCount()返回的实际加载的数据编号,然后调用适配器的notifyDataSetChanged() ViewPager begin to create fragments (just the first 2 fragments) by data in memory. ViewPager开始按内存中的数据创建片段(只是前2个片段)。 It's done before notifyDataSetChanged() is returned. 这是在返回notifyDataSetChanged()之前完成的。 Then the ViewPager has the right fragments you need. 然后, ViewPager拥有您需要的正确片段。

  4. If the data in the database and memory are both updated (write through), or just data in memory is updated (write back), or only data in the database is updated. 如果数据库和内存中的数据都已更新(直写),或者只更新内存中的数据(写回),或者只更新数据库中的数据。 In the last two cases if data is not automatically loaded from the database to memory (as mentioned above). 在最后两种情况下,如果数据没有自动从数据库加载到内存(如上所述)。 The ViewPager and pager adapter just deal with data in memory. ViewPager和寻呼机适配器只处理内存中的数据。

  5. So when data in memory is updated, we just need to call the adapter's notifyDataSetChanged() . 因此,当内存中的数据更新时,我们只需要调用适配器的notifyDataSetChanged() Since the fragment is already created, the adapter's onItemPosition() will be called before notifyDataSetChanged() returns. Nothing needs to be done in getItemPosition() . Then the data is updated. 由于片段已经创建,因此在notifyDataSetChanged()返回之前将调用适配器的onItemPosition() 。在getItemPosition()getItemPosition()任何操作。然后更新数据。


#6楼

I had a similar problem but don't want to trust on the existing solutions (hard coded tag names etc.) and I couldn't make M-WaJeEh's solution work for me. 我有类似的问题,但不想信任现有的解决方案(硬编码标签名称等),我不能让M-WaJeEh的解决方案适合我。 Here is my solution: 这是我的解决方案:

I keep references to the fragments created in getItem in an array. 我保留了对数组中getItem中创建的片段的引用。 This works fine as long as the activity is not destroyed due to configurationChange or lack of memory or whatever (--> when coming back to the activity, fragments return to their last state without 'getItem' being called again and thus without updating the array). 只要活动没有因为configurationChange或缺少内存或其他原因而被破坏,这种方法就可以正常工作了( - >当回到活动时,片段返回到它们的最后状态而没有再次调用'getItem',因此没有更新数组)。

To avoid this problem I implemented instantiateItem(ViewGroup, int) and update my array there, like this: 为了避免这个问题,我实现了instantiateItem(ViewGroup,int)并在那里更新我的数组,如下所示:

        @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Object o = super.instantiateItem(container, position);
        if(o instanceof FragmentX){
            myFragments[0] = (FragmentX)o;
        }else if(o instanceof FragmentY){
            myFragments[1] = (FragmentY)o;
        }else if(o instanceof FragmentZ){
            myFragments[2] = (FragmentZ)o;
        }
        return o;
    }

So, on the one hand I'm happy that I found a solution that works for me and wanted to share it with you, but I also wanted to ask whether somebody else tried something similar and whether there is any reason why I shouldn't do it like that? 所以,一方面我很高兴我找到了一个适合我的解决方案并希望与你分享,但我也想问一下其他人是否尝试了类似的东西以及是否有任何理由我不应该这样做吗? So far it works very good for me... 到目前为止它对我来说非常好......

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值