动态更新ViewPager?

Ixx*_*Ixx 303 android android-fragments android-viewpager

我无法更新ViewPager中的内容.

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

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

@Override
public Fragment getItem(int position) {
    return new MyFragment(context, paramters);
}
Run Code Online (Sandbox Code Playgroud)

这很好用(但据说我不能改变内容).

所以我发现了这个:ViewPager PagerAdapter没有更新View

特别是在其中讨论了方法instantiateItem():

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

所以现在我想实现instantiateItem()才能做到这一点.但我不知道我必须返回那里(return是Object)和getItem(int position)的关系是什么?

目前我正在使用getItem来实例化Fragment,这是错的吗?但是,我是否必须将片段放在实例变量中,或者根本不实现getItem()......?我只是不明白.

我试过阅读参考资料:

  • public abstract Fragment getItem(int position)

    返回与指定位置关联的片段.

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

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

    容器包含将在其中显示页面的View.position要实例化的页面位置.

    返回

    返回表示新页面的Object.这不需要是View,但可以是页面的其他容器.

......但我仍然不明白它们是如何相关的以及我必须做些什么.

这是我的代码.我正在使用支持包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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

Bil*_*ips 589

使用FragmentPagerAdapter或FragmentStatePagerAdapter时,最好只处理getItem()而不是触摸instantiateItem().PagerAdapter上的instantiateItem()- destroyItem()- isViewFromObject()接口是FragmentPagerAdapter用来实现更简单getItem()接口的低级接口.

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

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

这个答案的早期版本错误地使用了FragmentPagerAdapter作为它的例子 - 这将无法工作,因为FragmentPagerAdapter在第一次显示片段后永远不会破坏片段.

我不建议您链接的帖子中提供的解决方法setTag()findViewWithTag()解决方法.正如您所发现的那样,使用setTag()findViewWithTag()不使用片段,所以它不是一个很好的匹配.

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

默认情况下,getItemPosition()返回POSITION_UNCHANGED,表示"此对象很好,不要销毁或删除它".返回POSITION_NONE修复了问题,而是说:"此对象不再是我正在显示的项目,将其删除." 因此它具有删除和重新创建适配器中每个项目的效果.

这是一个完全合法的修复!此修复使notifyDataSetChanged的行为类似于常规适配器而没有视图回收.如果您实施此修复程序并且性能令人满意,那么您将参加比赛.任务完成.

如果您需要更好的性能,可以使用更高级的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;
        }
    }
});
Run Code Online (Sandbox Code Playgroud)

通过此实现,将仅显示显示新标题的片段.显示仍在列表中的标题的任何片段将被移动到列表中的新位置,并且将销毁具有不再在列表中的标题的片段.

如果片段尚未重新创建,但仍需要更新,该怎么办?生命片段的更新最好由片段本身处理.毕竟这是拥有片段的优势 - 它是它自己的控制器.片段可以将侦听器或观察者添加到其他对象中onCreate(),然后将其删除onDestroy(),从而管理更新本身.您不必getItem()像在ListView或其他AdapterView类型的适配器中那样放置所有更新代码.

最后一件事 - 只是因为FragmentPagerAdapter没有破坏片段并不意味着getItemPosition在FragmentPagerAdapter中完全没用.您仍然可以使用此回调来重新排序ViewPager中的片段.但它永远不会从FragmentManager中完全删除它们.

  • 好的,想出了问题 - 你需要使用FragmentStatePagerAdapter而不是FragmentPagerAdapter.FragmentPagerAdapter永远不会从FragmentManager中删除片段,它只会分离并附加它们.查看文档以获取更多信息 - 通常,您只想将FragmentPagerAdapter用于永久性碎片.我用修正编辑了我的例子. (63认同)
  • 你是对的,将适配器的类更改为FragmentStatePagerAdapter修复了我的所有麻烦.谢谢! (11认同)
  • 不起作用,它仍然没有更新任何东西......发布我的代码. (4认同)
  • 即使使用FragmentStatePagerAdapter时返回POSITION_NONE,我也可以看到它删除了我的片段并创建了新实例。但是,新实例会收到带有旧片段状态的saveInstanceState Bundle。如何完全销毁该片段,以便再次创建时Bundle为null? (2认同)

M-W*_*eEh 136

而不是返回的POSITION_NONE距离getItemPosition(),造成众目睽睽娱乐,这样做:

//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);
}
Run Code Online (Sandbox Code Playgroud)

你的片段应该实现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
         }
    }
}
Run Code Online (Sandbox Code Playgroud)

和界面:

public interface UpdateableFragment {
   public void update(UpdateData xyzData);
}
Run Code Online (Sandbox Code Playgroud)

你的数据类:

public class UpdateData {
    //whatever you want here
}
Run Code Online (Sandbox Code Playgroud)

  • 在我工作生涯的过去2.5天里看到的所有东西......这是唯一一个对我有用的东西..各种各样的arigato,谢谢,spaciba,sukran .. n. (3认同)
  • 龙之母!,这就像一个魅力,唯一的解决方案对我有用......很多!! (2认同)

Zia*_*ish 20

