Umschalten zwischen Android Navigation Drawer-Bild und Up Caret bei Verwendung von Fragmenten

177

Bei Verwendung der Navigationsleiste empfehlen die Android-Entwickler, dass in der Aktionsleiste "nur die Bildschirme, die in der Navigationsleiste dargestellt werden, tatsächlich das Bild der Navigationsleiste haben sollten" und dass "alle anderen Bildschirme das traditionelle Up-Carat-Format haben".

Einzelheiten finden Sie hier: http://youtu.be/F5COhlbpIbY

Ich verwende eine Aktivität, um mehrere Ebenen von Fragmenten zu steuern, und kann das Bild der Navigationsschublade auf allen Ebenen anzeigen und funktionieren lassen.

Beim Erstellen von Fragmenten niedrigerer Ebenen kann ich das aufrufen ActionBarDrawerToggle setDrawerIndicatorEnabled(false), um das Bild der Navigationsschublade auszublenden und das Caret Up anzuzeigen

LowerLevelFragment lowFrag = new LowerLevelFragment();

//disable the toggle menu and show up carat
theDrawerToggle.setDrawerIndicatorEnabled(false);
getSupportFragmentManager().beginTransaction().replace(R.id.frag_layout, 
lowFrag, "lowerFrag").addToBackStack(null).commit();

Das Problem, das ich habe, ist, wenn ich zurück zu den Fragmenten der obersten Ebene navigiere, die das Up-Karat anstelle des ursprünglichen Navigationsschubladenbilds weiterhin anzeigt. Irgendwelche Vorschläge, wie Sie die ActionBar auf den Fragmenten der obersten Ebene "aktualisieren" können, um das Bild der Navigationsleiste wieder anzuzeigen?


Lösung

Toms Vorschlag hat bei mir funktioniert. Folgendes habe ich getan:

Hauptaktivität

Diese Aktivität steuert alle Fragmente in der App.

Wenn ich neue Fragmente vorbereite, um andere zu ersetzen, stelle ich den DrawerToggle folgendermaßen ein setDrawerIndicatorEnabled(false):

LowerLevelFragment lowFrag = new LowerLevelFragment();

//disable the toggle menu and show up carat
theDrawerToggle.setDrawerIndicatorEnabled(false);
getSupportFragmentManager().beginTransaction().replace(R.id.frag_layout,   
lowFrag).addToBackStack(null).commit();

Als nächstes habe onBackPressedich in einem Override von das oben Gesagte rückgängig gemacht, indem ich DrawerToggle so eingestellt habe, dass es setDrawerIndicatorEnabled(true)wie folgt aussieht :

@Override
public void onBackPressed() {
    super.onBackPressed();
    // turn on the Navigation Drawer image; 
    // this is called in the LowerLevelFragments
    setDrawerIndicatorEnabled(true)
}

In den LowerLevelFragments

In den Fragmenten habe ich modifiziert onCreateund onOptionsItemSelectedmag das:

Wird onCreatehinzugefügt setHasOptionsMenu(true), um die Konfiguration des Optionsmenüs zu aktivieren. Auch setzen setDisplayHomeAsUpEnabled(true)die aktivieren < in der Aktionsleiste :

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // needed to indicate that the fragment would 
    // like to add items to the Options Menu        
    setHasOptionsMenu(true);    
    // update the actionbar to show the up carat/affordance 
    getActivity().getActionBar().setDisplayHomeAsUpEnabled(true);
}

Dann , onOptionsItemSelectedwenn die < gedrückt wird es ruft die onBackPressed()von der Aktivität einer Ebene in der Hierarchie nach oben und in der Navigationsleiste Bild anzuzeigen:

@Override
public boolean onOptionsItemSelected(MenuItem item) {   
    // Get item selected and deal with it
    switch (item.getItemId()) {
        case android.R.id.home:
            //called when the up affordance/carat in actionbar is pressed
            getActivity().onBackPressed();
            return true;
         
    }
EvilAsh
quelle
2
Außerdem können Sie in Ihrer onBackPressed () -Methode mit der Methode getFragmentManager (). GetBackStackEntryCount () überprüfen, wie viele Einträge sich im Backstack befinden, und die Schubladenanzeige nur aktivieren, wenn das Ergebnis 0 ist. In diesem Fall ist es nicht erforderlich, homeAsUpIndicator in jedem LowerLevelFragments zu aktivieren.
Pvshnik
2
Das ist sehr nützlich! Sie sollten den "Lösungsteil" Ihres Beitrags verschieben und ihn zu einer tatsächlichen "Antwort" machen. Sie erhalten mehr Punkte für Upvotes und es ist immerhin eine Antwort
Oleksiy
Warum ersetzen Sie das Fragment hier : .replace(R.id.frag_layout. Wenn dies eine weitere Hierarchieebene ist, würde ich erwarten, dass Sie .addes in den Backstack.
JJD
5
Bro, wie verweisen Sie auf das theDrawerToggle.setDrawerIndicatorEnabled(false);Innere des Fragments? Ich denke, es ist in der Hauptaktivitätsklassendatei deklariert. Ich kann keinen Weg finden, darauf zu verweisen. Irgendwelche Hinweise?
Skynet
1
Bei Verwendung einer Symbolleiste musste ich die Anzeigeoptionen ändern, um das Home in der Zwischenzeit nicht wie oben zu verwenden. Andernfalls würde die setDisplayOptions()Methode im ToolbarWidgetWrapper(des internen Pakets android.support.v7.internal.widget) das Symbol nicht neu erstellen, wenn dasselbe Fragment ein zweites Mal eingegeben wird. Lassen Sie dies einfach hier, wenn andere ebenfalls auf dieses Problem stoßen.
Wolfram Rittmeyer

Antworten:

29

Sie haben geschrieben, dass Sie zum Implementieren von Fragmenten niedrigerer Ebene das vorhandene Fragment ersetzen, anstatt das Fragment niedrigerer Ebene in einer neuen Aktivität zu implementieren.

Ich würde denken, dass Sie dann die Zurück-Funktionalität manuell implementieren müssten: Wenn der Benutzer zurückgedrückt hat, haben Sie Code, der den Stapel öffnet (z Activity::onBackPressed. B. beim Überschreiben). Wo immer Sie das tun, können Sie das umkehren setDrawerIndicatorEnabled.

Tom
quelle
Danke Tom, das hat funktioniert! Ich habe den ursprünglichen Beitrag mit der von mir verwendeten Lösung aktualisiert.
EvilAsh
6
Wie verweise ich auf theDrawerToggle in einem Fragment niedrigerer Ebene? Es wurde in der Hauptaktivität definiert, ich kann mich nicht darum kümmern!
Skynet
1
Sie können die Aktivität aus dem Fragment abrufen. Fügen Sie Ihrer Hauptaktivität einfach einen Getter hinzu, um auf den Schubladenschalter umzuschalten.
Raphael Royer-Rivard
83

Es ist so einfach wie 1-2-3.

Wenn Sie erreichen wollen:

1) Schubladenanzeige - wenn sich keine Fragmente im hinteren Stapel befinden oder die Schublade geöffnet ist

2) Pfeil - wenn sich einige Fragmente im hinteren Stapel befinden

private FragmentManager.OnBackStackChangedListener
        mOnBackStackChangedListener = new FragmentManager.OnBackStackChangedListener() {
    @Override
    public void onBackStackChanged() {
        syncActionBarArrowState();
    }
};

