Xóa trang Fragment khỏi ViewPager trong Android


138

Tôi đang cố gắng tự động thêm và xóa Mảnh vỡ khỏi ViewPager, thêm các tác phẩm mà không gặp sự cố nào, nhưng loại bỏ không hoạt động như mong đợi.

Mỗi lần tôi muốn xóa mục hiện tại, mục cuối cùng sẽ bị xóa.

Tôi cũng đã thử sử dụng FragmentStatePagerAd CHƯƠNG hoặc trả về POSITION_NONE trong phương thức getItemPocation của bộ điều hợp.

Tôi đang làm gì sai?

Đây là một ví dụ cơ bản:

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>

mảnh vỡ

<?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>

2
+1 cho mã nguồn
wizurd

Là chính xác để sử dụng constructor không mặc định trong MyFragment.java??
codeKiller

Câu trả lời:


95

Giải pháp của Louth là không đủ để khiến mọi thứ hoạt động với tôi, vì những mảnh vỡ hiện tại không bị phá hủy. Được thúc đẩy bởi câu trả lời này , tôi thấy rằng giải pháp là ghi đè getItemId(int position)phương thức FragmentPagerAdaptercung cấp ID duy nhất mới bất cứ khi nào có sự thay đổi ở vị trí mong đợi của Mảnh vỡ.

Mã nguồn:

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

Bây giờ, ví dụ nếu bạn xóa một tab duy nhất hoặc thực hiện một số thay đổi đối với đơn hàng, bạn nên gọi notifyChangeInPosition(1)trước khi gọi notifyDataSetChanged(), điều này sẽ đảm bảo rằng tất cả các Mảnh vỡ sẽ được tạo lại.

Tại sao giải pháp này hoạt động

Ghi đè getItemP vị trí ():

Khi notifyDataSetChanged()được gọi, bộ điều hợp gọi notifyChanged()phương thức ViewPagermà nó được gắn vào. Sau ViewPagerđó kiểm tra giá trị được trả về bởi bộ điều hợp getItemPosition()cho từng mục, loại bỏ các mục trả về POSITION_NONE(xem mã nguồn ) và sau đó phục hồi.

Ghi đè getItemId ():

Điều này là cần thiết để ngăn chặn bộ điều hợp tải lại đoạn cũ khi ViewPagerđang hồi sinh. Bạn có thể dễ dàng hiểu lý do tại sao điều này hoạt động bằng cách xem mã nguồn cho InstantiateItem () trong 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));
    }

Như bạn có thể thấy, getItem()phương thức chỉ được gọi nếu trình quản lý phân đoạn không tìm thấy đoạn nào có cùng Id. Đối với tôi có vẻ như một lỗi mà các mảnh cũ vẫn được đính kèm ngay cả sau khi notifyDataSetChanged()được gọi, nhưng tài liệu cho ViewPagerthấy rõ rằng:

Lưu ý lớp này hiện đang được thiết kế và phát triển sớm. API có thể sẽ thay đổi trong các bản cập nhật sau của thư viện tương thích, yêu cầu thay đổi mã nguồn của ứng dụng khi chúng được biên dịch so với phiên bản mới hơn.

Vì vậy, hy vọng cách giải quyết được đưa ra ở đây sẽ không cần thiết trong phiên bản tương lai của thư viện hỗ trợ.


1
Cảm ơn rất nhiều cho câu trả lời này! Thật là mệt mỏi khi cố gắng giải quyết vấn đề này mà không sử dụng FragmentStatePagerAd CHƯƠNG. Giải thích tuyệt vời quá. Bạn đã tìm được cách để getItem kích hoạt khi quay lại đoạn đó chưa? Tôi đã thử sử dụng thông báo thông minh của bạnDataSetChanged () ở nhiều nơi (ví dụ như onStop) nhưng không thành công
u2tall

Tôi thực sự không hiểu câu hỏi của bạn ... Vui lòng gửi câu hỏi mới bao gồm mã nguồn nếu bạn không thể tìm ra câu hỏi.
Tim Rae

1
Năm 2016, cách giải quyết này vẫn được yêu cầu thực hiện notifyDataSetChanged()để làm việc.
Subin Sebastian

Ồ Giải thích tuyệt vời. Tôi đã có một trình xem khá phức tạp khi tôi gỡ bỏ và chèn các mục một cách linh hoạt và sẽ không làm cho nó hoạt động mà không hiểu tất cả điều này.
rupps

Vẫn cần cách giải quyết này để thay thế đoạn cũ. Đằng đó.
kopikaokao

291

