webentwicklung-frage-antwort-db.com.de

ActionBar verwenden Navigation in Master-/Detailfragmenten

Ich habe eine App mit Master/Detail-Layout (1 Aktivität, 1 ListView-Fragment und 1 Detailfragment). Wenn der Benutzer in der ListView auf ein Element klickt, instanziiert eine Fragmenttransaktion ein Detailfragment im rechten Fensterbereich, das die diesem Element entsprechenden Informationen enthält. Wenn das Detailfragment angezeigt wird, blende ich die Schaltflächen/Elemente der ursprünglichen Aktionsleiste aus und zeige 3 neue AB-Elemente (done/delete/cancel). Der Benutzer kann das rechte Fenster reinigen und zum ursprünglichen UI-Status zurückkehren, indem Sie entweder die Zurück-Taste drücken oder eines der drei AB-Elemente drücken.

Das Problem, das bei mir auftritt, besteht darin, dass die Aktivität erneut geladen wird, wenn der Benutzer das Home-Symbol der App auswählt (dh "Navigation nach oben") (dh die Animation, die angibt, dass die Aktivität gestartet wird, sowohl als Aktionsleiste als auch als Aktionsleiste angezeigt wird Benutzeroberfläche wurde neu gezeichnet). Das Problem tritt nur auf, wenn das App-Startsymbol gedrückt wird. Wenn der Benutzer die Zurück-Schaltfläche oder die Aktionsleisten-Schaltfläche Abbrechen/Fertigstellen/Löschen drückt, wird das Fragment einfach aus dem rechten Fensterbereich entfernt und die Benutzeroberfläche kehrt ohne "erneutes Laden" in den Ausgangszustand zurück.

Das XML-Layout für die Aktivität sieht wie folgt aus (innerhalb von LinearLayout; Prettify verbirgt diese Zeile):

<fragment class="*.*.*.ListFragment"
        Android:id="@+id/titles" Android:layout_weight="1"
        Android:layout_width="0px"
        Android:layout_height="match_parent" />

<FrameLayout Android:id="@+id/details" Android:layout_weight="2"
        Android:layout_width="0px"
        Android:layout_height="match_parent" />

Das DetailsFragement hat die Anweisung actionBar.setDisplayHomeAsUpEnabled in seiner onCreate-Methode:

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


    ActionBar actionBar = getSherlockActivity().getSupportActionBar();
    actionBar.setDisplayHomeAsUpEnabled(true);

}

Sowohl für das ListView-Fragment als auch für die Detailfragmente werden die onCreateOptionsMenu () - und die onOptionsItemSelected () - Methode in den Fragmenten implementiert. Unterhalb des Codes für das Detailfragment:

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    inflater.inflate(R.menu.edit_menu, menu);
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {

    // some variable statements...

    switch (item.getItemId()) {
        case Android.R.id.home:
            //Toast.makeText(getSherlockActivity(), "Tapped home", Toast.LENGTH_SHORT).show();
            onHomeSelectedListener.onHomeSelected();
            return true;

        case R.id.menu_edit_item_done:
            editedTask.setTitle(editedTaskTitle);
            onTaskEditedListener.onTaskEdited(editedTask, UPDATE_TASK, true);
            return true;

        default:
            return super.onOptionsItemSelected(item);

    }
}

In der Host-Aktivität implementiere ich den onHomeSelectedListner, um die App-Startsymbol-Taste zu bedienen (d. H. "Navigation nach oben"):

public void onHomeSelected(){

    FragmentManager manager = getSupportFragmentManager();
    FragmentTransaction ft = manager.beginTransaction();
    TaskFragment taskFragment = (TaskFragment)getSupportFragmentManager().findFragmentById(R.id.details);
    ft.remove(taskFragment);
    ft.commit();
    manager.popBackStack();

}

Der Listener der Aktivität, der für die Behandlung aller anderen Aktionsleisten-Schaltflächen (d. H. "Done/delete/cancel") zuständig ist, ist onTaskEditedListener. Abgesehen von anderem Code, der einige Daten verarbeitet, hat er dieselben Fragmenttransaktionen wie oben gezeigt. 

Update (1/24) Basierend auf dem Feedback von tyczj und straya habe ich log -Anweisungen in onCreate (), onResume (), onPause () der Aktivität eingefügt, um die Unterschiede zwischen den Listen onHomeSelected und onTaskEdited zu ermitteln. Ich kann bestätigen, dass während des Ereignisses "up navigation" (d. H. OnHomeSelected) onPause (), onCreate () und onResume () aufgerufen werden. Während des onTaskEdited-Aufrufs (d. H. Zurück-Taste oder Drücken von Fertig/Löschen/Abbrechen) wird keines dieser Ereignisse aufgerufen.

