Entfernen Sie die Fragment-Seite aus ViewPager in Android

138

Ich versuche, Fragmente dynamisch zu einem ViewPager hinzuzufügen und daraus zu entfernen. Das Hinzufügen funktioniert problemlos, aber das Entfernen funktioniert nicht wie erwartet.

Jedes Mal, wenn ich das aktuelle Element entfernen möchte, wird das letzte entfernt.

Ich habe auch versucht, einen FragmentStatePagerAdapter zu verwenden oder POSITION_NONE in der getItemPosition-Methode des Adapters zurückzugeben.

Was mache ich falsch?

Hier ist ein einfaches Beispiel:

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>
Astuetz
quelle
2
+1 für den Quellcode
wizurd
Ist es richtig, einen nicht standardmäßigen Konstruktor in zu verwenden MyFragment.java?
CodeKiller

Antworten:

95

Die Lösung von Louth reichte nicht aus, um die Dinge für mich zum Laufen zu bringen, da die vorhandenen Fragmente nicht zerstört wurden. Motiviert durch diese Antwort fand ich, dass die Lösung darin besteht, die getItemId(int position)Methode von außer Kraft zu setzenFragmentPagerAdapter zur Angabe einer neuen eindeutigen ID wenn sich die erwartete Position eines Fragments geändert hat.

Quellcode:

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

Wenn Sie beispielsweise eine einzelne Registerkarte löschen oder die Reihenfolge ändern, sollten Sie vor dem Anruf notifyChangeInPosition(1)anrufennotifyDataSetChanged() , um sicherzustellen, dass alle Fragmente neu erstellt werden.

Warum diese Lösung funktioniert

Überschreiben von getItemPosition ():

Beim notifyDataSetChanged()Aufruf ruft der Adapter die notifyChanged()Methode auf, an ViewPagerdie er angehängt ist. Das ViewPagerprüft dann kehrte der Wert des Adapters getItemPosition()für jedes Element, die Elemente , die Rückkehr zu entfernen POSITION_NONE(siehe Quellcode ) und dann neu zu besiedeln.

Überschreiben von getItemId ():

Dies ist erforderlich, um zu verhindern, dass der Adapter das alte Fragment beim ViewPagererneuten Auffüllen neu lädt. Sie können leicht verstehen, warum dies funktioniert, indem Sie sich den Quellcode für instantiateItem () in ansehen 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));
    }

Wie Sie sehen, wird die getItem()Methode nur aufgerufen, wenn der Fragmentmanager keine vorhandenen Fragmente mit derselben ID findet. Für mich scheint es ein Fehler zu sein, dass die alten Fragmente auch nach dem notifyDataSetChanged()Aufruf noch angehängt sind, aber in der Dokumentation für ViewPagerheißt es eindeutig:

Beachten Sie, dass sich diese Klasse derzeit in einem frühen Design und einer frühen Entwicklung befindet. Die API wird sich wahrscheinlich in späteren Aktualisierungen der Kompatibilitätsbibliothek ändern und Änderungen am Quellcode von Apps erfordern, wenn diese mit der neueren Version kompiliert werden.

Hoffentlich ist die hier angegebene Problemumgehung in einer zukünftigen Version der Support-Bibliothek nicht erforderlich.

Tim Rae
quelle
1
Vielen Dank für diese Antwort! Es war anstrengend, dieses Problem zu lösen, ohne FragmentStatePagerAdapter zu verwenden. Tolle Erklärung auch. Haben Sie einen Weg gefunden, getItem auszulösen, wenn Sie zum Fragment zurückkehren? Ich habe versucht, Ihre clevere notifyDataSetChanged () an verschiedenen Stellen (dh onStop) ohne Erfolg zu verwenden
u2tall
Ich verstehe Ihre Frage nicht wirklich ... Bitte posten Sie eine neue Frage mit Quellcode, wenn Sie sie nicht herausfinden können.
Tim Rae
1
2016 ist diese Problemumgehung noch erforderlich, damit notifyDataSetChanged()sie funktioniert.
Subin Sebastian
Beeindruckend! Erstaunliche Erklärung. Ich hatte einen ziemlich komplexen Viewpager, in dem ich Elemente dynamisch entfernte und einfügte und es nicht zum Laufen brachte, ohne all dies zu verstehen.
Rupps
Diese Problemumgehung ist weiterhin erforderlich, um das alte Fragment zu ersetzen. Verdammt.
Kopikaokao
291