ViewPager không xóa các đoạn của bạn bằng mã ở trên vì nó tải một số khung nhìn (hoặc các đoạn trong trường hợp của bạn) vào bộ nhớ. Ngoài chế độ xem hiển thị, nó cũng tải chế độ xem sang hai bên của chế độ hiển thị. Điều này cung cấp khả năng cuộn mượt mà từ chế độ xem sang chế độ xem khiến cho ViewPager trở nên rất tuyệt.

Để đạt được hiệu quả bạn muốn, bạn cần làm một vài điều.

  1. Thay đổi FragmentPagerAd CHƯƠNG thành FragmentStatePagerAdRAM. Lý do cho điều này là vì FragmentPagerAd CHƯƠNG sẽ giữ tất cả các chế độ xem mà nó tải vào bộ nhớ mãi mãi. Trường hợp FragmentStatePagerAd CHƯƠNG xử lý các chế độ xem nằm ngoài các chế độ xem hiện tại và có thể duyệt được.

  2. Ghi đè phương thức bộ điều hợp getItemP vị trí (hiển thị bên dưới). Khi chúng tôi gọi mAdapter.notifyDataSetChanged();ViewPager thẩm vấn bộ điều hợp để xác định những gì đã thay đổi về mặt định vị. Chúng tôi sử dụng phương pháp này để nói rằng mọi thứ đã thay đổi để xử lý lại tất cả định vị chế độ xem của bạn.

Và đây là mã ...

private class MyPagerAdapter extends FragmentStatePagerAdapter {

    //... your existing code

    @Override
    public int getItemPosition(Object object){
        return PagerAdapter.POSITION_NONE;
    }

}

1
Ok, cái này có vẻ là cách giải quyết dễ dàng nhất cho vấn đề này. Mặc dù một số cách giải quyết khác được thảo luận về các báo cáo lỗi tại đây: code.google.com/p/android/issues/detail?id=19110 và tại đây: code.google.com/p/android/issues/detail?id= 19001
Subin Sebastian

@Louth: Tôi đang sử dụng ViewPager với TabAdAd, được mở rộng từ FragmentStatePagerAd Module (ví dụ trên trang ref của Google: developer.android.com/reference/android/support/v4/view/ tựa ). Câu hỏi tiếp theo của tôi là: làm thế nào để bạn xóa một tab ở nơi đầu tiên? Cảm ơn.
gcl1

4
@Louth Khi chúng tôi gọi notifyDataSetChanged () trình xem sẽ tạo ra các đoạn mới, nhưng các phần cũ vẫn còn trong bộ nhớ. Và vấn đề của tôi là; họ nhận được một cuộc gọi cho sự kiện onOptionsItemSelected của họ. Làm thế nào tôi có thể thoát khỏi những mảnh vỡ cũ?
thánh

1
Cảm ơn bạn, chỉ cần thay thế FragmentPagerAd CHƯƠNG thành FragmentStatePagerAd CHƯƠNG đã giải quyết vấn đề của tôi. Sau đó, tôi chỉ cần gỡ bỏ AllViews () trên ViewPager và tải lại các đoạn một cách linh hoạt.
Michal

17
Tại sao bạn luôn trả về POSITION_NONE thay vì thực sự tìm kiếm nếu một khung nhìn / đoạn / đối tượng cụ thể vẫn tồn tại và hợp lệ? Phương pháp này được cho là để trả lời câu hỏi "này, tôi có thể sử dụng lại cái này không?" - và luôn luôn nói "Không!" chỉ có nghĩa là giải trí của tất cả mọi thứ! Điều này là không cần thiết trong nhiều trường hợp.
Zordid

14

giải pháp làm việc của tôi để xóa trang phân đoạn khỏi chế độ xem

public class MyFragmentAdapter extends FragmentStatePagerAdapter {

    private ArrayList<ItemFragment> pages;

    public MyFragmentAdapter(FragmentManager fragmentManager, ArrayList<ItemFragment> pages) {
        super(fragmentManager);
        this.pages = pages;
    }

    @Override
    public Fragment getItem(int index) {
        return pages.get(index);
    }

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

    @Override
    public int getItemPosition(Object object) {
        int index = pages.indexOf (object);

        if (index == -1)
            return POSITION_NONE;
        else
            return index;
    }
}

Và khi tôi cần xóa một số trang theo chỉ mục, tôi làm điều này

pages.remove(position); // ArrayList<ItemFragment>
adapter.notifyDataSetChanged(); // MyFragmentAdapter

Đây là khởi tạo bộ chuyển đổi của tôi

