안드로이드의 ViewPager에서 조각 페이지 제거


138

ViewPager에서 Fragments를 동적으로 추가하고 제거하려고하는데 아무런 문제없이 작품을 추가하지만 제거가 예상대로 작동하지 않습니다.

현재 항목을 제거하려고 할 때마다 마지막 항목이 제거됩니다.

또한 FragmentStatePagerAdapter를 사용하거나 어댑터의 getItemPosition 메소드에서 POSITION_NONE을 리턴하려고 시도했습니다.

내가 뭘 잘못하고 있죠?

기본 예는 다음과 같습니다.

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>

2
소스 코드는 +1
wizurd

에서 기본이 아닌 생성자를 사용하는 것이 맞 MyFragment.java습니까?
codeKiller

답변:


95

Louth의 솔루션은 기존 조각이 파괴되지 않았기 때문에 나를 위해 일하기에 충분하지 않았습니다. 이 답변에 동기를 부여 하여 솔루션 의 단편 위치가 변경 될 때마다 새로운 고유 ID를 제공하는 getItemId(int position)방법 을 재정의하는 것이 해결책이라는 것을 알았습니다 FragmentPagerAdapter.

소스 코드:

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

예를 들어 단일 탭을 삭제하거나 주문을 변경 한 경우 전화 notifyChangeInPosition(1)하기 전에 전화 해야합니다.notifyDataSetChanged() 모든 조각을 다시 작성해야합니다.

이 솔루션이 작동하는 이유

getItemPosition () 재정의 :

경우 notifyDataSetChanged()라고, 어댑터는 호출 notifyChanged()의 방법 ViewPager이 부착되어 있습니다. 그런 ViewPager다음 getItemPosition()각 항목에 대해 어댑터가 반환 한 값을 확인하여 반환되는 항목을 제거하고 POSITION_NONE( 소스 코드 참조 ) 다시 채 웁니다.

getItemId () 재정의 :

이것은 ViewPager다시 채워질 때 어댑터가 이전 조각을 다시로드하지 못하도록하기 위해 필요합니다 . 에서 instantiateItem () 의 소스 코드 를 보면 이것이 왜 작동하는지 쉽게 이해할 수 있습니다 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));
    }

보다시피 getItem(), 프래그먼트 관리자가 동일한 ID를 가진 기존 프래그먼트를 찾지 못한 경우에만 메소드가 호출됩니다. 나에게 그것은 오래된 조각이 여전히 notifyDataSetChanged()호출 된 후에도 첨부 된 버그처럼 보이지만에 대한 설명서 ViewPager에는 다음과 같이 명시되어 있습니다.

이 클래스는 현재 초기 설계 및 개발 단계에 있습니다. API는 이후 버전의 호환성 라이브러리에서 변경 될 수 있으며, 새로운 버전에 대해 컴파일 될 때 앱의 소스 코드를 변경해야합니다.

따라서 향후 버전의 지원 라이브러리에서는 여기에 제공된 해결 방법이 필요하지 않기를 바랍니다.


1
이 답변에 정말 감사합니다! FragmentStatePagerAdapter를 사용하지 않고이 문제를 해결하려고 애 쓰고 있습니다. 좋은 설명도 있습니다. 프래그먼트로 돌아올 때 getItem을 트리거하는 방법을 찾았습니까? 나는 성공하지 않고 다양한 장소 (예 : onStop)에서 영리한 notifyDataSetChanged ()를 사용하려고 시도했습니다
u2tall

질문이 잘 이해되지 않습니다 ... 소스 코드를 포함하여 새 질문을 게시 할 수없는 경우 게시하십시오.
Tim Rae

1
2016 년에도이 해결 방법이 여전히 notifyDataSetChanged()작동해야합니다.
Subin Sebastian

와! 놀라운 설명. 항목을 동적으로 제거하고 삽입하는 꽤 복잡한 viewpager가 있었으며이 모든 것을 이해하지 않으면 작동하지 않습니다.
rupps

