Fragmente dynamisch zum Layout hinzufügen

W

washpuda

Neues Mitglied
Threadstarter
Hallo Leute,

ich bin echt am verzweifeln, ich versuche schon seit zwei Tagen einfach ein Fragment zu einem Constraintlayout programmiertechnisch hinzuzufügen und habe schon total viel recherchiert. Bei den Beispielen stoße ich immer auf das gleiche Problem (java.lang.IllegalArgumentException: No view found for id 0x7f080066 (de.bajestro.picmaptogo:id/frameLayout) for fragment TestBDP{26d90e0 #1 id=0x7f080066} oder so ähnlich). In meiner App muss ich später auch Fragmente während der Laufzeit einfach austauschen können.

Eigentlich bin ich total fit in Java, arbeite aber sonst mit Swing, dort kann man einfach panel.add(otherPanel); aufrufen und mit panel.revalidate() aktualisieren.

Ich baue mir mehrere Dialoge. Damit ich die Grundfunktionalität nicht mehrmals implementieren muss und ich möglichst viel Code wiederverwenden möchte, baue ich mir einen Grunddialog der von FragmentDialog abgeleitet ist. Diesem möchte ich im Konstruktor ein Fragment mit den Individuellen DialogOptionen übergeben. Das übergebene Fragment soll einfach zwischen den beiden Guidelines platziert werden.

Als Beispiel habe ich Activity mit einem Button der den Dialog startet (vorerst ohne anzeige des Übergebenen Fragments).

Wenn ich auf den OK-Button klicke soll das Fragment in der Mitte dargestellt werden, doch leider stürzt es hier immer ab.

Das bekomme ich einfach nicht hin, habe schon alles probiert (direkt hinzugefügt mit und ohne Layout ) und überall nachgelesen. Im Beispiel versuche ich ein FrameLayout als Platzhalter zu tauschen.
Irgendwie hat der Fragmentmanager kein Zugriff auf ein Layout?? Er findet auch das darunterliegende Constraintlayout nicht?? Ich vermute der Fragmentmanager verweist auf irgend etwas ganz anderes?

Ich hoffe mir kann irgendjemand helfen.

Activity mit Button zum Starten des Dialogs:

Code:
public class DialogTestActivity extends AppCompatActivity {
    Button btnOpenDialog;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_dialog_test);
        btnOpenDialog = findViewById(R.id.btnShowDialog);
        btnOpenDialog.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                TestBDP testBDP = new TestBDP("param1", "param2");  // conatainer fragment
                BDialog bDialog = new BDialog( testBDP);
                FragmentManager fragmentManager = getSupportFragmentManager();
                FragmentTransaction ft =  fragmentManager.beginTransaction();
                Fragment prev = fragmentManager.findFragmentByTag("dialog");
                if (prev != null) {
                   ft.remove(prev);
                 }
                ft.addToBackStack(null);
                bDialog.show(ft, "dialog");
            }
        });
    }
}
Grunddialog (oben Titel und unten eine Buttonleiste. Dazwischen soll das übergebene Fragment angezeigt werden)

Code:
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.FrameLayout;

import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;

import de.bajestro.picmaptogo.R;
import picturemap.tools.Utils;

public class BDialog extends DialogFragment implements View.OnClickListener {

    Fragment dialogFragment;
    Button btnCancel, btnOK, btnTakeOver, btnDefault;