MyFragmentAdapter adapter = new MyFragmentAdapter(getSupportFragmentManager(), pages);
viewPager.setAdapter(adapter);

Bạn sẽ vui lòng chia sẻ mã để thêm đoạn?
VVB

Nhưng trong khi thêm nó phản ánh chậm sau khi vuốt hai lần / ba lần
VVB

nếu bạn không xóa các mục, nó chỉ trả lại mục, không có gì nữa ... bạn đang thử nghiệm trên vm hoặc thiết bị thực?
Vasil Valchev

Hãy thử các giải pháp khác, câu trả lời trắng hơn phiếu bầu. Xem máy nhắn tin có hệ thống rút tiền rất phức tạp để thực hiện tốt hơn trên swip, do đó, việc hack này không được xử lý bởi mục đích xem máy nhắn tin ban đầu ...
Vasil Valchev

11

Đoạn phải được xóa nhưng vấn đề là trạng thái lưu viewpager

Thử

myViewPager.setSaveFromParentEnabled(false);

Không có gì làm việc nhưng điều này đã giải quyết vấn đề!

Chúc mừng!


2

Tôi có ý tưởng chỉ cần sao chép mã nguồn từ android.support.v4.app.FragmentPagerAdpatervào một lớp tùy chỉnh có tên CustumFragmentPagerAdapter. Điều này đã cho tôi cơ hội sửa đổi instantiateItem(...)để mỗi lần nó được gọi, nó sẽ loại bỏ / phá hủy đoạn hiện tại được đính kèm trước khi nó thêm đoạn mới nhận được từ getItem()phương thức.

Chỉ cần sửa đổi instantiateItem(...)theo cách sau:

@Override
public Object instantiateItem(ViewGroup container, int position) {
    if (mCurTransaction == null) {
        mCurTransaction = mFragmentManager.beginTransaction();
    }
    final long itemId = getItemId(position);

    // Do we already have this fragment?
    String name = makeFragmentName(container.getId(), itemId);
    Fragment fragment = mFragmentManager.findFragmentByTag(name);

    // remove / destroy current fragment
    if (fragment != null) {
        mCurTransaction.remove(fragment);
    }

    // get new fragment and add it
    fragment = getItem(position);
    mCurTransaction.add(container.getId(), fragment,    makeFragmentName(container.getId(), itemId));

    if (fragment != mCurrentPrimaryItem) {
        fragment.setMenuVisibility(false);
        fragment.setUserVisibleHint(false);
    }

    return fragment;
}

1

Bạn có thể kết hợp cả hai để tốt hơn:

private class MyPagerAdapter extends FragmentStatePagerAdapter {

    //... your existing code

    @Override
    public int getItemPosition(Object object){

      if(Any_Reason_You_WantTo_Update_Positions) //this includes deleting or adding pages
 return PagerAdapter.POSITION_NONE;
    }
else
return PagerAdapter.POSITION_UNCHANGED; //this ensures high performance in other operations such as editing list items.

}

Điều này chỉ hoạt động nếu bạn đang xóa tab ngoài cùng bên phải, để xóa tab bên trong hoặc tab bên trái, bạn phải sắp xếp lại các tab và trả lại vị trí mới như đã nêu trong tài liệu developer.android.com/reference/android/support/v4 / view /
Bắn

1

Dành cho độc giả tương lai!

Bây giờ bạn có thể sử dụng ViewPager2 để tự động thêm, xóa đoạn khỏi trình xem.

Trích dẫn tham chiếu API biểu mẫu

ViewPager2 thay thế ViewPager, giải quyết hầu hết các điểm đau của người tiền nhiệm, bao gồm hỗ trợ bố trí từ phải sang trái, hướng dọc, bộ sưu tập Fragment có thể sửa đổi, v.v.

Hãy xem MutableCollectionFragmentActivity.kttrong googlesample / android-viewpager2 để biết ví dụ về việc thêm, xóa các đoạn động từ trình xem.


Để biết thông tin của bạn:

Bài viết:

Tham chiếu API

Ghi chú phát hành

Mẫu Repo: https://github.com/googlesamples/android-viewpager2


Tôi có 2 phần chính và phần khác. Tôi đang thêm phần khác sau mỗi vị trí thứ 5 và nó không nằm trong danh sách mảng, vậy làm cách nào để loại bỏ phần khác đó nếu nó không có trong danh sách? Tôi đang sử dụng viewpager2
b devloper

0