@Override
protected void onCreate(Bundle savedInstanceState) {
    getSupportActionBar().setDisplayShowHomeEnabled(true);
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    mDrawerToggle = new ActionBarDrawerToggle(
            this,             
            mDrawerLayout,  
            R.drawable.ic_navigation_drawer, 
            0, 
            0  
    ) {

        public void onDrawerClosed(View view) {
            syncActionBarArrowState();
        }

        public void onDrawerOpened(View drawerView) {
            mDrawerToggle.setDrawerIndicatorEnabled(true);
        }
    };

    mDrawerLayout.setDrawerListener(mDrawerToggle);
    getSupportFragmentManager().addOnBackStackChangedListener(mOnBackStackChangedListener);
}

@Override
protected void onDestroy() {
    getSupportFragmentManager().removeOnBackStackChangedListener(mOnBackStackChangedListener);
    super.onDestroy();
}

private void syncActionBarArrowState() {
    int backStackEntryCount = 
        getSupportFragmentManager().getBackStackEntryCount();
    mDrawerToggle.setDrawerIndicatorEnabled(backStackEntryCount == 0);
}

3) Beide Indikatoren müssen entsprechend ihrer Form wirken

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    if (mDrawerToggle.isDrawerIndicatorEnabled() && 
        mDrawerToggle.onOptionsItemSelected(item)) {
        return true;
    } else if (item.getItemId() == android.R.id.home && 
               getSupportFragmentManager().popBackStackImmediate()) {
        return true;
    } else {
        return super.onOptionsItemSelected(item);
    }
}

PS Siehe Weitere Tipps zum Verhalten der dreizeiligen Anzeige finden Erstellen einer Navigationsleiste für Android-Entwickler .

riwnodennyk
quelle
3
Am Ende hatte ich etwas Ähnliches wie deins. Ich denke, diese Lösung (mit dem BackStackChangedListener, um zwischen den gezeigten zu wechseln) ist die eleganteste. Ich habe jedoch zwei Änderungen: 1) Ich rufe die Schubladenanzeige in den Aufrufen onDrawerClosed / onDrawerOpened nicht auf / ändere sie nicht - sie wird nicht benötigt, und 2) Ich lasse die Fragmente selbst die Aufwärtsnavigation handhaben, indem ich ein AbstractFragment erstelle, das sie alle erben implementiert onOptionsItemSelected (..) und ruft immer setHasOptionsMenu (true) auf;
Espen Riskedal
2
Yout sollte auch die Wischgeste für die Navigationsschublade im Pfeilmodus sperren: mDrawerLayout.setDrawerLockMode (DrawerLayout.LOCK_MODE_LOCKED_CLOSED); und aktivieren Sie es wieder im Schubladenanzeigemodus
artkoenig
5
All dies habe ich in meinem NavigationDrawer-Fragment getan. Funktioniert übrigens wie ein Zauber, danke.
frostymarvelous
1
setActionBarArrowDependingOnFragmentsBackStack()... was für ein langer Name: P
S.Thiongane
2
Dies zeigt die Aufwärtsschaltfläche für mich nicht an, wenn ich von Fragment A nach B gehe und A zum Backstack hinzufüge. :(
Aandis
14

Ich habe als nächstes verwendet:

getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
            @Override
            public void onBackStackChanged() {
                if(getSupportFragmentManager().getBackStackEntryCount() > 0){
                    mDrawerToggle.setDrawerIndicatorEnabled(false);
                    getSupportActionBar().setDisplayHomeAsUpEnabled(true);
                }
                else {
                    getSupportActionBar().setDisplayHomeAsUpEnabled(false);
                    mDrawerToggle.setDrawerIndicatorEnabled(true);
                }
            }
        });
Yuriy Sych
quelle
1
Vielen Dank, dies ist die einzige, die tatsächlich funktioniert hat. Sobald ich dies hinzufüge, wird drawerToggle.setToolbarNavigationClickListener(dieser Listener aufgerufen, wenn auf den Pfeil
geklickt wird
Danke @Yuriy, dies hat mir bei der Lösung meines Problems geholfen
Muhammad Shoaib Murtaza
12

Wenn die Schaltfläche in der Aktionsleiste nicht funktioniert, vergessen Sie nicht, den Listener hinzuzufügen:

// Navigation back icon listener
mDrawerToggle.setToolbarNavigationClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            onBackPressed();
        }
});