    public BDialog( AbstractBDialogFragment dialogFragment) {
        this.dialogFragment = dialogFragment;
         setStyle(DialogFragment.STYLE_NO_TITLE,android.R.style.Theme_Holo_Light);
        dialogFragment.setBDialog(this);
    }


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
    }


    @Override
       public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        System.out.println(" ----- on Create view");
        View view = inflater.inflate(R.layout.bdialog, container);
        btnOK = view.findViewById(R.id.btnOK);
        btnOK.setOnClickListener(this);
        btnCancel = view.findViewById(R.id.btnCancel);
        btnCancel.setOnClickListener(this);
        btnDefault = view.findViewById(R.id.btnDefault);
        btnDefault.setOnClickListener(this);
        btnTakeOver = view.findViewById(R.id.btnTakeOver);
        btnTakeOver.setOnClickListener(this);
       return view;
    }

    @Override
    public void onStart() {
        super.onStart();
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.btnOK:
                Utils.print("OK");

                FragmentManager fragmentManager = getFragmentManager();
                FrameLayout layout = getView().findViewById(R.id.frameLayout);
                fragmentManager.beginTransaction().replace(layout.getId(),dialogFragment).commit();

                break;
            case R.id.btnCancel:
                Utils.print("Abbrechen");
                break;

            case R.id.btnDefault:
                Utils.print("Default");
                break;

            case R.id.btnTakeOver:
                Utils.print("takeOver");
                break;
            default:
                throw new IllegalStateException("Unexpected value: " + view.getId());
        }
    }
}
Das Fragement, was in der Mitte des Dialogs angezeigt werden soll.

Code:
public class TestBDP extends AbstractBDialogFragment {

    private static final String ARG_PARAM1 = "param1";
    private static final String ARG_PARAM2 = "param2";

    private String mParam1;
    private String mParam2;

    public TestBDP(String mParam1, String  mParam2) {
        super();
        this.mParam1 = mParam1;
        this.mParam2 = mParam2;
    }

    @Override
    public void setBestätigt() {}
    @Override
    public void onBackStackChanged() {}
}
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;

abstract class AbstractBDialogFragment extends Fragment implements FragmentManager.OnBackStackChangedListener {
    BDialog bDialog;

    public AbstractBDialogFragment(){
        super();
    }

    public abstract void setBestätigt();

    public void setBDialog(BDialog bDialog){
        this.bDialog = bDialog;
    }
HTML:
<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:id="@+id/bDialogLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/colorAccent"
        android:text="@string/dialog"
        android:textAppearance="@style/TextAppearance.AppCompat.Large"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/btnCancel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:text="@string/cancel"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/btnTakeOver"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@+id/guideline1" />

    <Button
        android:id="@+id/btnOK"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="16dp"
        android:text="@string/ok"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/btnDefault"
        app:layout_constraintTop_toTopOf="@+id/guideline1" />

    <Button
        android:id="@+id/btnTakeOver"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/takeOver"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/btnDefault"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/btnCancel"
        app:layout_constraintTop_toTopOf="@+id/guideline1" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_begin="29dp" />

    <Button
        android:id="@+id/btnDefault"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="4dp"

        android:text="@string/default1"
        android:visibility="invisible"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/btnOK"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/btnTakeOver" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_end="60dp" />

    <FrameLayout
        android:id="@+id/frameLayout"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@+id/guideline1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@+id/guideline">

    </FrameLayout>


</androidx.constraintlayout.widget.ConstraintLayout>
 
swa00

swa00

Moderator
Teammitglied
Hallo @washpuda ,

und willkommen im Forum.

Ich baue mir mehrere Dialoge. Damit ich die Grundfunktionalität nicht mehrmals implementieren muss und ich möglichst viel Code wiederverwenden möchte, baue ich mir einen Grunddialog der von FragmentDialog abgeleitet ist.
Ich würde dein Vorhaben überhaupt nicht mit dem Fragmentlayouter umsetzen .

Was hindert dich denn daran , einen Dialog abzuleiten, der mittels Inflate
dein layout einbindet ?

Das wäre weitaus einfacher und übersichtlicher
 
Zuletzt bearbeitet:
W

washpuda

Neues Mitglied
Threadstarter
Hallo Danke erstmal für die schnelle Antwort,

Der Grunddialog wird noch etwas umfangreicher, da dieser bereits viele Dinge übernehmen soll. Auch nachträgliche Anpassungen z. B. ändern der Optik, hinzufügen eines Hilfebuttons sollen einmal an zentraler Stelle möglich sein (mache ich bei Swing auch so).

Wenn ich mittels Inflater ein Layout einbinde, wie bekomme ich dann Zugriff auf die Schaltflächen, das passiert bei mir ja im Fragment, im Layout wüsste ich nicht wie.

Es muss doch Fragmenten möglich sein. Ich möchte die Fragmente verstehen, da sich damit auch Anpassungen für verschiedene Displaygrößen möglich sind. Auch habe ich mich ja an die Beispiele im Netz gehalten, warum funktioniert es bei mir nicht, warum findet er kein Layout.
 
swa00

swa00

Moderator
Teammitglied
ok, ich sehe, dass du zwar in Java fit bist , aber nicht in der Thematik Android :)