Câu trả lời của Louth hoạt động tốt. Nhưng tôi không nghĩ rằng luôn luôn trả lại POSITION_NONE là một ý tưởng hay. Bởi vì POSITION_NONE có nghĩa là đoạn đó sẽ bị hủy và một đoạn mới sẽ được tạo. Bạn có thể kiểm tra xem trong hàm dataInChanged trong mã nguồn của ViewPager.

        if (newPos == PagerAdapter.POSITION_NONE) {
            mItems.remove(i);
            i--;
            ... not related code
            mAdapter.destroyItem(this, ii.position, ii.object);

Vì vậy, tôi nghĩ rằng bạn tốt hơn nên sử dụng một danh sách mảng của yếu tố chính để lưu tất cả các đoạn bạn đã tạo. Và khi bạn thêm hoặc xóa một số trang, bạn có thể có được vị trí phù hợp từ danh sách mảng của riêng bạn.

 public int getItemPosition(Object object) {
    for (int i = 0; i < mFragmentsReferences.size(); i ++) {
        WeakReference<Fragment> reference = mFragmentsReferences.get(i);
        if (reference != null && reference.get() != null) {
            Fragment fragment = reference.get();
            if (fragment == object) {
                return i;
            }
        }
    }
    return POSITION_NONE;
}

Theo các bình luận, getItemPocation được gọi khi chế độ xem máy chủ đang cố xác định xem vị trí của một mục có thay đổi hay không. Và giá trị trả về có nghĩa là vị trí mới của nó.

Nhưng điều này là không đủ. Chúng tôi vẫn còn một bước quan trọng để thực hiện. Trong mã nguồn của FragmentStatePagerAd CHƯƠNG, có một mảng có tên "mFragments" lưu trữ các đoạn không bị phá hủy. Và trong chức năng tức thì.

if (mFragments.size() > position) {
        Fragment f = mFragments.get(position);
        if (f != null) {
            return f;
        }
    }

Nó trả về đoạn được lưu trữ trực tiếp khi nó thấy rằng đoạn được lưu trữ không phải là null. Vì vậy, có một vấn đề. Từ ví dụ, hãy xóa một trang ở vị trí 2, Thứ nhất, Chúng tôi xóa đoạn đó khỏi danh sách mảng tham chiếu của riêng chúng tôi. vì vậy, trong getItemPocation, nó sẽ trả về POSITION_NONE cho đoạn đó và sau đó đoạn đó sẽ bị hủy và xóa khỏi "mFragments".

 mFragments.set(position, null);

Bây giờ đoạn ở vị trí 3 sẽ ở vị trí 2. Và ngay lập tức, nó với vị trí param 3 sẽ được gọi. Tại thời điểm này, mục thứ ba trong "mFramgents" không phải là null, vì vậy nó sẽ trả về trực tiếp. Nhưng thực ra những gì nó trả về là đoạn ở vị trí 2. Vì vậy, khi chúng ta chuyển sang trang 3, chúng ta sẽ tìm thấy một trang trống ở đó.

Để giải quyết vấn đề này. Lời khuyên của tôi là bạn có thể sao chép mã nguồn của FragmentStatePagerAdOG vào dự án của riêng bạn và khi bạn thêm hoặc xóa các hoạt động, bạn nên thêm và xóa các thành phần trong danh sách mảng "mFragments".

Mọi thứ sẽ đơn giản hơn nếu bạn chỉ sử dụng PagerAd CHƯƠNG thay vì FragmentStatePagerAd CHƯƠNG. Chúc may mắn.


0

thêm hoặc loại bỏ đoạn trong viewpager một cách linh hoạt.
Gọi setupViewPager (viewPager) khi bắt đầu hoạt động. Để tải đoạn khác nhau, hãy gọi setupViewPagerCustom (viewPager). ví dụ: trên nút bấm gọi: setupViewPagerCustom (viewPager);

    private void setupViewPager(ViewPager viewPager)
{
    ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
    adapter.addFrag(new fragmnet1(), "HOME");
    adapter.addFrag(new fragmnet2(), "SERVICES");

    viewPager.setAdapter(adapter);
}

private void setupViewPagerCustom(ViewPager viewPager)
{

    ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());

    adapter.addFrag(new fragmnet3(), "Contact us");
    adapter.addFrag(new fragmnet4(), "ABOUT US");


    viewPager.setAdapter(adapter);
}

//Viewpageradapter, handles the views

static class ViewPagerAdapter extends FragmentStatePagerAdapter
{
    private final List<Fragment> mFragmentList = new ArrayList<>();
    private final List<String> mFragmentTitleList = new ArrayList<>();

    public ViewPagerAdapter(FragmentManager manager){
        super(manager);
    }