Der ViewPager entfernt Ihre Fragmente nicht mit dem obigen Code, da er mehrere Ansichten (oder Fragmente in Ihrem Fall) in den Speicher lädt. Zusätzlich zur sichtbaren Ansicht wird die Ansicht auf beide Seiten der sichtbaren Ansicht geladen. Dies ermöglicht das reibungslose Scrollen von Ansicht zu Ansicht, was den ViewPager so cool macht.

Um den gewünschten Effekt zu erzielen, müssen Sie einige Dinge tun.

  1. Ändern Sie den FragmentPagerAdapter in einen FragmentStatePagerAdapter. Der Grund dafür ist, dass der FragmentPagerAdapter alle Ansichten, die er lädt, für immer im Speicher behält. Wo der FragmentStatePagerAdapter über Ansichten verfügt, die außerhalb der aktuellen und durchquerbaren Ansichten liegen.

  2. Überschreiben Sie die Adaptermethode getItemPosition (siehe unten). Wenn wir mAdapter.notifyDataSetChanged();den ViewPager aufrufen, fragt er den Adapter ab, um festzustellen, was sich in Bezug auf die Positionierung geändert hat. Wir verwenden diese Methode, um zu sagen, dass sich alles geändert hat. Verarbeiten Sie daher Ihre gesamte Ansichtspositionierung erneut.

Und hier ist der Code ...

private class MyPagerAdapter extends FragmentStatePagerAdapter {

    //... your existing code

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

}
Louth
quelle
1
Ok, dies scheint die einfachste Problemumgehung für dieses Problem zu sein. Obwohl in den Fehlerberichten einige andere Problemumgehungen erläutert
Subin Sebastian
@Louth: Ich verwende ViewPager mit TabsAdapter, erweitert um FragmentStatePagerAdapter (Beispiel auf der Google-Referenzseite: developer.android.com/reference/android/support/v4/view/… ). Meine Folgefrage lautet: Wie löscht man überhaupt einen Tab? Vielen Dank.
gcl1
4
@Louth Wenn wir notifyDataSetChanged () aufrufen, erstellt der Viewpager neue Fragmente, die älteren befinden sich jedoch noch im Speicher. Und mein Problem ist; Sie erhalten einen Aufruf für ihr Ereignis onOptionsItemSelected. Wie kann ich die alten Fragmente loswerden?
Syloc
1
Vielen Dank, nur das Ersetzen von FragmentPagerAdapter durch einen FragmentStatePagerAdapter hat mein Problem gelöst. Danach entferne ich einfach AllViews () auf ViewPager und lade Fragmente wieder dynamisch neu.
Michal
17
Warum geben Sie immer POSITION_NONE zurück, anstatt wirklich zu prüfen, ob eine bestimmte Ansicht / ein bestimmtes Fragment / ein bestimmtes Objekt noch vorhanden und gültig ist? Diese Methode soll die Frage beantworten "Hey, kann ich diese wiederverwenden?" - und immer "Nein!" bedeutet einfach Erholung von allem! Dies ist in vielen Fällen nicht erforderlich.
Zordid
14

Meine Arbeitslösung zum Entfernen von Fragment-Seiten aus dem View-Pager

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

Und wenn ich eine Seite nach Index entfernen muss, mache ich das

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

Hier ist es meine Adapterinitialisierung

MyFragmentAdapter adapter = new MyFragmentAdapter(getSupportFragmentManager(), pages);
viewPager.setAdapter(adapter);
Vasil Valchev
quelle
Teilen Sie bitte den Code für das Hinzufügen eines Fragments mit.
VVB
Aber während des Hinzufügens reflektiert es langsam nach zweimaligem / dreimaligem Wischen
VVB
Wenn Sie keine Elemente entfernen, werden nur Elemente zurückgegeben, nichts weiter. Testen Sie auf VM oder einem realen Gerät?
Vasil Valchev
Probieren Sie andere Lösungen aus und antworten Sie mit mehr Stimmen. View Pager hat ein sehr komplexes Einlösesystem für eine bessere Leistung beim Swip, so dass diese Hacks nicht von Original View Pager mit Zweck behandelt werden ...
Vasil Valchev
11