Fragmente werden nicht für so etwas benutzt.

Deine Dialoge solltest du entweder von einem View oder einem Dialog ableiten.

Die InfalteLayouts kannst du dann mit gleichen Resourcen ID's ausstatten und dann auf diese zugreifen.
Mit einem Contruktor übergibst du dann dieser Klasse deine "Custom-Daten"

Und den Datenaustausch zum Main würde ich entweder mittels ListenerInterfaces, Broadcasts oder Intents vornehmen.
Ggf könnte man das Ganze dann auch noch als Singleton deklarieren
 
Zuletzt bearbeitet:
W

washpuda

Neues Mitglied
Threadstarter
Wie soll ich den die gleichen Resourcen ID's verwenden? Die Informationen der Dialoge sind sehr unterschiedlich.

Wenn das Fragment dynamisch eingebunden werden kann, muss ich nur die wesentlichen Funktionalitäten innerhalb des Fragments umsetzen. In der Dokumentation von Android sind ja Fragmente wie kleine Subactivitys und genau das brauche ich (bausteine mit eigener Logic).

Und Fragmente kann man in Layouts einbinden. Wenn ich dies statisch ins layout.xml eintrage wird es doch auch eingebunden, warum geht es nicht programmiertechnisch?
 
swa00

swa00

Moderator
Teammitglied
Wenn ich dies statisch ins layout.xml eintrage wird es doch auch eingebunden, warum geht es nicht programmiertechnisch?
Natürlich kannst du jedes CustomView dynamisch einem ContainerView zur Runtime hinzufügen, entfernen, invisible machen usw.
Dazu erstellst du dir eine Klasse, die von einem View abgeleitet ist, deine Funktionen besitzt und fügst sie dem Container mit .. addView schlichtweg hinzu.

Aber du solltest es nicht von einem Fragment ableiten.
Meinetwegen kannst du die Grundfunktionen dann auch noch aus einem BasisCustomView ableiten.

Auch kannst du jedes erstellte CustomView bereits im xml Layout deklarieren und es zur runtime schlichtweg bearbeiten.

So mache ich das tagtäglich z.b. bei medizinischen Produkten.


Kleine Anmerkung : ConstraintLayouts ist die denkbar schlechteste Lösung - Ich rate zu Relative/Linear
 
Zuletzt bearbeitet:
J

jogimuc

Erfahrenes Mitglied
Hallo programmatisch bindet du ein fragment mit der Transaktionen ein. Das machst du nicht. Du willst zwar den backstak bereinigen, aber der Transaktion übergibst du kein fragment.

Außerdem wo ist denn überhaupt dein Dialog?
Du willst wenn ich dich richtig verstehe einen Dialog aufrufen und in dem Dialog ein Fragment einbinden richtig?

Wie es aussieht willst du das alles in einen machen.
 
swa00

swa00

Moderator
Teammitglied
Jörg, ich denke , er hat noch nicht ganz die Unterschiede verstanden.
Ich versuche ihn eben darauf zu bekommen , dass er das Pferd von Hinten aufzäumt :)

Fragmente und deren Wasserkopf sind für sein Vorhaben nicht geeignet.