오래된 조각을 대체하려면 여전히이 해결 방법이 필요합니다. 댕
kopikaokao

291

ViewPager는 위의 코드를 사용하여 프래그먼트를 제거하지 않습니다. 여러 뷰 (또는 귀하의 경우 프래그먼트)를 메모리에로드하기 때문입니다. 가시적 뷰 외에도 가시적 뷰의 양쪽에 뷰를로드합니다. 이것은 ViewPager를 매우 시원하게 만드는 뷰 간 부드러운 스크롤을 제공합니다.

원하는 효과를 얻으려면 몇 가지 작업을 수행해야합니다.

  1. FragmentPagerAdapter를 FragmentStatePagerAdapter로 변경하십시오. 그 이유는 FragmentPagerAdapter가 메모리에로드하는 모든 뷰를 영원히 메모리에 유지하기 때문입니다. FragmentStatePagerAdapter가 현재 및 통과 가능한보기를 벗어나는보기를 처리하는 위치입니다.

  2. 어댑터 메소드 getItemPosition을 대체하십시오 (아래 참조). mAdapter.notifyDataSetChanged();ViewPager 를 호출 하면 어댑터에 질문하여 위치 지정 측면에서 변경된 사항을 확인합니다. 이 방법을 사용하면 모든 것이 변경되었다고 말하고 모든 뷰 위치를 다시 처리하십시오.

그리고 여기 코드가 있습니다 ...

private class MyPagerAdapter extends FragmentStatePagerAdapter {

    //... your existing code

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

}

1
좋아, 이것은이 문제에 대한 가장 쉬운 해결 방법 인 것 같습니다. code.google.com/p/android/issues/detail?id=19110 및 여기 : code.google.com/p/android/issues/detail?id=
Subin Sebastian

@Louth : FragmentStatePagerAdapter에서 확장 된 TabsAdapter와 함께 ViewPager를 사용하고 있습니다 (예 : Google 참조 페이지 : developer.android.com/reference/android/support/v4/view/… ). 내 후속 질문은 처음에 탭을 어떻게 삭제합니까? 감사.
gcl1 2019

4
@Louth notifyDataSetChanged ()를 호출하면 viewpager가 새 조각을 생성하지만 이전 조각은 여전히 ​​메모리에 있습니다. 그리고 내 문제는; onOptionsItemSelected 이벤트에 대한 호출을받습니다. 오래된 조각을 제거하려면 어떻게해야합니까?
syloc

1
감사합니다. FragmentPagerAdapter를 FragmentStatePagerAdapter로 바꾸면 문제가 해결되었습니다. 그런 다음 ViewPager에서 AllViews ()를 제거하고 조각을 다시 동적으로 다시로드하십시오.
Michal

17
주어진보기 / 조각 / 객체가 여전히 존재하고 유효한지 실제로 찾는 대신 항상 POSITION_NONE을 반환하는 이유는 무엇입니까? 이 방법은 "이봐, 이걸 재사용해도 될까?"라는 질문에 답해야합니다. -항상 "아니요!" 단지 모든 것의 레크리에이션을 의미합니다! 많은 경우에 이것은 불필요합니다.
Zordid

14

뷰 호출기에서 조각 페이지를 제거하는 작업 솔루션

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

색인으로 일부 페이지를 제거해야 할 때이 작업을 수행합니다.

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

여기 내 어댑터 초기화입니다

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

조각 추가를 위해 코드를 공유 하시겠습니까?
VVB

그러나 그것을 추가하면서 두 번 / 세 번 강타 후 느리게 반영
VVB

항목을 제거하지 않으면 항목 만 반환되고 더 이상 아무것도 없습니다 ... VM 또는 실제 장치에서 테스트하고 있습니까?
Vasil Valchev