Update (1/25) Basierend auf einem Vorschlag von Mark Murphy habe ich den Aufruf der onHomeSelected-Methode in der "case Android.R.id.home" -Anweisung auskommentiert, nur um zu sehen, was die Aktivität bewirken würde. Der Gedanke war, dass die App nichts tun würde, da es keine Aussagen gibt. Es stellt sich heraus, dass dies nicht der Fall ist. Auch ohne einen Aufruf der Listener-Methode (d. H., Die das Fragment entfernt), wird die Aktivität erneut gestartet und das Detailfragment aus dem Fragmentcontainer entfernt.

Update (2/28) Ich habe vorübergehend die Problemumgehung behoben, dass meine Hauptaktivität durch Deaktivieren der Fensteranimationen neu gestartet wurde (wie in meiner eigenen Antwort hervorgehoben). Durch weitere Tests habe ich jedoch einen Fehler entdeckt. Dank des Beispielcodes von Wolfram Rittmeyer konnte ich während der Aufwärtsnavigation herausfinden, aus welchem ​​Grund meine Aktivitäten (im Master/Detail-Einzellayout) neu gestartet wurden: 1) Obwohl ich diesen "onHomeSelectedListener" ordnungsgemäß verwendet habe Wenn Sie das Fragment aus dem Backstack entfernen, hatte ich immer noch Restcode in onOptionsItemSelected des ListView-Fragments, das eine neue Absicht zum Starten der Hosting-Aktivität erstellte. Deshalb wurde durch Drücken des Home-Symbols der App die Aktivität erneut gestartet. 2) In meiner endgültigen Implementierung (in meiner eigenen Antwort gezeigt) habe ich den onHomeSelectedListener in der Aktivität entfernt und die StartActivity-Intention (dh den fehlerhaften Code) ersetzt. In onOptionsItemSelected des ListView wird der ursprünglich im onHomeSelectedListener enthaltene Fragment-Entfernungs- + PopBackStack-Code verwendet.

17
cavega

Nach langem Suchen und Stöbern stellt sich heraus, dass der einzige Grund, warum meine Aktivität während der "Aufwärtsnavigation" für die Master/Detail-Konfiguration neu gestartet wurde, darin lag, dass ich in onOptionsItemSelected des ListView-Fragments etwas Code hinterließ, der die Absicht hatte, die Hauptaktivität zusätzlich zu starten zu meinem vollständigen Fragment-Transaktionscode an anderer Stelle. Im Folgenden finden Sie die letzte Implementierung, mit der ich "up navigation" für die Konfiguration von Telefonen (mehrere Aktivitäten) und Tablets (einzelne Aktivität/mehrere Bereiche) verwendet habe. Dank an Wolfram Rittmeyer für ein paar Hinweise in seinem Code (Link im Kommentarbereich), die mir helfen, mein Problem zu ermitteln!

Hauptaktivität: hostet die Fragmente und führt einige andere app-spezifische Vorgänge aus

ListView Fragment: Behandelt die Navigation in Tabellenkonfiguration nach oben

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case Android.R.id.home:
            if(mDualPane){
                FragmentManager manager = getSherlockActivity().getSupportFragmentManager();
                FragmentTransaction ft = manager.beginTransaction();
                DetailFragment detailFragment = (DetailFragment)manager.findFragmentById(R.id.details);
                ft.remove(detailFragment);
                ft.commit();
                manager.popBackStack();
                getSherlockActivity().getSupportActionBar().setDisplayHomeAsUpEnabled(false);
                getSherlockActivity().getSupportActionBar().setHomeButtonEnabled(false);
            }
            return true;

        // Other case statements...

        default:
            return super.onOptionsItemSelected(item);
    }
}

Details Fragment: Steuert die Navigation in der Telefonkonfiguration

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

    // Sets "up navigation" for both phone/tablet configurations
    ActionBar actionBar = getSherlockActivity().getSupportActionBar();
    actionBar.setDisplayHomeAsUpEnabled(true);
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {

    switch (item.getItemId()) {
        case Android.R.id.home:
            if(!mDualPane){
                Intent parentActivityIntent = new Intent(getSherlockActivity(), MainActivity.class);
                parentActivityIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
                startActivity(parentActivityIntent);
                getSherlockActivity().finish();
            }
            return true;

        // Other case statements...

        default:
            return super.onOptionsItemSelected(item);
    }

}
8
cavega