Er will schlichtweg funktionale (Custom)Views austauschen
 
W

washpuda

Neues Mitglied
Threadstarter
Danke erst einmal,

ich schaue mir das mit dem CustomView noch einmal an. Wichtig ist mir, einfach eine Möglichkeit zu haben mehrere Views wie Buttons, Eingabefelder etc in einem Container zu setzen, dort auch Listener für die Komponenten zu setzen. Und Das möglichst kompakt.

Ich Denke ich habe einfach nur ein tiefes Verständnissproblem, da ich sehr viel Swing verwendet habe.

In Swing erstelle ich einfach eine Klasse die vom Container abgeleitet ist. hier adde ich alle Komponenten hinzu und setze auch die Listener (gerade die Listener sind mir ja wicht). Schon habe ich alles Kompakt in einem Container daher in einer Klasse. Dieses setze ich zentral als Centerelement in meine Dialog und schon fertig.

Bestätige ich den Dialog, wird das Erreignis an meinen Conainer weitergeleitet, der dann alle Aktionen ausführt.


@ jogimuc Ich leite ja vom Dialogfragment ab, was auch alles funktioniert. Nur das Nachträgliche hinzufügen eines Fragements in den Dialog zur Laufzeit ist nicht möglich, obwohl ich das Fragment statisch über die layoul.xml einbinden kann. Wiso übergebe ich der Transaktion kein Fragment. Was mache ich falsch?
 
swa00

swa00

Moderator
Teammitglied
ich schaue mir das mit dem CustomView noch einmal an. Wichtig ist mir, einfach eine Möglichkeit zu haben mehrere Views wie Buttons, Eingabefelder etc in einem Container zu setzen, dort auch Listener für die Komponenten zu setzen. Und Das möglichst kompakt.
Dann berichte und wenn du Fragen hast - her damit :)
 
W

washpuda

Neues Mitglied
Threadstarter
Ja eine Frage hätte ich schon mal. Wie kann ich den ein Customview ein Layout zuordnen, welches ich mit dem Android Studio Editor bearbeiten kann?

Es ist der Begriff funktionale (Custom)Views gefallen. Viel findet man nicht dazu im Internet, hättet Ihr vielleicht mal ein Beispiel.
 
swa00

swa00

Moderator
Teammitglied
Ja eine Frage hätte ich schon mal. Wie kann ich den ein Customview ein Layout zuordnen, welches ich mit dem Android Studio Editor bearbeiten kann?


a) du erstellst dir eine Klasse, die du von einem View ableitest (z.b. RelativeLayout)
b) Im Klassenconstruktor bindest du mit inflate dein xml ein und übergibst dabei deinen Context
c) und nun dann kannst du in dieser Klasse, wie in in einer Activity - lustig deine Funktionen und Elemente unterbringen

Das machst du dann, sooft du magst und fügst sie mit addView deinem Mainlayout-container hinzu.
Du kannst auch dieses View im LayoutEditor deinem MainLayout statisch hinzufügen und ersparst dir somit das handling.

Bei Bedarf dann ein und ausblenden, oder was auch immer du tun magst.
 
Zuletzt bearbeitet:
J

jogimuc

Erfahrenes Mitglied
@ jogimuc Ich leite ja vom Dialogfragment ab, was auch alles funktioniert. Nur das Nachträgliche hinzufügen eines Fragements in den Dialog zur Laufzeit ist nicht möglich, obwohl ich das Fragment statisch über die layoul.xml einbinden kann. Wiso übergebe ich der Transaktion kein Fragment. Was mache ich falsch?
if (prev != null) {
ft.remove(prev);
}
ft.addToBackStack(null);
bDialog.show(ft, "dialog");

also den Backstack bereinigen und anschießend nichts neu laden ist auf jeden fall falsch.

Außerdem löschst du nicht mit „ft.addToBackStack(null);“ den Stack.
Sondern fügst einfach nichts hinzu.
genau in dieser Zeile wird der Fehler entstehen und die App abstürten.

