Ein Fragment dient dazu, Activities zu untergliedern. Sie schreiben ein eigenes Fragment, indem Sie die Klasse Fragment erweitern. Fragmente können einerseits Teile eines Bildschirms sein, können allerdings auch den kompletten Bildschirm darstellen. Dennoch werden Sie dann von einer gemeinsamen Activity verwaltet.
Das ermöglicht eine einfachere Speicherverwaltung aber auch eine elegante Form der Navigation zwischen den Bildschirmen.
- Fragment anlegen
- Lifecycle eines Fragments
- Fragmente nebeneinander im Landscape-Modus darstellen
- Fragments mit mehreren Elementen füllen
- Fragments als Activity-Ersatz
Fragment anlegen
Fragments werden in Android-Studio am einfachsten folgendermaßen angelegt:- Im Android Studio klicken Sie mit der rechten Maustaste auf app.
- Wählen Sie den Menüpunkt New|Fragment|Fragment Blank.
- Als Fragment Name wählen Sie: TopFragment
- Der Fragment Layout Name wird automatisch: fragment_top.xml
- Abschließen mit Finish
Fragment-Java-Dateien
Das Java-Programm für ein Fragment wird zunächst auf die kleinstmögliche Größe reduziert.public class TopFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment return inflater.inflate(R.layout.fragment_top, container, false); } }
Fragment-Layout anpassen
Die automatisch erstellten XML-Dateien haben etwa folgendes Aussehen:<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MeinFragment"> <!-- TODO: Update blank fragment layout --> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:text="@string/hello_blank_fragment" /> </FrameLayout>Über die values/strings.xml wird ein Text "Hello blank fragment" ausgegeben. Damit die beiden sichtbar unterscheidbar werden, tragen wir bei einem den Text ".Top", beim anderen ".Down" in das TextView ein.
Die Fragments haben unterschiedliche IDs, die unter tools:context zu sehen sind. Diese wurden bei der Erzeugung der Fragments erstellt: DownFragment und TopFragment.
Die Fragment-XML-Dateien werden in die Activity-XML eingebunden
In der Main-Activity-XML wird das Fragment als fragment eingetragen:<fragment android:id="@+id/.DownFragment" android:name="de.willemer.twofragments.DownFragment" android:layout_width="wrap_content" android:layout_height="wrap_content" />Anschließend nörgelt der Designer, dass das fragment nicht mit Constraints an die Ränder angebunden wurde.
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <fragment android:id="@+id/TopFragment" android:name="de.willemer.twofragments.TopFragment" android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toTopOf="@id/DownFragment" /> <fragment android:id="@+id/DownFragment" android:name="de.willemer.twofragments.DownFragment" android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/TopFragment" />
Zugriff zwischen Activity und Fragment
Eine Activity kann mehrere Fragmente erzeugen, verwalten und stoppen.Das Fragment kann jederzeit über den Aufruf von getActivity auf »seine« Activity zugreifen. Der Aufruf ist parameterlos, der Rückgabewert vom Typ Activity.
So können Fragmente über ihre Activity Daten austauschen. Dazu definiert man in der Activity eine Methode, die dann über getActivity aufgerufen werden kann.
MainActivity main = (MainActivity)getActivity(); int zahl = main.tudochwas(12);
Komplizierter wird es, wenn eine Activity auf seine Fragments zugreifen will, schon dadurch, dass es mehrere Fragments gibt. Dazu besitzt eine Activity einen FragmentManager.
FragmentManager fm = getFragmentManager(); TopFragment top = (TopFragment) fm.findFragmentById(R.id.TopFragment);
Lifecycle eines Fragments
Hinweis: onActivityCreated ist als deprecated abgekündigt. Bei neueren Entwicklungen sollte der Callback also nicht mehr verwendet werden.
Fragmente nebeneinander im Landscape-Modus darstellen
Nun wollen wir erreichen, dass die Fragments beim Querlegen des Smartphones nicht mehr untereinander erscheinen, sondern nebeneinander und somit den Platz besser ausnutzen.Dafür wird ein Main-XML-Layout benötigt, das aktiviert wird, wenn das Gerät im Landscape-Modus ist. Die Layouts für den Landscape-Modus werden statt im Verzeichnis res/layout im Verzeichnis res/layout-land abgelegt.
- Klicken Sie das Verzeichnis res mit rechter Maustaste an.
- Wählen Sie New | Directory an und geben Sie als Namen layout-land an.
Erstellen Sie im Verzeichnis layout-land ein Layout, in dem die Fragmente nebeneinander stehen.
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <fragment android:id="@+id/TopFragment" android:name="de.willemer.twofragments.TopFragment" android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@+id/DownFragment" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <fragment android:id="@+id/DownFragment" android:name="de.willemer.twofragments.DownFragment" android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_constraintStart_toEndOf="@id/TopFragment" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
Fragments mit mehreren Elementen füllen
Wenn man die Fragment-XML mit der rechten Maustaste anklickt, kann man das automatisch erzeugte FrameLayout zu einem ConstraintLayout umwandeln lassen. Anschließend lassen sich leicht mehrere Kontrollelemente in dem Fragment ablegen.Der Zugriff auf die Elemente in den Fragmenten können genauso von der Main-Activity bearbeitet werden, als lägen sie in der Layout-Datei der Main-Activity selbst.
Links
Fragments als Activity-Ersatz
Statt jeden Screen von einer eigenen Activity zu steuern, ist es möglich, die Screens jeweils durch ein Fragment verwalten zu lassen und diese durch eine gemeinsame Activity zu steuern.
- Es wird ein Projekt (Empty Activity) angelegt. Dies enthält eine MainActivity.
- In der Layout-XML-Datei wird nur der Rahmen benötigt. Eine eventuell
herumlungernde TextView kann entsorgt werden.
Dafür soll aber der Rahmen eine id bekommen, beispielsweise nennen wir
ihn container:
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/container" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"/>
- In der MainActivity.java wird die onCreate-Methode ergänzt:
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (savedInstanceState == null) { getSupportFragmentManager().beginTransaction() .setReorderingAllowed(true) .add(R.id.container, new FragStart(), null) .commit(); } }
Darin wird eine Instanz von FragStart erzeugt. Dies ist das Fragment und kann direkt erzeugt werden. - Projekt anklicken, dann New|Fragment(Blank)
- Im Fragment-Layout das FrameLayout rechts anklicken und Convert FrameLayout to ConstraintLayout
- Mit dem Designer wird eine TextView mit der ID textView und ein Button mit der id btFragStart eingesetzt.
- Das vorgebene Java-Listing kann erheblich zusammengekürzt werden.
Übrig bleibt nur folgendes:
public class FragStart extends Fragment implements View.OnClickListener { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment View view = inflater.inflate(R.layout.fragment_frag_start, container, false); Button bt = (Button)view.findViewById(R.id.btFragStart); bt.setOnClickListener(this); return view; } @Override public void onClick(View v) { TextView tv = (TextView) getView().findViewById(R.id.tvFragStart); tv.setText("Clicked"); } }
Das Fragment besitzt also einen Button und kontrolliert diesen vollständig. Wichtig ist, dass aus dem Fragment nicht direkt findViewById aufgerufen werden kann, ohne dass der Methode eine View vorangestellt wird. Diese erlangt man meist über den Aufruf von getView().
Zur besseren Demonstration erzeugen wir ein zweites Fragment.
- Wir erzeugen wieder ein leeres Fragment (Blank) und werfen in der Java-Datei wieder alles über Bord, außer der Methode onCreateView. Auch hier setzen wir wieder einen Button ein.
- Dieses Mal soll beim Klick des Buttons ein Wechsel zwischen den Fragments statfinden. Die Methode für den Wechsel stelt die zentrale Activity, in diesem Fall die MainActivity, zur Verfügung.
- Die onClick-Methode der beiden Fragmente sieht so aus:
@Override public void onClick(View v) { MainActivity main = (MainActivity) getActivity(); main.wechsleFragment(TAG); }
In der Konstanten TAG hat das Fragment FragStart den String "FragStart" und das Fragment FragEnd den String "FragEnd" stehen. - In der Activity wird dann die Methode wechsleFragment definiert:
public void wechsleFragment(String str) { if (str.equals("FragStart")) { getSupportFragmentManager().beginTransaction() .setReorderingAllowed(true) .replace(R.id.container, new FragEnd(), null) .addToBackStack(null) .commit(); } else { getSupportFragmentManager().beginTransaction() .setReorderingAllowed(true) .replace(R.id.container, new FragStart(), null) .addToBackStack(null) .commit(); } }