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>
MyFragment.java
?Antworten:
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:
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 dienotifyChanged()
Methode auf, anViewPager
die er angehängt ist. DasViewPager
prüft dann kehrte der Wert des AdaptersgetItemPosition()
für jedes Element, die Elemente , die Rückkehr zu entfernenPOSITION_NONE
(siehe Quellcode ) und dann neu zu besiedeln.Überschreiben von getItemId ():
Dies ist erforderlich, um zu verhindern, dass der Adapter das alte Fragment beim
ViewPager
erneuten Auffüllen neu lädt. Sie können leicht verstehen, warum dies funktioniert, indem Sie sich den Quellcode für instantiateItem () in ansehenFragmentPagerAdapter
.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 demnotifyDataSetChanged()
Aufruf noch angehängt sind, aber in der Dokumentation fürViewPager
heißt es eindeutig:Hoffentlich ist die hier angegebene Problemumgehung in einer zukünftigen Version der Support-Bibliothek nicht erforderlich.
quelle
notifyDataSetChanged()
sie funktioniert.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.
Ä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.
Ü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 ...
quelle
Meine Arbeitslösung zum Entfernen von Fragment-Seiten aus dem View-Pager
Und wenn ich eine Seite nach Index entfernen muss, mache ich das
Hier ist es meine Adapterinitialisierung
quelle
Das Fragment muss bereits entfernt worden sein, aber das Problem war der Viewpager-Sicherungsstatus
Versuchen
Nichts hat funktioniert, aber das hat das Problem gelöst!
Prost !
quelle
Ich hatte die Idee, den Quellcode einfach
android.support.v4.app.FragmentPagerAdpater
in eine benutzerdefinierte Klasse mit dem Namen zu kopierenCustumFragmentPagerAdapter
. Dies gab mir die Möglichkeit, dasinstantiateItem(...)
so zu ändern , dass es bei jedem Aufruf das aktuell angehängte Fragment entfernt / zerstört, bevor es das von dergetItem()
Methode empfangene neue Fragment hinzufügt .Ändern Sie einfach
instantiateItem(...)
das wie folgt:quelle
Sie können beide besser kombinieren:
quelle
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
Nimm Blick auf
MutableCollectionFragmentActivity.kt
in 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
quelle
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.
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.
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.
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.
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.
quelle
quelle
Ich hatte einige Probleme mit
FragmentStatePagerAdapter
. Nach dem Entfernen eines Elements:Nach vielen Experimenten habe ich die folgende Lösung gefunden.
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.
quelle
Versuchen Sie diese Lösung. Ich habe die Datenbindung für die Bindungsansicht verwendet. Sie können die allgemeine Funktion "findViewById ()" verwenden.
quelle
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:
quelle
Ich habe dieses Problem durch diese Schritte gelöst
1- Verwenden Sie FragmentPagerAdapter
2- Erstellen Sie in jedem Fragment eine zufällige ID
3- Überschreiben Sie getItemPosition im Adapter
4-override getItemId im Adapter
5- Jetzt Code löschen ist
Das hat bei mir funktioniert, ich hoffe es hilft
quelle
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
quelle
Sie können die
destroyItem
Methode einfach überschreibenquelle
Ich hoffe das kann helfen was du willst.
quelle