对于那些仍然遇到我之前遇到的同样问题的人,当我有一个包含7个片段的ViewPager时.这些片段的默认值是从api服务加载英语内容但这里的问题是我想从设置活动更改语言,在完成设置活动后我希望主活动中的ViewPager刷新片段以匹配用户的语言选择并且如果用户在这里选择阿拉伯语,那么加载阿拉伯语内容,这是我第一次做的工作

1-您必须使用如上所述的FragmentStatePagerAdapter.

2-on mainActivity我重写onResume并执行以下操作

 if (!(mPagerAdapter == null)) {
mPagerAdapter.notifyDataSetChanged();
}
Run Code Online (Sandbox Code Playgroud)

3-i覆盖了mPagerAdapter中的getItemPosition()并使其返回POSITION_NONE.

 @Override
   public int getItemPosition(Object object) {
     return POSITION_NONE;
    }
Run Code Online (Sandbox Code Playgroud)

像魅力一样工作


osc*_*cat 14

我遇到了这个问题,终于在今天解决了,所以我写下了我所学到的东西,我希望它对那些不熟悉Android ViewPager并且像我一样更新的人有所帮助.我FragmentStatePagerAdapter在API级别17使用,目前只有2个片段.我认为必定有些不正确的地方,请指正,谢谢. 在此输入图像描述

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

  2. 在您调用之后viewpager.setAdapter(pageradapter),将getCount()不断调用适配器来构建片段.因此,如果正在加载数据,则getCount()可以返回0,因此您不需要为没有显示的数据创建虚拟片段.

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

  4. 如果数据库和内存中的数据都已更新(直写),或者只更新内存中的数据(写回),或者只更新数据库中的数据.在最后两种情况下,如果数据没有自动从数据库加载到内存(如上所述).在ViewPager和寻呼机转接器只是处理内存中的数据.

  5. 因此,当内存中的数据更新时,我们只需要调用适配器notifyDataSetChanged().由于片段已经创建,因此onItemPosition()将在notifyDataSetChanged(?返回之前调用适配器.没有什么需要做的getItemPosition().然后更新数据.


cza*_*aku 12

在代码中尝试destroyDrawingCache()使用ViewPager notifyDataSetChanged().

  • 同样在这里.非常感谢你.我特别艰难,因为我在浏览器中没有使用`Fragments`.所以谢谢! (3认同)

Sam*_*awy 10

经过挫折小时,尝试所有上述解决方案来克服这个问题,也像其他类似的问题,尝试了许多解决方案,这个这个,所有与我没能解决这个问题,使ViewPager摧毁旧的Fragment,并填补了pager用新Fragment的.我已经解决了以下问题:

1)使ViewPager类扩展FragmentPagerAdapter如下:

 public class myPagerAdapter extends FragmentPagerAdapter {
Run Code Online (Sandbox Code Playgroud)

2)为创建一个项目ViewPager是存储titlefragment如下:

public class PagerItem {
private String mTitle;
private Fragment mFragment;


public PagerItem(String mTitle, Fragment mFragment) {
    this.mTitle = mTitle;
    this.mFragment = mFragment;
}
public String getTitle() {
    return mTitle;
}
public Fragment getFragment() {
    return mFragment;
}
public void setTitle(String mTitle) {
    this.mTitle = mTitle;
}

public void setFragment(Fragment mFragment) {
    this.mFragment = mFragment;
}

}
Run Code Online (Sandbox Code Playgroud)

3)使ViewPager我的FragmentManager实例的构造函数将它存储在我class的下面:

private FragmentManager mFragmentManager;
private ArrayList<PagerItem> mPagerItems;

public MyPagerAdapter(FragmentManager fragmentManager, ArrayList<PagerItem> pagerItems) {
    super(fragmentManager);
    mFragmentManager = fragmentManager;
    mPagerItems = pagerItems;
}
Run Code Online (Sandbox Code Playgroud)

4)创建重新设置该方法adapter通过删除所有以前的用新的数据数据fragmentfragmentManager自身直接使adapter设置新fragment如下又从新的列表:

public void setPagerItems(ArrayList<PagerItem> pagerItems) {
    if (mPagerItems != null)
        for (int i = 0; i < mPagerItems.size(); i++) {
            mFragmentManager.beginTransaction().remove(mPagerItems.get(i).getFragment()).commit();
        }
    mPagerItems = pagerItems;
}
Run Code Online (Sandbox Code Playgroud)

5)从容器中ActivityFragment不用新数据重新初始化适配器.使用以下新数据通过方法设置新数据setPagerItems:

ArrayList<PagerItem> pagerItems = new ArrayList<PagerItem>();
    pagerItems.add(new PagerItem("Fragment1", new MyFragment1()));
    pagerItems.add(new PagerItem("Fragment2", new MyFragment2()));

    mPagerAdapter.setPagerItems(pagerItems);
    mPagerAdapter.notifyDataSetChanged();
Run Code Online (Sandbox Code Playgroud)

我希望它有所帮助.


hor*_*urh 9

由于某种原因,没有一个答案对我有用,所以我必须覆盖restoreState方法而不在我的fragmentStatePagerAdapter中调用super.码:

private class MyAdapter extends FragmentStatePagerAdapter {

    // [Rest of implementation]

    @Override
    public void restoreState(Parcelable state, ClassLoader loader) {}

}
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

263493 次

最近记录:

6 年,5 月 前