从ViewPager Android中删除片段页 [英] Remove Fragment Page from ViewPager in Android

查看:158
本文介绍了从ViewPager Android中删除片段页的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想动态添加和删除从ViewPager碎片,没有任何问题,增加工程,但除去没有按预期工作。

每当我想删除目前的项目,最后一个被删除。

我也试过使用FragmentStatePagerAdapter或适配器的getItemPosition方法返回POSITION_NONE。

我是什么做错了吗?

下面是一个基本的例子:

MainActivity.java

 公共类MainActivity扩展FragmentActivity实现TextProvider {

    私人按钮MADD;
    私人按钮mRemove;
    私人ViewPager mPager;

    私人MyPagerAdapter mAdapter;

    私人的ArrayList<字符串> mEntries =新的ArrayList<字符串>();

    @覆盖
    公共无效的onCreate(包savedInstanceState){
        super.onCreate(savedInstanceState);
        的setContentView(R.layout.main);

        mEntries.add(POS 1);
        mEntries.add(POS 2);
        mEntries.add(POS 3);
        mEntries.add(POS 4);
        mEntries.add(POS 5);

        MADD =(按钮)findViewById(R.id.add);
        mRemove =(按钮)findViewById(R.id.remove);
        mPager =(ViewPager)findViewById(R.id.pager);

        mAdd.setOnClickListener(新OnClickListener(){
            @覆盖
            公共无效的onClick(视图查看){
                addNewItem();
            }
        });

        mRemove.setOnClickListener(新OnClickListener(){
            @覆盖
            公共无效的onClick(视图查看){
                removeCurrentItem();
            }
        });

        mAdapter =新MyPagerAdapter(this.getSupportFragmentManager(),这一点);

        mPager.setAdapter(mAdapter);

    }

    私人无效addNewItem(){
        mEntries.add(新项目);
        mAdapter.notifyDataSetChanged();
    }

    私人无效removeCurrentItem(){
        INT位置= mPager.getCurrentItem();
        mEntries.remove(位置);
        mAdapter.notifyDataSetChanged();
    }

    @覆盖
    公共字符串getTextForPosition(INT位置){
        返回mEntries.get(位置);
    }
    @覆盖
    公众诠释getCount将(){
        返回mEntries.size();
    }


    私有类MyPagerAdapter扩展FragmentPagerAdapter {

        私人TextProvider mProvider;

        公共MyPagerAdapter(FragmentManager FM,TextProvider提供商){
            超(FM);
            this.mProvider =提供商;
        }

        @覆盖
        公共片段的getItem(INT位置){
            返回MyFragment.newInstance(mProvider.getTextForPosition(位置));
        }

        @覆盖
        公众诠释getCount将(){
            返回mProvider.getCount();
        }

    }

}
 

TextProvider.java

 公共接口TextProvider {
    公共字符串getTextForPosition(INT位);
    公众诠释getCount将();
}
 

MyFragment.java

 公共类MyFragment扩展片段{

    私人字符串行文字;

    公共静态MyFragment的newInstance(字符串文本){
        MyFragment F =新MyFragment(文本);
        返回F;
    }

    公共MyFragment(){
    }

    公共MyFragment(字符串文本){
        this.mText =文本;
    }

    @覆盖
    公共查看onCreateView(LayoutInflater充气,容器的ViewGroup,
            捆绑savedInstanceState){

        查看根= inflater.inflate(R.layout.fragment,集装箱,假);

        ((TextView中)root.findViewById(R.id.position))的setText(多行文字)。

        返回根;
    }

}
 

activity_main.xml

 < XML版本=1.0编码=UTF-8&GT?;
< LinearLayout中的xmlns:机器人=htt​​p://schemas.android.com/apk/res/android
    机器人:layout_width =match_parent
    机器人:layout_height =match_parent
    机器人:方向=垂直>

    <按钮
        机器人:ID =@ + ID /加
        机器人:layout_width =match_parent
        机器人:layout_height =WRAP_CONTENT
        机器人:文本=添加新项/>

    <按钮
        机器人:ID =@ + ID /删除
        机器人:layout_width =match_parent
        机器人:layout_height =WRAP_CONTENT
        机器人:文本=删除当前项目/>

    < android.support.v4.view.ViewPager
        机器人:ID =@ + ID /寻呼机
        机器人:layout_width =match_parent
        机器人:layout_height =0dip
        机器人:layout_weight =1/>

< / LinearLayout中>
 

fragment.xml之

 < XML版本=1.0编码=UTF-8&GT?;
< LinearLayout中的xmlns:机器人=htt​​p://schemas.android.com/apk/res/android
    机器人:layout_width =match_parent
    机器人:layout_height =match_parent
    机器人:方向=垂直>

    <的TextView
        机器人:ID =@ + ID /位置
        机器人:layout_width =match_parent
        机器人:layout_height =match_parent
        机器人:重力=中心
        机器人:TEXTSIZE =35SP/>

