Android-Programmierung Fragment
Willemers Informatik-Ecke
Zurück zur Android-Hauptseite

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

Fragments werden in Android-Studio am einfachsten folgendermaßen angelegt: Um mit zwei Fragmenten die Beispiele durchzuspielen, muss dieser Vorgang wiederholt werden. Das zweite Fragment heißt dann konsequenterweise DownFragment.

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.

Das Verzeichnis bleibt im Projektbaum bei Android Studio unsichtbar, solange er im Android-Sicht ist. Wählen Sie aus der Klappbox über dem Projektbaum die Sicht Project Files aus. Nun ist das Verzeichnis res/layout-land zu sehen.

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.

Aus dem Fragment kann jederzeit die Activity über den Aufruf von getActivity() zugegriffen werden, weil das Fragment ohne die Activity nicht lebensfähig wäre. Dort können also Daten und Methoden abgelegt werden, dir für mehrere Bildschirme wichtig sind, ohne dass sie per Intent hin- und hergesandt oder in SharedPreferences gesichert werden müssen.

Zur besseren Demonstration erzeugen wir ein zweites Fragment.

So wie hier also die Activity die Dienstleistung für die beiden Fragments in Form der Methode wechsleFragment zur Verfügung stellt, könnte sie natürlich auch Daten speichern, die von beiden Fragments benötigt werden. Tatsächlich wird man für die Speicherung der oberflächenbezogenen Daten eher das ViewModel einsetzen.