Ich habe einige Probleme beim Implementieren einer Schubladennavigation mit einer Home-Taste. Bis auf den Aktionsknopf hat alles funktioniert.

Burrich
quelle
10

Versuchen Sie, die Auswahl des Home-Elements in der MainActivity abhängig vom Status des DrawerToggle zu behandeln. Auf diese Weise müssen Sie nicht jedem Fragment denselben Code hinzufügen.

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Only handle with DrawerToggle if the drawer indicator is enabled.
    if (mDrawerToggle.isDrawerIndicatorEnabled() &&
            mDrawerToggle.onOptionsItemSelected(item)) {
        return true;
    }
    // Handle action buttons
    switch (item.getItemId()) {
        // Handle home button in non-drawer mode
        case android.R.id.home:
            onBackPressed();
            return true;

        default:
            return super.onOptionsItemSelected(item);
    }
}
dzeikei
quelle
+1 saubere Lösung. Damit Ihre Antwort vollständig verwendet werden kann, sollten Sie den Backstack überprüfen. Wenn es leer ist, setzen Sie die Schubladenanzeige automatisch auf true.
HpTerm
@HpTerm Ich kümmere mich um den Backstack, onBackPressed()weil ich für beide das gleiche Verhalten wollte.
Dzeikei
6

NACHVERFOLGEN