< / LinearLayout中>
 

解决方案

由劳斯的解决方案是不够的,得到的东西为我工作,因为现有的片段不被破坏掉了。通过这个答案的启发,我发现,解决的办法是覆盖 getItemId(INT位置) FragmentPagerAdapter 赋予了新的唯一ID时出现了一个片段的预期位置的变化。

来源$ C ​​$ C:

 私有类MyPagerAdapter扩展FragmentPagerAdapter {

    私人TextProvider mProvider;
    私人长了baseid = 0;

    公共MyPagerAdapter(FragmentManager FM,TextProvider提供商){
        超(FM);
        this.mProvider =提供商;
    }

    @覆盖
    公共片段的getItem(INT位置){
        返回MyFragment.newInstance(mProvider.getTextForPosition(位置));
    }

    @覆盖
    公众诠释getCount将(){
        返回mProvider.getCount();
    }


    //当notifyDataSetChanged(这就是所谓的)被称为
    @覆盖
    公众诠释getItemPosition(Object对象){
        //刷新当数据集改变了所有的碎片
        返回PagerAdapter.POSITION_NONE;
    }


    @覆盖
    众长getItemId(INT位置){
        //给一个ID从不同位置时的位置已经改变
        返回baseid之后+位置;
    }

    / **
     *通知片段的位置已经改变。
     *创建一个新的ID为每个位置强制片段休闲
     项目已改变* @参数n个
     * /
    公共无效notifyChangeInPosition(INT N){
        //所有previous片段的范围之外后移getItemId返回的ID
        baseid之后+ = getCount将()+ N;
    }
}
 

现在,例如,如果你删除一个选项卡,或做出一些改变的顺序,你应该叫 notifyChangeInPosition(1)之前调用 notifyDataSetChanged (),这将确保所有的片段将被重新创建。

为什么这个解决方案的工作原理

重写getItemPosition():

notifyDataSetChanged()被调用时,适配器调用<$ C $的 notifyChanged()方法C> ViewPager ,它连接到。该 ViewPager 然后检查由适配器的 getItemPosition()为每个项目,除去那些返回项目<$返回的值C $ C> POSITION_NONE (见<一href="https://github.com/android/platform_frameworks_support/blob/master/v4/java/android/support/v4/view/ViewPager.java#L847">source code ),然后重新填充。

重写getItemId():

这是必要的,以prevent从重装时, ViewPager 被重新填充旧片段适配器。你可以很容易理解为什么这个工程通过看<一href="https://github.com/android/platform_frameworks_support/blob/master/v4/java/android/support/v4/app/FragmentPagerAdapter.java#L91">the对于instantiateItem()的源代码code FragmentPagerAdapter

 最后长的itemId = getItemId(位置);

    //我们是否已经有这样的片段?
    字符串名称= makeFragmentName(container.getId()的itemId);
    片段片段= mFragmentManager.findFragmentByTag(名称);
    如果(片段!= NULL){
        如果(调试)Log.v(TAG,附加项#+的itemId +:F =+片段);
        mCurTransaction.attach(片段);
    } 其他 {
        片段=的getItem(位置);
        如果(调试)Log.v(TAG,添加项目#+的itemId +:F =+片段);
        mCurTransaction.add(container.getId(),片段,
                makeFragmentName(container.getId()的itemId));
    }
 

正如你所看到的,的getItem()方法仅在片段管理器发现不存在碎片用相同的ID叫。对我来说,它看起来像旧片段仍连接,即使 notifyDataSetChanged()被称为一个错误,但对于 ViewPager 并明确指出:

  

请注意这个类是目前在早期的设计和开发。该API将在兼容性库的后续更新可能会改变,需要改变应用程序的源$ C ​​$ C时,编译反对新版本。

所以希望这里给出的解决方法将不再需要在支持库的未来版本。

I'm trying to dynamically add and remove Fragments from a ViewPager, adding works without any problems, but removing doesn't work as expected.

Everytime I want to remove the current item, the last one gets removed.

I also tried to use an FragmentStatePagerAdapter or return POSITION_NONE in the adapter's getItemPosition method.

What am I doing wrong?

Here's a basic example:

MainActivity.java

public class MainActivity extends FragmentActivity implements TextProvider {

    private Button mAdd;
    private Button mRemove;
    private ViewPager mPager;

    private MyPagerAdapter mAdapter;

    private ArrayList<String> mEntries = new ArrayList<String>();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mEntries.add("pos 1");
        mEntries.add("pos 2");
        mEntries.add("pos 3");
        mEntries.add("pos 4");
        mEntries.add("pos 5");

        mAdd = (Button) findViewById(R.id.add);
        mRemove = (Button) findViewById(R.id.remove);
        mPager = (ViewPager) findViewById(R.id.pager);

        mAdd.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                addNewItem();
            }
        });

        mRemove.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                removeCurrentItem();
            }
        });

        mAdapter = new MyPagerAdapter(this.getSupportFragmentManager(), this);

        mPager.setAdapter(mAdapter);

    }

    private void addNewItem() {
        mEntries.add("new item");
        mAdapter.notifyDataSetChanged();
    }

    private void removeCurrentItem() {
        int position = mPager.getCurrentItem();
        mEntries.remove(position);
        mAdapter.notifyDataSetChanged();
    }

    @Override
    public String getTextForPosition(int position) {
        return mEntries.get(position);
    }
    @Override
    public int getCount() {
        return mEntries.size();
    }


    private class MyPagerAdapter extends FragmentPagerAdapter {

        private TextProvider mProvider;

        public MyPagerAdapter(FragmentManager fm, TextProvider provider) {
            super(fm);
            this.mProvider = provider;
        }

        @Override
        public Fragment getItem(int position) {
            return MyFragment.newInstance(mProvider.getTextForPosition(position));
        }

        @Override
        public int getCount() {
            return mProvider.getCount();
        }

    }

}