다른 해결책을 시도하고 더 많은 투표에 대답하십시오. View pager는 스 와이프에서 더 나은 성능을 위해 매우 복잡한 캐싱 시스템을 가지고 있으므로이 해킹은 원래 view pager
whit

11

조각이 이미 제거되었지만 문제는 viewpager 저장 상태였습니다.

시험

myViewPager.setSaveFromParentEnabled(false);

아무것도 효과가 없었지만 문제가 해결되었습니다!

건배!


2

소스 코드를에서 android.support.v4.app.FragmentPagerAdpater이라는 사용자 정의 클래스로 복사하는 아이디어를 얻었습니다 CustumFragmentPagerAdapter. 이렇게 instantiateItem(...)하면 호출 할 때마다 getItem()메소드 에서받은 새로운 조각을 추가하기 전에 현재 첨부 된 조각을 제거 / 파괴 할 수 있습니다 .

instantiateItem(...)다음과 같이 간단하게 수정하십시오 .

@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

더 나은 두 가지를 결합 할 수 있습니다.

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.

}

맨 오른쪽 탭을 삭제하고 내부 탭이나 왼쪽 탭을 삭제하려면 탭을 재정렬하고 설명서 개발자에
BitByteDog

1

미래의 독자들을 위해!

이제 ViewPager2 를 사용 하여 viewpager에서 조각을 동적으로 추가하고 제거 할 수 있습니다 .

인용 양식 API 참조

ViewPager2는 ViewPager를 대체하여 오른쪽에서 왼쪽으로 레이아웃 지원, 수직 방향, 수정 가능한 조각 모음 등을 포함하여 이전 버전의 대부분의 어려움을 해결합니다.

봐 가지고 MutableCollectionFragmentActivity.kt에서 googlesample / 안드로이드 - viewpager2을 , 부가 viewpager에서 동적 조각을 제거하는 예를 들어.


귀하의 정보를 위해 :

조항:

API 참조

릴리즈 노트

샘플 저장소 : https://github.com/googlesamples/android-viewpager2


나는 2 개의 조각 mainfragment 및 otherfragment가 있습니다. 매 5 번째 위치마다 다른 조각을 추가하고 배열 목록에 없으므로 목록에없는 경우 다른 조각을 제거하는 방법은 무엇입니까? 나는 viewpager2를 사용하고있다
b devloper

0

Louth의 대답은 잘 작동합니다. 그러나 항상 POSITION_NONE을 반환한다고 생각하지는 않습니다. POSITION_NONE은 프래그먼트가 파괴되고 새로운 프래그먼트가 생성됨을 의미합니다. ViewPager의 소스 코드에있는 dataSetChanged 함수에서이를 확인할 수 있습니다.

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

따라서 weakReference의 배열 목록을 사용하여 만든 모든 조각을 저장하는 것이 좋습니다. 그리고 페이지를 추가하거나 제거하면 자신의 배열 목록에서 올바른 위치를 얻을 수 있습니다.

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

주석에 따르면, 호스트보기가 항목의 위치가 변경되었는지 판별하려고 시도 할 때 getItemPosition이 호출됩니다. 그리고 반환 값은 새로운 위치를 의미합니다.

그러나 이것은 충분하지 않습니다. 우리는 여전히 중요한 조치를 취해야합니다. FragmentStatePagerAdapter의 소스 코드에는 "mFragments"라는 배열이 있으며 파괴되지 않은 조각을 캐시합니다. 그리고 instantiateItem 함수에서.

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

캐시 된 조각이 null이 아닌 경우 캐시 된 조각을 직접 반환했습니다. 문제가 있습니다. 예를 들어, 위치 2에서 한 페이지를 삭제하겠습니다. 먼저 자체 참조 배열 목록에서 해당 조각을 제거합니다. 따라서 getItemPosition에서는 해당 프래그먼트에 대해 POSITION_NONE을 반환 한 다음 해당 프래그먼트가 "mFragments"에서 제거 및 제거됩니다.

 mFragments.set(position, null);