Die von @dzeikei angegebene Lösung ist ordentlich, kann jedoch bei Verwendung von Fragmenten erweitert werden, um das Zurücksetzen der Schubladenanzeige bei leerem Backstack automatisch zu handhaben.

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Only handle with DrawerToggle if the drawer indicator is enabled.
    if (mDrawerToggle.isDrawerIndicatorEnabled() &&
            mDrawerToggle.onOptionsItemSelected(item)) {
        return true;
    }
    // Handle action buttons
    switch (item.getItemId()) {
        // Handle home button in non-drawer mode
        case android.R.id.home:
            // Use getSupportFragmentManager() to support older devices
            FragmentManager fragmentManager = getFragmentManager();
            fragmentManager.popBackStack();
            // Make sure transactions are finished before reading backstack count
            fragmentManager.executePendingTransactions();
            if (fragmentManager.getBackStackEntryCount() < 1){
                mDrawerToggle.setDrawerIndicatorEnabled(true);  
            }
            return true;

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

BEARBEITEN

Für die Frage von @JJD.

Die Fragmente werden in einer Aktivität gehalten / verwaltet. Der obige Code wird einmal in dieser Aktivität geschrieben, behandelt aber nur das Up Caret für das onOptionsItemSelected.

In einer meiner Apps musste ich auch das Verhalten des Up Caret behandeln, wenn die Zurück-Taste gedrückt wurde. Dies kann durch Überschreiben behandelt werden onBackPressed.

@Override
public void onBackPressed() {
    // Use getSupportFragmentManager() to support older devices
    FragmentManager fragmentManager = getFragmentManager();
    fragmentManager.executePendingTransactions();
    if (fragmentManager.getBackStackEntryCount() < 1){
        super.onBackPressed();
    } else {
        fragmentManager.executePendingTransactions();
        fragmentManager.popBackStack();
        fragmentManager.executePendingTransactions();
        if (fragmentManager.getBackStackEntryCount() < 1){
            mDrawerToggle.setDrawerIndicatorEnabled(true);
        }
    }
};

Beachten Sie die Codeduplizierung zwischen onOptionsItemSelectedund, onBackPresseddie vermieden werden kann, indem Sie eine Methode erstellen und diese Methode an beiden Stellen aufrufen.

Beachten Sie auch, dass ich zwei weitere Male hinzufüge, executePendingTransactionswas in meinem Fall erforderlich war, oder dass ich manchmal seltsame Verhaltensweisen des Up Caret hatte.

HpTerm
quelle
Ist dies der vollständige Code, den ich hinzufügen muss, um das Up- Caret-Verhalten zu implementieren, oder verweisen Sie auf einen der anderen Beiträge? Bitte klären Sie dies.
JJD
Danke für die Bearbeitung. Eigentlich halte ich mDrawerToggleinnerhalb einer NavigationDrawerFragmentKlasse. Damit es funktioniert, muss ich auch den Status der Home-Taste / Anzeige umschalten - siehe : NavigationDrawerFragment#toggleDrawerIndicator. Außerdem bin ich mir nicht sicher, ob Sie den ersten Check-in benötigen onOptionsItemSelected: Ich habe ihn auskommentiert. - Vereinfachtes Beispiel
JJD
Überarbeitete Implementierung : Sie haben Recht mit dem ersten Einchecken onOptionsItemSelected. Dadurch wird sichergestellt, dass die Navigationsleiste weiterhin in der Hierarchie der obersten Ebene geöffnet wird. Ich habe den Code jedoch in die verschoben NavigationDrawerFragment#onOptionsItemSelected. Das hilft mir , nicht zu belichten , mDrawerToggleum die MainActivity.
JJD
@JJD Ich erinnerte mich, dass ich ein wenig zu kämpfen hatte, weil für jede Hierarchieebene alles für das Up Caret funktionierte. Solange es auch für Sie funktioniert, ist das in Ordnung. Natürlich können Sie den Code, wie Sie sagen, an eine andere Stelle verschieben, um ihn nicht verfügbar zu machen.
HpTerm
2

Ich habe eine Schnittstelle für die Hosting-Aktivität erstellt, um den Ansichtsstatus des Hamburger-Menüs zu aktualisieren. Für Fragmente der obersten Ebene setze ich den Umschalter auf trueund für Fragmente, für die ich den Aufwärtspfeil anzeigen möchte, auf den ich den Umschalter eingestellt habe false.

public class SomeFragment extends Fragment {

    public interface OnFragmentInteractionListener {
        public void showDrawerToggle(boolean showDrawerToggle);
    }

    private OnFragmentInteractionListener mListener;

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            this.mListener = (OnFragmentInteractionListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement OnFragmentInteractionListener");
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        mListener.showDrawerToggle(false);
    }
}

Dann in meiner Tätigkeit ...

public class MainActivity extends Activity implements SomeFragment.OnFragmentInteractionListener {

    private ActionBarDrawerToggle mDrawerToggle;

    public void showDrawerToggle(boolean showDrawerIndicator) {
        mDrawerToggle.setDrawerIndicatorEnabled(showDrawerIndicator);
    }

}
Bill Mote
quelle
2

Diese Antwort funktionierte, aber es gab ein kleines Problem damit. Das getSupportActionBar().setDisplayHomeAsUpEnabled(false)wurde nicht explizit aufgerufen und führte dazu, dass das Schubladensymbol ausgeblendet wurde, selbst wenn sich keine Elemente im Backstack befanden. Daher funktionierte das Ändern der setActionBarArrowDependingOnFragmentsBackStack()Methode für mich.

private void setActionBarArrowDependingOnFragmentsBackStack() {
        int backStackEntryCount = getSupportFragmentManager()
                .getBackStackEntryCount();
        // If there are no items in the back stack
        if (backStackEntryCount == 0) {
            // Please make sure that UP CARAT is Hidden otherwise Drawer icon
            // wont display
            getSupportActionBar().setDisplayHomeAsUpEnabled(false);
            // Display the Drawer Icon
            mDrawerToggle.setDrawerIndicatorEnabled(true);
        } else {
            // Show the Up carat
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
            // Hide the Drawer Icon
            mDrawerToggle.setDrawerIndicatorEnabled(false);
        }

    }
Sheraz Ahmad Khilji
quelle
In meinem Fall verwendete die rigth-Lösung nur actionBarDrawerToggle.setDrawerIndicatorEnabled (getSupportFragmentManager (). getBackStackEntryCount () <0);
Gorets
1

Die Logik ist klar. Schaltfläche Zurück anzeigen, wenn der Fragmentstapel gelöscht ist. Zeigen Sie eine Material-Hamburger-Back-Animation an, wenn der Fragmentstapel nicht klar ist.

getSupportFragmentManager().addOnBackStackChangedListener(
    new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            syncActionBarArrowState();
        }
    }
);