Das Fragment muss bereits entfernt worden sein, aber das Problem war der Viewpager-Sicherungsstatus

Versuchen

myViewPager.setSaveFromParentEnabled(false);

Nichts hat funktioniert, aber das hat das Problem gelöst!

Prost !

Kathan Shah
quelle
2

Ich hatte die Idee, den Quellcode einfach android.support.v4.app.FragmentPagerAdpaterin eine benutzerdefinierte Klasse mit dem Namen zu kopieren CustumFragmentPagerAdapter. Dies gab mir die Möglichkeit, das instantiateItem(...)so zu ändern , dass es bei jedem Aufruf das aktuell angehängte Fragment entfernt / zerstört, bevor es das von der getItem()Methode empfangene neue Fragment hinzufügt .

Ändern Sie einfach instantiateItem(...)das wie folgt:

@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;
}
Sven
quelle
1

Sie können beide besser kombinieren:

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.

}
MSaudi
quelle
Dies funktioniert nur, wenn Sie die Registerkarte ganz rechts löschen. Um eine innere Registerkarte oder die linke Registerkarte zu löschen, müssen Sie die Registerkarten neu anordnen und die neue Position zurückgeben, wie in der Dokumentation developer.android.com/reference/android/support/v4 angegeben / view /…
BitByteDog
1

Für zukünftige Leser!

Jetzt können Sie ViewPager2 zum dynamischen Hinzufügen und Entfernen von Fragmenten aus dem Viewpager verwenden.

Zitieren der Formular- API-Referenz

ViewPager2 ersetzt ViewPager und behebt die meisten Probleme des Vorgängers, einschließlich Unterstützung des Layouts von rechts nach links, vertikale Ausrichtung, modifizierbare Fragmentsammlungen usw.

Nimm Blick auf MutableCollectionFragmentActivity.ktin googlesample / android-viewpager2 ein Beispiel für das Hinzufügen, Entfernen Fragmente dynamisch aus dem viewpager.


Für Ihre Information:

Artikel:

API-Referenz

Versionshinweise

Beispiel-Repo: https://github.com/googlesamples/android-viewpager2

user158
quelle
Ich habe 2 Fragmente Mainfragment und Otherfragment. Ich füge nach jeder fünften Position ein anderes Fragment hinzu und es befindet sich nicht in der Arrayliste. Wie kann man dieses andere Fragment entfernen, wenn es nicht in der Liste enthalten ist? Ich benutze viewpager2
b devloper
0

Louths Antwort funktioniert gut. Aber ich denke nicht, dass es eine gute Idee ist, immer POSITION_NONE zurückzugeben. Weil POSITION_NONE bedeutet, dass das Fragment zerstört werden soll und ein neues Fragment erstellt wird. Sie können dies in der Funktion dataSetChanged im Quellcode von ViewPager überprüfen.

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

Ich denke, Sie sollten besser eine Arrayliste mit schwacher Referenz verwenden, um alle von Ihnen erstellten Fragmente zu speichern. Und wenn Sie eine Seite hinzufügen oder entfernen, können Sie die richtige Position von Ihrer eigenen Arrayliste erhalten.

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

Gemäß den Kommentaren wird getItemPosition aufgerufen, wenn die Hostansicht versucht festzustellen, ob sich die Position eines Elements geändert hat. Und der Rückgabewert bedeutet seine neue Position.

Das ist aber nicht genug. Wir haben noch einen wichtigen Schritt vor uns. Im Quellcode von FragmentStatePagerAdapter befindet sich ein Array mit dem Namen "mFragments", das die nicht zerstörten Fragmente zwischenspeichert. Und in der InstantiateItem-Funktion.

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

Das zwischengespeicherte Fragment wurde direkt zurückgegeben, wenn festgestellt wurde, dass das zwischengespeicherte Fragment nicht null ist. Es gibt also ein Problem. Lassen Sie uns beispielsweise eine Seite an Position 2 löschen. Zunächst entfernen wir dieses Fragment aus unserer eigenen Referenz-Arrayliste. In getItemPosition wird also POSITION_NONE für dieses Fragment zurückgegeben, und dann wird dieses Fragment zerstört und aus "mFragments" entfernt.

 mFragments.set(position, null);