    @Override
    public Fragment getItem(int position) {
        return mFragmentList.get(position);
    }

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

    @Override
    public int getItemPosition(Object object){
        return PagerAdapter.POSITION_NONE;
    }

    public void addFrag(Fragment fragment, String title){
        mFragmentList.add(fragment);
        mFragmentTitleList.add(title);
    }

    @Override
    public CharSequence getPageTitle(int position){
        return mFragmentTitleList.get(position);
    }
}

0

Tôi đã có một số vấn đề với FragmentStatePagerAdapter. Sau khi xóa một mục:

  • có một vật phẩm khác được sử dụng cho một vị trí (một vật phẩm không thuộc về vị trí đó nhưng thuộc về một vị trí bên cạnh nó)
  • hoặc một số đoạn không được tải (chỉ có nền trống hiển thị trên trang đó)

Sau rất nhiều thí nghiệm, tôi đã đưa ra giải pháp sau đây.

public class SomeAdapter extends FragmentStatePagerAdapter {

    private List<Item> items = new ArrayList<Item>();

    private boolean removing;

    @Override
    public Fragment getItem(int position) {
        ItemFragment fragment = new ItemFragment();
        Bundle args = new Bundle();
        // use items.get(position) to configure fragment
        fragment.setArguments(args);
        return fragment;
    }

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

    @Override
    public int getItemPosition(Object object) {
        if (removing) {
            return PagerAdapter.POSITION_NONE;
        }

        Item item = getItemOfFragment(object);

        int index = items.indexOf(item);
        if (index == -1) {
            return POSITION_NONE;
        } else {
            return index;
        }
    }

   public void addItem(Item item) {
        items.add(item);
        notifyDataSetChanged();
    }

    public void removeItem(int position) {
        items.remove(position);

        removing = true;
        notifyDataSetChanged();
        removing = false;
    }
}

Giải pháp này chỉ sử dụng hack trong trường hợp xóa một mục. Mặt khác (ví dụ: khi thêm một mục), nó vẫn giữ được sự sạch sẽ và hiệu suất của mã gốc.

Tất nhiên, từ bên ngoài bộ chuyển đổi, bạn chỉ gọi addItem / removeItem, không cần gọi notifyDataSetChanged ().


0

Hãy thử giải pháp này. Tôi đã sử dụng databinding để xem ràng buộc. Bạn có thể sử dụng hàm "findViewById ()" phổ biến.

public class ActCPExpense extends BaseActivity implements View.OnClickListener,  {
private static final String TAG = ActCPExpense.class.getSimpleName();
private Context mContext;
private ActCpLossBinding mBinding;

private ViewPagerAdapter adapter;



@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    try {
        setContentView(R.layout.act_cp_loss);
        mBinding = DataBindingUtil.setContentView(this, R.layout.act_cp_loss);
        mContext = ActCPExpense.this;
        initViewsAct();


    } catch (Exception e) {
        LogUtils.LOGE(TAG, e);
    }

}


private void initViewsAct() {

    adapter = new ViewPagerAdapter(getSupportFragmentManager());
    adapter.addFragment(FragmentCPPayee.newInstance(), "Title");
    mBinding.viewpager.setAdapter(adapter);
    mBinding.tab.setViewPager(mBinding.viewpager);



}



@Override
public boolean onOptionsItemSelected(MenuItem itemActUtility) {
    int i = itemActUtility.getItemId();
    if (i == android.R.id.home) {
        onBackPressed();

    } 

    return super.onOptionsItemSelected(itemActUtility);
}

@Override
public void onClick(View view) {
    super.onClick(view);
    int id = view.getId();
    if (id == R.id.btnAdd) {
        addFragment();

    } else if (id == R.id.btnDelete) {
        removeFragment();

    }


}
    private void addFragment(){
    adapter.addFragment(FragmentCPPayee.newInstance("Title");
    adapter.notifyDataSetChanged();
    mBinding.tab.setViewPager(mBinding.viewpager);
}
private void removeFragment(){
    adapter.removeItem(mBinding.viewpager.getCurrentItem());
    mBinding.tab.setViewPager(mBinding.viewpager);
}


class ViewPagerAdapter extends FragmentStatePagerAdapter {
    private final List<Fragment> mFragmentList = new ArrayList<>();
    private final List<String> mFragmentTitleList = new ArrayList<>();


    public ViewPagerAdapter(FragmentManager manager) {
        super(manager);
    }

    @Override
    public int getItemPosition(@NonNull Object object) {
        return PagerAdapter.POSITION_NONE;
    }


    @Override
    public Fragment getItem(int position) {
        return mFragmentList.get(position);
    }

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


    public void addFragment(Fragment fragment, String title) {
        mFragmentList.add(fragment);
        mFragmentTitleList.add(title);

    }

    public void removeItem(int pos) {

        destroyItem(null, pos, mFragmentList.get(pos));
        mFragmentList.remove(pos);
        mFragmentTitleList.remove(pos);
        adapter.notifyDataSetChanged();
        mBinding.viewpager.setCurrentItem(pos - 1, false);
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return "Title " + String.valueOf(position + 1);
    }
}


}