이제 위치 3의 프래그먼트가 위치 2에 있습니다. 그리고 매개 변수 위치 3의 instantiatedItem이 호출됩니다. 현재 "mFramgents"의 세 번째 항목은 null이 아니므로 직접 반환됩니다. 그러나 실제로 반환 된 것은 위치 2의 조각입니다. 따라서 3 페이지로 넘어 가면 빈 페이지가 나타납니다.

이 문제를 해결하려면 필자는 FragmentStatePagerAdapter의 소스 코드를 자신의 프로젝트에 복사 할 수 있으며 작업을 추가 또는 제거 할 때 "mFragments"배열 목록에서 요소를 추가 및 제거해야합니다.

FragmentStatePagerAdapter 대신 PagerAdapter를 사용하면 상황이 더 간단 해집니다. 행운을 빕니다.


0

viewpager에서 조각을 동적으로 추가하거나 제거하십시오.
활동 시작시 setupViewPager (viewPager)를 호출하십시오. 다른 조각 호출을 불러 오려면 setupViewPagerCustom (viewPager)을 호출하십시오. 예를 들어 버튼 클릭 호출시 : 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

에 문제가 FragmentStatePagerAdapter있었습니다. 항목을 제거한 후 :

  • 위치에 사용 된 다른 항목이있었습니다 (위치에 속하지 않고 그 옆에있는 항목).
  • 또는 일부 조각이로드되지 않았습니다 (해당 페이지에 빈 배경 만 표시됨)

많은 실험을 한 후 다음과 같은 해결책을 찾았습니다.

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

이 솔루션은 아이템을 제거 할 때만 해킹을 사용합니다. 그렇지 않으면 (예 : 항목을 추가 할 때) 원본 코드의 청결도와 성능이 유지됩니다.

물론 어댑터 외부에서 addItem / removeItem 만 호출하고 notifyDataSetChanged ()를 호출 할 필요는 없습니다.


0

이 솔루션을 사용해보십시오. 바인딩 뷰에 데이터 바인딩을 사용했습니다. 일반적인 "findViewById ()"함수를 사용할 수 있습니다.

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

"clearFragments"기능을 추가했으며 새 프래그먼트를 설정하기 전에 해당 기능을 사용하여 어댑터를 지 웁니다. 이것은 조각의 적절한 제거 작업을 호출합니다. 내 pagerAdapter 클래스 :

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

이 단계 로이 문제를 해결했습니다.

1- FragmentPagerAdapter 사용

각 조각에서 2는 임의의 ID를 만듭니다.

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

3 어댑터의 getItemPosition을 대체합니다.

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

어댑터의 4 개 getItemId 대체

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

5- 이제 코드 삭제가

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

이것은 나를 위해 일했다 나는 도움을 바란다


0

내 최종 버전 코드는 모든 버그를 수정했습니다. 3 일이 걸렸다

2020/07/18 업데이트 : 많은 소스 코드를 변경하고 너무 많은 버그를 수정했지만 오늘도 여전히 작동한다고 약속하지는 않습니다.

https://github.com/lin1987www/FragmentBuilder/blob/master/commonLibrary/src/main/java/android/support/v4/app/FragmentStatePagerAdapterFix.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;
    }
}

나는 2 개의 조각 mainfragment 및 otherfragment가 있습니다. 매 5 번째 위치마다 다른 조각을 추가하고 배열 목록에 없으므로 목록에없는 경우 다른 조각을 제거하는 방법은 무엇입니까? 나는 viewpager2를 사용하고있다
b devloper

github 링크가 작동하지 않음
b devloper

-1

destroyItem메소드를 무시할 수 있습니다.

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

-9

이것이 당신이 원하는 것을 도울 수 있기를 바랍니다.

private class MyPagerAdapter extends FragmentStatePagerAdapter {

    //... your existing code

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

9
이것은 받아 들여진 대답과 정반대입니다-어떻게 작동합니까 ??
Zordid
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.