Wenn Sie das Navigation Design Pattern betrachten, werden Sie sehen, dass Sie nach dem Drücken der Home-Taste zur Startaktivität zurückkehren möchten.

Angenommen, Sie haben 2 Aktivitäten, die sie A1 und A2 nennen. Durch Klicken auf etwas in A1 gelangen Sie zu A2. Wenn der Benutzer die Starttaste drückt, sollten Sie ihn zu A1 zurückbringen, um den Stapel von allem bis zu dieser Aktivität zu löschen

Intent intent = new Intent(this, A1.class);  
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);

dies ist, was die Flagge Intent.FLAG_ACTIVITY_CLEAR_TOP tut

Wenn festgelegt, und die Aktivität, die gerade gestartet wird, bereits in der aktuellen Task ausgeführt wird, werden anstelle des Startens einer neuen Instanz dieser Aktivität alle anderen Aktivitäten, die sich darüber befinden, geschlossen, und diese Absicht wird an die (jetzt an) übergeben oben) alte Tätigkeit als neue Absicht.

Stellen Sie sich beispielsweise eine Aufgabe vor, die aus den Aktivitäten A, B, C, D besteht. Wenn D startActivity () mit einem Intent aufruft, der die Komponente von Aktivität B auflöst, werden C und D beendet und B erhält den angegebenen Intent , was dazu führt, dass der Stapel jetzt ist: A, B.` 

Die aktuell ausgeführte Instanz von Aktivität B im obigen Beispiel erhält entweder die neue Absicht, die Sie hier in ihrer onNewIntent () - Methode beginnen, oder sie wird selbst beendet und mit der neuen Absicht neu gestartet. Wenn der Startmodus als "mehrfach" (Standardeinstellung) festgelegt wurde und Sie FLAG_ACTIVITY_SINGLE_TOP nicht in derselben Absicht festgelegt haben, wird der Vorgang abgeschlossen und erneut erstellt. Für alle anderen Startmodi oder wenn FLAG_ACTIVITY_SINGLE_TOP gesetzt ist, wird diese Absicht an die aktuelle Instanz onNewIntent () übergeben.

Dieser Startmodus kann auch in Verbindung mit FLAG_ACTIVITY_NEW_TASK verwendet werden: Wenn er zum Starten der Stammaktivität einer Aufgabe verwendet wird, werden alle derzeit ausgeführten Instanzen dieser Aufgabe in den Vordergrund gebracht und in den Stammstatus zurückgesetzt. Dies ist besonders nützlich, wenn Sie beispielsweise eine Aktivität vom Benachrichtigungsmanager aus starten.

3
tyczj

Ich denke, Sie sollten die Aufwärts-Schaltfläche nur innerhalb der Aktivität behandeln. Wenn Sie sich in einem Telefon befinden, wird die Aufwärts-Schaltfläche durch Aktivitäten behandelt, die als Wrapper dieses Fragments fungieren, in einem Tablett (Master/Detail-Muster) nicht gewünscht es sowieso

0
urSus

don't: break und dann super.onOptionsItemSelected (Element) zurückgeben, sondern nur: return true;

AKTUALISIEREN:

Sie sagen also, dass die Aktivität basierend auf dem, was Sie mit Views sehen, "erneut gestartet" wird. Können Sie jedoch durch die Protokollierung der verschiedenen Lebenszyklusmethoden bestätigen, was mit der Aktivität (und Fragments für diese Angelegenheit) passieren kann oder nicht? Auf diese Weise können Sie sicher sein, was das aktuelle (fehlerhafte) Verhalten ist, bevor Sie mit der Diagnose fortfahren.

AKTUALISIEREN:

OK, gut um sicher zu sein, was das Verhalten angeht:) .__ Nun zu Ihrer Frage "Wie implementiere ich die" Aufwärtsnavigation "für ein Master/Detail-Layout (1 Aktivität/2Fragmente)?": ist, dass die 2 Fragmente innerhalb einer einzigen FragmentTransaction hinzugefügt wurden und Sie einfach popBackStack verwenden, um sie zu entfernen und zu dem vorherigen Status zurückzukehren. Ich denke, Sie verdoppeln sich, indem Sie ein Fragment manuell in einer FragmentTransaction entfernen und dann den Backstack platzieren. Versuchen Sie es einfach mit popBackStack. Und um sicher zu gehen, dass Sie ActionBarSherlock und support.v4 verwenden, verwenden Sie eine FragmentActivity (und nicht eine Activity) und SherlockFragment?

0
straya