0

Tôi đã thêm một chức năng "ClearFragments" và tôi đã sử dụng chức năng đó để xóa bộ điều hợp trước khi thiết lập các đoạn mới. Điều này gọi các hành động loại bỏ thích hợp của Fragment. Lớp pagerAdOG của tôi:

private class ChartPagerAdapter extends FragmentPagerAdapter{
    private ArrayList<Fragment> fragmentList;

    ChartPagerAdapter(FragmentManager fm){
        super(fm);
        fragmentList = new ArrayList<>();
    }

    void setFragments(ArrayList<? extends Fragment> fragments){
        fragmentList.addAll(fragments);
    }

    void clearFragments(){
        for(Fragment fragment:fragmentList)
            getChildFragmentManager().beginTransaction().remove(fragment).commit();
        fragmentList.clear();
    }

    @Override
    public Fragment getItem(int i) {
        return fragmentList.get(i);
    }

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

}

0

tôi đã giải quyết vấn đề này bằng các bước này

1- sử dụng FragmentPagerAd CHƯƠNG

2- trong mỗi phân đoạn tạo một id ngẫu nhiên

fragment.id = new Random().nextInt();

3- ghi đè getItemP vị trí trong bộ điều hợp

@Override
public int getItemPosition(@NonNull Object object) {
    return PagerAdapter.POSITION_NONE;
}

4 ghi đè getItemId trong bộ chuyển đổi

 @Override
public long getItemId(int position) {
    return mDatasetFragments.get(position).id;
}

5- bây giờ xóa mã là

 adapter.mDatasetFragments.remove(< item to delete position >);
 adapter.notifyDataSetChanged();

điều này làm việc cho tôi tôi hy vọng sự giúp đỡ


0

Mã phiên bản cuối cùng của tôi, đã sửa TẤT CẢ lỗi. Tôi mất 3 ngày

Cập nhật 2020/07/18: Tôi đã thay đổi rất nhiều mã nguồn và sửa rất nhiều lỗi, nhưng tôi không hứa là nó vẫn hoạt động cho đến ngày hôm nay.

https://github.com/lin1987www/FragmentBuilder/blob/master/commonL Library/src/main/java/android/support/v4/app/FragmentStatePagerAd ModuleFix.java

public class FragmentStatePagerAdapterFix extends PagerAdapter {
    private static final String TAG = FragmentStatePagerAdapterFix.class.getSimpleName();
    private static final boolean DEBUG = false;

    private WeakReference<FragmentActivity> wrFragmentActivity;
    private WeakReference<Fragment> wrParentFragment;
    private final FragmentManager mFragmentManager;
    private FragmentTransaction mCurTransaction = null;

    protected ArrayList<Fragment> mFragments = new ArrayList<>();
    protected ArrayList<FragmentState> mFragmentStates = new ArrayList<>();
    protected ArrayList<String> mFragmentTags = new ArrayList<>();
    protected ArrayList<String> mFragmentClassNames = new ArrayList<>();
    protected ArrayList<Bundle> mFragmentArgs = new ArrayList<>();

    private Fragment mCurrentPrimaryItem = null;
    private boolean[] mTempPositionChange;

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

    public FragmentActivity getFragmentActivity() {
        return wrFragmentActivity.get();
    }

    public Fragment getParentFragment() {
        return wrParentFragment.get();
    }

    public FragmentStatePagerAdapterFix(FragmentActivity activity) {
        mFragmentManager = activity.getSupportFragmentManager();
        wrFragmentActivity = new WeakReference<>(activity);
        wrParentFragment = new WeakReference<>(null);
    }

    public FragmentStatePagerAdapterFix(Fragment fragment) {
        mFragmentManager = fragment.getChildFragmentManager();
        wrFragmentActivity = new WeakReference<>(fragment.getActivity());
        wrParentFragment = new WeakReference<>(fragment);
    }

    public void add(Class<? extends android.support.v4.app.Fragment> fragClass) {
        add(fragClass, null, null);
    }