Jetzt befindet sich das Fragment an Position 3 an Position 2. Und das instanziierte Element mit der Parameterposition 3 wird aufgerufen. Zu diesem Zeitpunkt ist das dritte Element in "mFramgents" nicht null und wird daher direkt zurückgegeben. Tatsächlich wurde jedoch das Fragment an Position 2 zurückgegeben. Wenn wir also zu Seite 3 übergehen, finden wir dort eine leere Seite.

Um dieses Problem zu umgehen. Mein Rat ist, dass Sie den Quellcode von FragmentStatePagerAdapter in Ihr eigenes Projekt kopieren können. Wenn Sie Vorgänge hinzufügen oder entfernen, sollten Sie Elemente in der Arrayliste "mFragments" hinzufügen und entfernen.

Die Dinge werden einfacher, wenn Sie nur PagerAdapter anstelle von FragmentStatePagerAdapter verwenden. Viel Glück.

Passantwhu
quelle
0

Fragment dynamisch im Viewpager hinzufügen oder entfernen.
Rufen Sie beim Aktivitätsstart setupViewPager (viewPager) auf. Um verschiedene Fragmente zu laden, rufen Sie setupViewPagerCustom (viewPager) auf. zB auf Knopfdruck Aufruf: 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);
    }
}
Riad Hasan
quelle
0

Ich hatte einige Probleme mit FragmentStatePagerAdapter. Nach dem Entfernen eines Elements:

  • Es wurde ein anderes Element für eine Position verwendet (ein Element, das nicht zur Position, sondern zu einer Position daneben gehörte).
  • oder ein Fragment wurde nicht geladen (auf dieser Seite war nur ein leerer Hintergrund sichtbar)

Nach vielen Experimenten habe ich die folgende Lösung gefunden.

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

Diese Lösung verwendet nur einen Hack, wenn ein Element entfernt wird. Andernfalls (z. B. beim Hinzufügen eines Elements) bleibt die Sauberkeit und Leistung eines Originalcodes erhalten.

Natürlich rufen Sie von außerhalb des Adapters nur addItem / removeItem auf, ohne notifyDataSetChanged () aufrufen zu müssen.

Milan Laslop
quelle
0

Versuchen Sie diese Lösung. Ich habe die Datenbindung für die Bindungsansicht verwendet. Sie können die allgemeine Funktion "findViewById ()" verwenden.

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


}
Patel Jaimin
quelle
0

Ich habe eine Funktion "clearFragments" hinzugefügt und diese Funktion zum Löschen des Adapters verwendet, bevor die neuen Fragmente festgelegt wurden. Dies ruft die richtigen Entfernungsaktionen von Fragmenten auf. Meine pagerAdapter-Klasse:

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

}
Azmi Rutkay BIYIK
quelle
0

Ich habe dieses Problem durch diese Schritte gelöst

1- Verwenden Sie FragmentPagerAdapter

2- Erstellen Sie in jedem Fragment eine zufällige ID

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

3- Überschreiben Sie getItemPosition im Adapter

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

4-override getItemId im Adapter

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

5- Jetzt Code löschen ist

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

Das hat bei mir funktioniert, ich hoffe es hilft

Ali Parsa
quelle
0

Mein endgültiger Versionscode hat ALLE Fehler behoben. Ich habe 3 Tage gebraucht

Aktualisiert 2020/07/18: Ich hatte viel am Quellcode geändert und so viele Fehler behoben, aber ich verspreche nicht, dass es heute noch funktioniert.

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;
    }
}
林奕 忠
quelle
Ich habe 2 Fragmente Mainfragment und Otherfragment. Ich füge nach jeder fünften Position ein anderes Fragment hinzu und es befindet sich nicht in der Arrayliste. Wie kann man dieses andere Fragment entfernen, wenn es nicht in der Liste enthalten ist? Ich benutze viewpager2
b devloper
github link funktioniert nicht
b devloper
-1

Sie können die destroyItemMethode einfach überschreiben

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

Ich hoffe das kann helfen was du willst.

private class MyPagerAdapter extends FragmentStatePagerAdapter {

    //... your existing code

    @Override
    public int getItemPosition(Object object){
        return PagerAdapter.POSITION_UNCHANGED;
    }
}
Yin Khaing
quelle
9
Dies ist das genaue Gegenteil der akzeptierten Antwort - wie kann das funktionieren?
Zordid