private void syncActionBarArrowState() {
    int backStackEntryCount = getSupportFragmentManager().getBackStackEntryCount();
    mNavigationDrawerFragment.setDrawerIndicatorEnabled(backStackEntryCount == 0);
}

//add these in Your NavigationDrawer fragment class

public void setDrawerIndicatorEnabled(boolean flag){
    ActionBar actionBar = getActionBar();
    if (!flag) {
        mDrawerToggle.setDrawerIndicatorEnabled(false);
        actionBar.setDisplayHomeAsUpEnabled(true);
        mDrawerToggle.setHomeAsUpIndicator(getColoredArrow());
    } else {
        mDrawerToggle.setDrawerIndicatorEnabled(true);
    }
    mDrawerToggle.syncState();
    getActivity().supportInvalidateOptionsMenu();
}

//download back button from this(https://www.google.com/design/icons/) website and add to your project

private Drawable getColoredArrow() {
    Drawable arrowDrawable = ContextCompat.getDrawable(getActivity(), R.drawable.ic_arrow_back_black_24dp);
    Drawable wrapped = DrawableCompat.wrap(arrowDrawable);

    if (arrowDrawable != null && wrapped != null) {
        // This should avoid tinting all the arrows
        arrowDrawable.mutate();
        DrawableCompat.setTint(wrapped, Color.GRAY);
    }
    return wrapped;
}
kml_ckr
quelle
1

Wenn Sie sich die GMAIL-App ansehen und hierher kommen, um nach dem Carret- / Erschwinglichkeitssymbol zu suchen.

Ich würde Sie bitten, dies zu tun, keine der obigen Antworten war klar. Ich konnte die akzeptierte Antwort ändern.

  • NavigationDrawer -> Listview enthält Unterfragmente.


  • Unterfragmente werden so aufgelistet

  • firstFragment == position 0 ---> dies hat Unterfragmente -> Fragment

  • secondFragment
  • Drittes Fragment und so weiter ....

In firstFragment haben Sie ein anderes Fragment.

Rufen Sie dies bei DrawerActivity auf

getFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            if (getFragmentManager().getBackStackEntryCount() > 0) {
                mDrawerToggle.setDrawerIndicatorEnabled(false);
            } else {
                mDrawerToggle.setDrawerIndicatorEnabled(true);
            }
        }
    });

und in Fragment

    setHasOptionsMenu(true);    

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Get item selected and deal with it
    switch (item.getItemId()) {
        case android.R.id.home:
            //called when the up affordance/carat in actionbar is pressed
            activity.onBackPressed();
            return true;
    }
    return false;
}

Setzen Sie bei der Aktivitätsmethode OnBackPressed Drawer den Drawer-Schalter auf true, um das Navigationslistensymbol wieder zu aktivieren.

Danke, Pusp

EngineSense
quelle
0

IMO ist die Verwendung von onNavigateUp () (wie hier gezeigt ) in der Lösung von riwnodennyk oder Tom sauberer und scheint besser zu funktionieren. Ersetzen Sie einfach den onOptionsItemSelected-Code durch diesen:

@Override
public boolean onSupportNavigateUp() {
    if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
        // handle up navigation
        return true;
    } else {
        return super.onSupportNavigateUp();
    }
}
0101100101
quelle