Löschen mit getSupportFragmentManager().popBackStack();


Wenn du den Stack löschen willst dann in der if.


Du löschst einen Stack denn es noch gar nicht gibt oder in dem noch gar nichts drin ist.

Denn erst in der darauffolgenden Zeile wird dein Dialog aufgerufen.
Und auch erst jetzt wird dein findFragmentByTag("dialog") einen gültigen wert zurückgeben. vorher bestimmt nicht.


Übrigens verstehe ich nicht warum du den BackStack überhaupt löschen willst.

Der ganze Code hat aber noch nichts mit dem Fragment im Dialog zu tun. Das ist nur ein Dialog im Fragment Steil.

Ach noch etwas ein statisch hinzugefügtes fragment (xml ) kannst du nicht austauschen.
-- Dieser Beitrag wurde automatisch mit dem folgenden Beitrag zusammengeführt --
Wenn ich auf den OK-Button klicke soll das Fragment in der Mitte dargestellt werden, doch leider stürzt es hier immer ab.
Welchen OK Button meist du eigentlich? Den in der Activity oder in deinem Dialog?

PS.
Eigentlich deutet die Fehlermeldung auf den Button im Dialog hin.
Glaube wir haben hier etwas an einander vorbei geredet.

No view found for id 0x7f080066 (de.bajestro.picmaptogo:id/frameLayout) for fragment TestBDP{26d90e0 #1 id=0x7f080066} oder so ähnlich)
Code:
FragmentManager fragmentManager = getFragmentManager();
                FrameLayout layout = getView().findViewById(R.id.frameLayout);
                fragmentManager.beginTransaction().replace(layout.getId(),dialogFragment).commit();.

Ich finde in diesem Code sind einige Fehler.

getFragmentManager(); da du in einem Fragment bist brauchst du eigentlich die Achtivity.

Auch für das findViewById brauchst du die Activity nicht getView.

Wo wird eigentlich dialogFragment gesetzt?

Auch denke ich das layout.getId() nicht richtig ist. Wo ist den überhaupt layout erstellt?

Noch etwas du willst ja in deinem Dialog. Was ein fragment ist ein zweites fragment anzeigen.
In deinen framelayout als Container.

Dafür brauchst du auch eine eigene fragment Klasse die von fragment erbt. Darin brauchst du auch wieder ein Layout was da in der oncreateview inflatet wird. All das hast du nicht.

Wie ich auch schon sagte benötigst du immer den Kotext der activity. Den ein fragment läuft innerhalb der activity auch dein Dialog ist ein spezielles fragment was in der activity läuft.
Dein zweites fragment läuft in dem Dialog und somit auch in der activity.
Anstalt der activity konntest du auch die view benutzen die du in deimen Dialog inflatest. In der oncreateview.
Außerhalb brauchst du den Context der activity. Oder du machst die View als Klassen Variablen.
Denn context kannst du dir zb in der onattach holen.


ich hoffe das der Sachverhalt nun etwas klarer ist.
 
Zuletzt bearbeitet:
W

washpuda

Neues Mitglied
Threadstarter
Danke erst einmal für die vielen Antworten. Das Thema ist für mich doch noch etwas zu kompliziert, ich verstehe ja teilweise noch nicht einmal eure Fragen.

Ich hatte ursprünglich gedacht, dass es sich bei einem Fragment um ein wiederverwendbaren Block mit eigenem Layout und eigener Logik handelt, der in einer Activity oder auch einem Fragment eingebunden werden kann. Ähnlich wie bei Swing wo ein Panel einfach in ein anderes Panel zugeordnet werden kann.

Ich bilde jetzt für jeden Dialog eine eigene Activity. Damit entfällt das einbinden eines Fragments in meine Activity. Das klappt sehr gut, nachträgliche Anpassungen z.B. der Schriftart etc. muss ich dann eben für alle Dialoge durchführen.
 
Oben Unten