    public void add(Class<? extends android.support.v4.app.Fragment> fragClass, Bundle args) {
        add(fragClass, args, null);
    }

    public void add(Class<? extends android.support.v4.app.Fragment> fragClass, String tag) {
        add(fragClass, null, tag);
    }

    public void add(Class<? extends android.support.v4.app.Fragment> fragClass, Bundle args, String tag) {
        add(fragClass, args, tag, getCount());
    }

    public void add(Class<? extends android.support.v4.app.Fragment> fragClass, Bundle args, String tag, int position) {
        mFragments.add(position, null);
        mFragmentStates.add(position, null);
        mFragmentTags.add(position, tag);
        mFragmentClassNames.add(position, fragClass.getName());
        mFragmentArgs.add(position, args);
        mTempPositionChange = new boolean[getCount()];
    }

    public void remove(int position) {
        if (position < getCount()) {
            mTempPositionChange = new boolean[getCount()];
            for (int i = position; i < mTempPositionChange.length; i++) {
                mTempPositionChange[i] = true;
            }
            mFragments.remove(position);
            mFragmentStates.remove(position);
            mFragmentTags.remove(position);
            mFragmentClassNames.remove(position);
            mFragmentArgs.remove(position);
        }
    }

    public void clear(){
        mFragments.clear();
        mFragmentStates.clear();
        mFragmentTags.clear();
        mFragmentClassNames.clear();
        mFragmentArgs.clear();
    }

    @Override
    public void startUpdate(ViewGroup container) {
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Fragment fragment;
        // If we already have this item instantiated, there is nothing
        // to do.  This can happen when we are restoring the entire pager
        // from its saved state, where the fragment manager has already
        // taken care of restoring the fragments we previously had instantiated.
        fragment = mFragments.get(position);
        if (fragment != null) {
            return fragment;
        }
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
        FragmentState fs = mFragmentStates.get(position);
        if (fs != null) {
            fragment = fs.instantiate(getFragmentActivity(), getParentFragment());
            // Fix bug
            // http://stackoverflow.com/questions/11381470/classnotfoundexception-when-unmarshalling-android-support-v4-view-viewpagersav
            if (fragment.mSavedFragmentState != null) {
                fragment.mSavedFragmentState.setClassLoader(fragment.getClass().getClassLoader());
            }
        }
        if (fragment == null) {
            fragment = Fragment.instantiate(getFragmentActivity(), mFragmentClassNames.get(position), mFragmentArgs.get(position));
        }
        if (DEBUG) {
            Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
        }
        fragment.setMenuVisibility(false);
        fragment.setUserVisibleHint(false);
        mFragments.set(position, fragment);
        mFragmentStates.set(position, null);
        mCurTransaction.add(container.getId(), fragment, mFragmentTags.get(position));
        return fragment;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        Fragment fragment = (Fragment) object;
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
        if (DEBUG) {
            Log.v(TAG, "Removing item #" + position + ": f=" + object
                    + " v=" + ((Fragment) object).getView());
        }
        if (position < getCount()) {
            FragmentState fragmentState = new FragmentState(fragment);
            Fragment.SavedState savedState = mFragmentManager.saveFragmentInstanceState(fragment);
            if (savedState != null) {
                fragmentState.mSavedFragmentState = savedState.mState;
            }
            mFragmentStates.set(position, fragmentState);
            mFragments.set(position, null);
        }
        mCurTransaction.remove(fragment);
    }

    @Override
    public void setPrimaryItem(ViewGroup container, int position, Object object) {
        Fragment fragment = (Fragment) object;
        if (fragment != mCurrentPrimaryItem) {
            if (mCurrentPrimaryItem != null) {
                mCurrentPrimaryItem.setMenuVisibility(false);
                mCurrentPrimaryItem.setUserVisibleHint(false);
            }
            if (fragment != null) {
                fragment.setMenuVisibility(true);
                fragment.setUserVisibleHint(true);
            }
            mCurrentPrimaryItem = fragment;
        }
    }

    @Override
    public void finishUpdate(ViewGroup container) {
        if (mCurTransaction != null) {
            mCurTransaction.commitAllowingStateLoss();
            mCurTransaction = null;
            mFragmentManager.executePendingTransactions();
            // Fix: Fragment is added by transaction. BUT didn't add to FragmentManager's mActive.
            for (Fragment fragment : mFragments) {
                if (fragment != null) {
                    fixActiveFragment(mFragmentManager, fragment);
                }
            }
        }
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return ((Fragment) object).getView() == view;
    }