TextProvider.java

public interface TextProvider {
    public String getTextForPosition(int position);
    public int getCount();
}

MyFragment.java

public class MyFragment extends Fragment {

    private String mText;

    public static MyFragment newInstance(String text) {
        MyFragment f = new MyFragment(text);
        return f;
    }

    public MyFragment() {
    }

    public MyFragment(String text) {
        this.mText = text;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {

        View root = inflater.inflate(R.layout.fragment, container, false);

        ((TextView) root.findViewById(R.id.position)).setText(mText);

        return root;
    }

}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <Button
        android:id="@+id/add"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="add new item" />

    <Button
        android:id="@+id/remove"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="remove current item" />

    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="0dip"
        android:layout_weight="1" />

</LinearLayout>

fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/position"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:textSize="35sp" />

</LinearLayout>

解决方案

The solution by Louth was not enough to get things working for me, as the existing fragments were not getting destroyed. Motivated by this answer, I found that the solution is to override the getItemId(int position) method of FragmentPagerAdapter to give a new unique ID whenever there has been a change in the expected position of a Fragment.

Source Code:

private class MyPagerAdapter extends FragmentPagerAdapter {

    private TextProvider mProvider;
    private long baseId = 0;

    public MyPagerAdapter(FragmentManager fm, TextProvider provider) {
        super(fm);
        this.mProvider = provider;
    }

    @Override
    public Fragment getItem(int position) {
        return MyFragment.newInstance(mProvider.getTextForPosition(position));
    }

    @Override
    public int getCount() {
        return mProvider.getCount();
    }


    //this is called when notifyDataSetChanged() is called
    @Override
    public int getItemPosition(Object object) {
        // refresh all fragments when data set changed
        return PagerAdapter.POSITION_NONE;
    }


    @Override
    public long getItemId(int position) {
        // give an ID different from position when position has been changed
        return baseId + position;
    }

    /**
     * Notify that the position of a fragment has been changed.
     * Create a new ID for each position to force recreation of the fragment
     * @param n number of items which have been changed
     */
    public void notifyChangeInPosition(int n) {
        // shift the ID returned by getItemId outside the range of all previous fragments
        baseId += getCount() + n;
    }
}

Now, for example if you delete a single tab or make some change to the order, you should call notifyChangeInPosition(1) before calling notifyDataSetChanged(), which will ensure that all the Fragments will be recreated.

Why this solution works

Overriding getItemPosition():

When notifyDataSetChanged() is called, the adapter calls the notifyChanged() method of the ViewPager which it is attached to. The ViewPager then checks the value returned by the adapter's getItemPosition() for each item, removing those items which return POSITION_NONE (see the source code) and then repopulating.

Overriding getItemId():

This is necessary to prevent the adapter from reloading the old fragment when the ViewPager is repopulating. You can easily understand why this works by looking at the source code for instantiateItem() in FragmentPagerAdapter.

    final long itemId = getItemId(position);

    // Do we already have this fragment?
    String name = makeFragmentName(container.getId(), itemId);
    Fragment fragment = mFragmentManager.findFragmentByTag(name);
    if (fragment != null) {
        if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
        mCurTransaction.attach(fragment);
    } else {
        fragment = getItem(position);
        if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
        mCurTransaction.add(container.getId(), fragment,
                makeFragmentName(container.getId(), itemId));
    }

As you can see, the getItem() method is only called if the fragment manager finds no existing fragments with the same Id. To me it seems like a bug that the old fragments are still attached even after notifyDataSetChanged() is called, but the documentation for ViewPager does clearly state that:

Note this class is currently under early design and development. The API will likely change in later updates of the compatibility library, requiring changes to the source code of apps when they are compiled against the newer version.

So hopefully the workaround given here will not be necessary in a future version of the support library.

这篇关于从ViewPager Android中删除片段页的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