    @Override
    public Parcelable saveState() {
        Bundle state = null;
        // 目前顯示的 Fragments
        for (int i = 0; i < mFragments.size(); i++) {
            Fragment f = mFragments.get(i);
            if (f != null && f.isAdded()) {
                if (state == null) {
                    state = new Bundle();
                }
                String key = "f" + i;
                mFragmentManager.putFragment(state, key, f);
            }
        }
        if (mFragmentStates.size() > 0) {
            if (state == null) {
                state = new Bundle();
            }
            FragmentState[] fs = new FragmentState[mFragmentStates.size()];
            mFragmentStates.toArray(fs);
            state.putParcelableArray("states_fragment", fs);
        }
        return state;
    }

    @Override
    public void restoreState(Parcelable state, ClassLoader loader) {
        if (state != null) {
            Bundle bundle = (Bundle) state;
            bundle.setClassLoader(loader);
            Parcelable[] fs = bundle.getParcelableArray("states_fragment");
            mFragments.clear();
            mFragmentStates.clear();
            mFragmentTags.clear();
            mFragmentClassNames.clear();
            mFragmentArgs.clear();
            if (fs != null) {
                for (int i = 0; i < fs.length; i++) {
                    FragmentState fragmentState = (FragmentState) fs[i];
                    mFragmentStates.add(fragmentState);
                    if (fragmentState != null) {
                        mFragmentArgs.add(fragmentState.mArguments);
                        mFragmentTags.add(fragmentState.mTag);
                        mFragmentClassNames.add(fragmentState.mClassName);
                    } else {
                        mFragmentArgs.add(null);
                        mFragmentTags.add(null);
                        mFragmentClassNames.add(null);
                    }
                    mFragments.add(null);
                }
            }
            Iterable<String> keys = bundle.keySet();
            for (String key : keys) {
                if (key.startsWith("f")) {
                    int index = Integer.parseInt(key.substring(1));
                    Fragment f = mFragmentManager.getFragment(bundle, key);
                    if (f != null) {
                        f.setMenuVisibility(false);
                        mFragments.set(index, f);
                        mFragmentArgs.set(index, f.mArguments);
                        mFragmentTags.set(index, f.mTag);
                        mFragmentClassNames.set(index, f.getClass().getName());
                    } else {
                        Log.w(TAG, "Bad fragment at key " + key);
                    }
                }
            }
            // If restore will change
            notifyDataSetChanged();
        }
    }

    public static void fixActiveFragment(FragmentManager fragmentManager, Fragment fragment) {
        FragmentManagerImpl fm = (FragmentManagerImpl) fragmentManager;
        if (fm.mActive != null) {
            int index = fragment.mIndex;
            Fragment origin = fm.mActive.get(index);
            if (origin != null) {
                if ((origin.mIndex != fragment.mIndex) || !(origin.equals(fragment))) {
                    Log.e(TAG,
                            String.format("fixActiveFragment: Not Equal! Origin: %s %s, Fragment: %s $s",
                                    origin.getClass().getName(), origin.mIndex,
                                    fragment.getClass().getName(), fragment.mIndex
                            ));
                }
            }
            fm.mActive.set(index, fragment);
        }
    }

    // Fix
    // http://stackoverflow.com/questions/10396321/remove-fragment-page-from-viewpager-in-android
    @Override
    public int getItemPosition(Object object) {
        int index = mFragments.indexOf(object);
        if (index < 0) {
            return PagerAdapter.POSITION_NONE;
        }
        boolean isPositionChange = mTempPositionChange[index];
        int result = PagerAdapter.POSITION_UNCHANGED;
        if (isPositionChange) {
            result = PagerAdapter.POSITION_NONE;
        }
        return result;
    }
}

Tôi có 2 phần chính và phần khác. Tôi đang thêm phần khác sau mỗi vị trí thứ 5 và nó không nằm trong danh sách mảng, vậy làm cách nào để loại bỏ phần khác đó nếu nó không có trong danh sách? Tôi đang sử dụng viewpager2
b devloper

liên kết github không hoạt động
b devloper

-1

Bạn chỉ có thể ghi đè destroyItemphương thức

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    fragmentManager.beginTransaction().remove((Fragment) object).commitNowAllowingStateLoss();
}

-9

Tôi hy vọng điều này có thể giúp những gì bạn muốn.

private class MyPagerAdapter extends FragmentStatePagerAdapter {

    //... your existing code

    @Override
    public int getItemPosition(Object object){
        return PagerAdapter.POSITION_UNCHANGED;
    }
}

9
đây là chính xác ngược lại với câu trả lời được chấp nhận - làm thế nào điều này có thể làm việc ??
Zordid
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.