webentwicklung-frage-antwort-db.com.de

Deaktivieren Sie die Hamburger-Pfeil-Animation in der Symbolleiste

Es ist sehr einfach, Toolbar mit der Hamburger-Pfeil-Animation zu implementieren. Meiner Meinung nach ist diese Animation sinnlos, da die Navigationsschublade nach Materialdesign die Toolbar beim Öffnen abdeckt. Meine Frage ist, wie man diese Animation richtig deaktiviert und mit getSupportActionBar().setDisplayHomeAsUpEnabled(true); entweder Hamburger oder Rückwärtspfeil zeigt.

So habe ich es gemacht, aber es sieht aus wie ein dreckiger Hack:

mDrawerToggle.setDrawerIndicatorEnabled(false);

if (showHomeAsUp) {
    mDrawerToggle.setHomeAsUpIndicator(R.drawable.lib_ic_arrow_back_light);
    mDrawerToggle.setToolbarNavigationClickListener(view -> finish());
} else {
    mDrawerToggle.setHomeAsUpIndicator(R.drawable.lib_ic_menu_light);
    mDrawerToggle.setToolbarNavigationClickListener(view -> toggleDrawer());
}

Gibt es Hinweise, wie dies richtig implementiert werden sollte, um mit setDisplayHomeAsUpEnabled zwischen Hamburger- und Rückwärtspfeilsymbolen zu wechseln?

29
tomrozb

Dadurch wird die Animation deaktiviert, wenn Sie das drawerToggle erstellen und onDrawerSlide () überschreiben:

drawerToggle = new ActionBarDrawerToggle(this, drawerLayout,
        getToolbar(), R.string.open, R.string.close) {

    @Override
    public void onDrawerClosed(View view) {
        super.onDrawerClosed(view);
    }

    @Override
    public void onDrawerOpened(View drawerView) {
        super.onDrawerOpened(drawerView);
    }

    @Override
    public void onDrawerSlide(View drawerView, float slideOffset) {
        super.onDrawerSlide(drawerView, 0); // this disables the animation 
    }
};

Wenn Sie den Pfeil vollständig entfernen möchten, können Sie hinzufügen 

 super.onDrawerSlide(drawerView, 0); // this disables the arrow @ completed state

am Ende der Funktion onDrawerOpened.

47
Frank

Meiner Meinung nach ist diese Animation sinnlos

Nun, ActionBarDrawerToggle soll animiert werden. 

Aus den Dokumenten:

Sie können den animated -Schalter anpassen, indem Sie den .__ definieren. drawerArrowStyle in Ihrem ActionBar-Design.

Hinweise, wie dies richtig implementiert werden sollte, um nur .__ zu verwenden. setDisplayHomeAsUpEnabled zum Wechseln zwischen Hamburger und Rückwärtspfeil Icons?

Die Variable ActionBarDrawerToggle ist nur eine ausgefallene Methode, um ActionBar.setHomeAsUpIndicator aufzurufen. Sie müssen also entweder ActionBar.setDisplayHomeAsUpEnabled in true aufrufen, um sie anzuzeigen.

Wenn Sie überzeugt sind, dass Sie es verwenden müssen, dann würde ich vorschlagen, dass Sie einfach ActionBarDrawerToggle.onDrawerOpened(View drawerView) und ActionBarDrawerToggle.onDrawerClosed(View drawerView) aufrufen. 

Dadurch wird die Position DrawerIndicator auf 1 oder 0 gesetzt, wobei zwischen dem Pfeil und den Hamburgerzuständen der DrawerArrowDrawable gewechselt wird. 

In Ihrem Fall müssen Sie nicht einmal eine ActionBarDrawerToggle als DrawerLayout.DrawerListener anhängen. Wie in:

mYourDrawer.setDrawerListener(mYourDrawerToggle);

Ein weitaus vorwärts gerichteter Ansatz wäre, einmal ActionBar.setHomeAsUpIndicator aufzurufen und Ihr eigenes Hamburger-Symbol anzuwenden. Sie können dies auch über einen Stil tun. Wenn Sie dann den Rückwärtspfeil anzeigen möchten, rufen Sie einfach ActionBar.setDisplayHomeAsUpEnabled auf und lassen Sie AppCompat oder das Framework den Rest erledigen. Ich bin mir ziemlich sicher, dass Sie nach den Kommentaren gesucht haben.

Wenn Sie sich nicht sicher sind, welches Symbol verwendet werden soll, ist die Standardgröße von DrawerArrowDrawable24dp . Dies bedeutet, dass Sie den ic_menu_white_24dp oder ic_menu_black_24dp aus dem navigation icon set im offiziellen Material-Design-Icon-Pack von Google verwenden möchten. 

Sie können auch die Variable DrawerArrowDrawable in Ihr Projekt kopieren und dann den Pfeil- oder Hamburgerstatus ändern, wenn Sie sie benötigen. Es ist in sich abgeschlossen, abgesehen von ein paar Ressourcen. 

12
adneal

Dies ist meine Funktion zum Steuern des ActionBarDrawableToggle im NavigationDrawerFragment, das ich im onActivityCreated-Callback jedes Fragments aufrufe. Postfunktionen sind notwendig. Das Hamburger-Symbol ändert sich in den hinteren Pfeil und der hintere Pfeil ist anklickbar. Orientierungsänderungen werden von den Handlern ordnungsgemäß behandelt.

...

import Android.support.v7.app.ActionBar;
import Android.support.v7.app.ActionBarActivity;
import Android.support.v7.app.ActionBarDrawerToggle;

...

public class NavigationDrawerFragment extends Fragment
{
    private ActionBarDrawerToggle mDrawerToggle;

    ...

    public void syncDrawerState()
    {
       new Handler().post(new Runnable()
        {
            @Override
            public void run()
            {
                final ActionBar actionBar = activity.getSupportActionBar();
                if (activity.getSupportFragmentManager().getBackStackEntryCount() > 1 && (actionBar.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != ActionBar.DISPLAY_HOME_AS_UP)
                {
                    new Handler().post(new Runnable()
                    {
                        @Override
                        public void run()
                        {
                            mDrawerToggle.setDrawerIndicatorEnabled(false);
                            actionBar.setDisplayHomeAsUpEnabled(true);
                            mDrawerToggle.setToolbarNavigationClickListener(onToolbarNavigationClickListener());
                        }
                    });
                } else if (activity.getSupportFragmentManager().getBackStackEntryCount() <= 1 && (actionBar.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) == ActionBar.DISPLAY_HOME_AS_UP)
                {
                    actionBar.setHomeButtonEnabled(false);
                    actionBar.setDisplayHomeAsUpEnabled(false);
                    mDrawerToggle.setDrawerIndicatorEnabled(true);
                    mDrawerToggle.syncState();
                }
            }
        });      
    }
}

Dies ist nur meine onActivityCreated-Methode in meinem Basisfragment.

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState)
{
    super.onActivityCreated(savedInstanceState);
    navigationDrawerFragment.syncDrawerState();
}
4
r1m

Ich hatte eine ähnliche Anforderung und verbrachte einige Zeit damit, ActionBarDrawerToggle-Code durchzugehen. Was Sie derzeit haben, ist der beste Weg nach vorne. 

Da kommt noch mehr:

Die Hamburger-zu-Pfeil-Animation wird durch eine zeichnerische Implementierung bereitgestellt - DrawerArrowDrawableToggle. Derzeit haben wir nicht viel Kontrolle darüber, wie dieses Zeichen auf Schubladenzustände reagiert. Das sagt der Package-Access-Konstruktor für actionVarDrawerToggle:

/**
 * In the future, we can make this constructor public if we want to let developers customize
 * the
 * animation.
 */
<T extends Drawable & DrawerToggle> ActionBarDrawerToggle(Activity activity, Toolbar toolbar,
        DrawerLayout drawerLayout, T slider,
        @StringRes int openDrawerContentDescRes,
        @StringRes int closeDrawerContentDescRes)

Indem Sie Ihre eigene Implementierung von slider bereitstellen, können Sie steuern, wie es auf Schubladenzustände reagiert. Die Schnittstelle, die slider implementieren muss:

/**
 * Interface for toggle drawables. Can be public in the future
 */
static interface DrawerToggle {

    public void setPosition(float position);

    public float getPosition();
}

setPosition(float) ist das Highlight hier - alle Statusänderungen der Schubladen fordern dazu auf, die Schubladenanzeige zu aktualisieren. 

Für das von Ihnen gewünschte Verhalten würde Ihre slider-Implementierung setPosition(float position) nichts tun. 

Sie werden noch brauchen:

if (showHomeAsUp) {
    mDrawerToggle.setDrawerIndicatorEnabled(false);
    // Can be set in theme
    mDrawerToggle.setHomeAsUpIndicator(R.drawable.lib_ic_arrow_back_light);
    mDrawerToggle.setToolbarNavigationClickListener(view -> finish());
}

Wenn Sie setDrawerIndicatorEnabled(false) nicht tun, wird das mit setToolbarNavigationClickListener(view -> finish()); eingestellte OnClickListener nicht ausgelöst.

Was können wir richtig machen jetzt?

Bei genauerer Betrachtung finde ich, dass in ActionBarDrawerToggle eine Rückstellung für Ihren Bedarf vorhanden ist. Ich finde diese Bestimmung noch mehr als ein Hack, als Sie derzeit haben. Aber ich lasse dich entscheiden.

Mit ActionBarDrawerToggle können Sie einige die Schubladenanzeige über die Schnittstelle Delegate steuern. Sie können diese Schnittstelle auf folgende Weise von Ihrer Aktivität implementieren lassen:

public class TheActivity extends ActionBarActivity implements ActionBarDrawerToggle.Delegate {
....

    @Override
    public void setActionBarUpIndicator(Drawable drawableNotUsed, int i) {

        // First, we're not using the passed drawable, the one that animates

        // Second, we check if `displayHomeAsUp` is enabled
        final boolean displayHomeAsUpEnabled = (getSupportActionBar().getDisplayOptions()
            & ActionBar.DISPLAY_HOME_AS_UP) == ActionBar.DISPLAY_HOME_AS_UP;

        // We'll control what happens on navigation-icon click
        mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (displayHomeAsUpEnabled) {
                    finish();
                } else {
                    // `ActionBarDrawerToggle#toggle()` is private.
                    // Extend `ActionBarDrawerToggle` and make provision
                    // for toggling.
                    mDrawerToggle.toggleDrawer();
                }
            }
        });

        // I will talk about `mToolbarnavigationIcon` later on.

        if (displayHomeAsUpEnabled) {
            mToolbarNavigationIcon.setIndicator(
                          CustomDrawerArrowDrawable.HOME_AS_UP_INDICATOR);
        } else {
            mToolbarNavigationIcon.setIndicator(
                          CustomDrawerArrowDrawable.DRAWER_INDICATOR);
        }

        mToolbar.setNavigationIcon(mToolbarNavigationIcon);
        mToolbar.setNavigationContentDescription(i);
    }

    @Override
    public void setActionBarDescription(int i) {
        mToolbar.setNavigationContentDescription(i);
    }

    @Override
    public Drawable getThemeUpIndicator() {
        final TypedArray a = mToolbar.getContext()
            .obtainStyledAttributes(new int[]{Android.R.attr.homeAsUpIndicator});
        final Drawable result = a.getDrawable(0);
        a.recycle();
        return result;
    }

    @Override
    public Context getActionBarThemedContext() {
        return mToolbar.getContext();
    }

    ....
}

ActionBarDrawerToggle verwendet die hier angegebene setActionBarUpIndicator(Drawable, int). Da wir die Weitergabe von Drawable ignorieren, haben wir die volle Kontrolle darüber, was angezeigt wird. 

Catch: ActionBarDrawerToggle lässt unseren Activity als Delegierten fungieren, wenn wir den Toolbar-Parameter hier als null übergeben:

public ActionBarDrawerToggle(Activity activity, DrawerLayout drawerLayout,
        Toolbar toolbar, @StringRes int openDrawerContentDescRes,
        @StringRes int closeDrawerContentDescRes) { .... }

Und Sie müssen getV7DrawerToggleDelegate() in Ihrer Aktivität überschreiben:

@Nullable
@Override
public ActionBarDrawerToggle.Delegate getV7DrawerToggleDelegate() {
    return this;
}

Wie Sie sehen können, ist es eine Menge zusätzlicher Arbeit, den richtigen Weg zu gehen. Und wir sind noch nicht fertig.

Die animierenden DrawerArrowDrawableToggle können mit diesen Attributen gestaltet werden. Wenn Sie möchten, dass Ihre Zeichenzustände (homeAsUp & hamburger) genau wie die Standardwerte sind, müssen Sie sie als solche implementieren:

/**
 * A drawable that can draw a "Drawer hamburger" menu or an Arrow
 */
public class CustomDrawerArrowDrawable extends Drawable {

    public static final float DRAWER_INDICATOR = 0f;

    public static final float HOME_AS_UP_INDICATOR = 1f;

    private final Activity mActivity;

    private final Paint mPaint = new Paint();

    // The angle in degress that the arrow head is inclined at.
    private static final float ARROW_HEAD_ANGLE = (float) Math.toRadians(45);
    private final float mBarThickness;
    // The length of top and bottom bars when they merge into an arrow
    private final float mTopBottomArrowSize;
    // The length of middle bar
    private final float mBarSize;
    // The length of the middle bar when arrow is shaped
    private final float mMiddleArrowSize;
    // The space between bars when they are parallel
    private final float mBarGap;

    // Use Path instead of canvas operations so that if color has transparency, overlapping sections
    // wont look different
    private final Path mPath = new Path();
    // The reported intrinsic size of the drawable.
    private final int mSize;

    private float mIndicator;

    /**
     * @param context used to get the configuration for the drawable from
     */
    public CustomDrawerArrowDrawable(Activity activity, Context context) {
        final TypedArray typedArray = context.getTheme()
            .obtainStyledAttributes(null, R.styleable.DrawerArrowToggle,
                    R.attr.drawerArrowStyle,
                    R.style.Base_Widget_AppCompat_DrawerArrowToggle);
        mPaint.setAntiAlias(true);
        mPaint.setColor(typedArray.getColor(R.styleable.DrawerArrowToggle_color, 0));
        mSize = typedArray.getDimensionPixelSize(R.styleable.DrawerArrowToggle_drawableSize, 0);
        mBarSize = typedArray.getDimension(R.styleable.DrawerArrowToggle_barSize, 0);
        mTopBottomArrowSize = typedArray
            .getDimension(R.styleable.DrawerArrowToggle_topBottomBarArrowSize, 0);
        mBarThickness = typedArray.getDimension(R.styleable.DrawerArrowToggle_thickness, 0);
        mBarGap = typedArray.getDimension(R.styleable.DrawerArrowToggle_gapBetweenBars, 0);

        mMiddleArrowSize = typedArray
            .getDimension(R.styleable.DrawerArrowToggle_middleBarArrowSize, 0);
        typedArray.recycle();

        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        mPaint.setStrokeCap(Paint.Cap.SQUARE);
        mPaint.setStrokeWidth(mBarThickness);

        mActivity = activity;
    }

    public boolean isLayoutRtl() {
        return ViewCompat.getLayoutDirection(mActivity.getWindow().getDecorView())
            == ViewCompat.LAYOUT_DIRECTION_RTL;
    }

    @Override
    public void draw(Canvas canvas) {
        Rect bounds = getBounds();
        final boolean isRtl = isLayoutRtl();
        // Interpolated widths of arrow bars
        final float arrowSize = lerp(mBarSize, mTopBottomArrowSize, mIndicator);
        final float middleBarSize = lerp(mBarSize, mMiddleArrowSize, mIndicator);
        // Interpolated size of middle bar
        final float middleBarCut = lerp(0, mBarThickness / 2, mIndicator);
        // The rotation of the top and bottom bars (that make the arrow head)
        final float rotation = lerp(0, ARROW_HEAD_ANGLE, mIndicator);

        final float topBottomBarOffset = lerp(mBarGap + mBarThickness, 0, mIndicator);
        mPath.rewind();

        final float arrowEdge = -middleBarSize / 2;
        // draw middle bar
        mPath.moveTo(arrowEdge + middleBarCut, 0);
        mPath.rLineTo(middleBarSize - middleBarCut, 0);

        final float arrowWidth = Math.round(arrowSize * Math.cos(rotation));
        final float arrowHeight = Math.round(arrowSize * Math.sin(rotation));

        // top bar
        mPath.moveTo(arrowEdge, topBottomBarOffset);
        mPath.rLineTo(arrowWidth, arrowHeight);

        // bottom bar
        mPath.moveTo(arrowEdge, -topBottomBarOffset);
        mPath.rLineTo(arrowWidth, -arrowHeight);
        mPath.moveTo(0, 0);
        mPath.close();

        canvas.save();

        if (isRtl) {
            canvas.rotate(180, bounds.centerX(), bounds.centerY());
        }
        canvas.translate(bounds.centerX(), bounds.centerY());
        canvas.drawPath(mPath, mPaint);

        canvas.restore();
    }

    @Override
    public void setAlpha(int i) {
        mPaint.setAlpha(i);
    } 

    // override
    public boolean isAutoMirrored() {
        // Draws rotated 180 degrees in RTL mode.
        return true;
    }

    @Override
    public void setColorFilter(ColorFilter colorFilter) {
        mPaint.setColorFilter(colorFilter);
    }

    @Override
    public int getIntrinsicHeight() {
        return mSize;
    }

    @Override
    public int getIntrinsicWidth() {
        return mSize;
    }

    @Override
    public int getOpacity() {
        return PixelFormat.TRANSLUCENT;
    }

    public void setIndicator(float indicator) {
        mIndicator = indicator;
        invalidateSelf();
    }

    /**
     * Linear interpolate between a and b with parameter t.
     */
    private static float lerp(float a, float b, float indicator) {
        if (indicator == HOME_AS_UP_INDICATOR) {
            return b;
        } else {
            return a;
        }
    }
}

Die CustomDrawerArrowDrawable's-Implementierung wurde von AOSP entliehen und reduziert, um nur zwei Zustände zeichnen zu können: homeAsUp & hamburger. Sie können zwischen diesen Status wechseln, indem Sie setIndicator(float) aufrufen. Wir verwenden dies in der von uns implementierten Delegate. Darüber hinaus können Sie mit CustomDrawerArrowDrawable das Format in xml formatieren: barSize, color usw. Auch wenn Sie dies nicht benötigen, können Sie mit der obigen Implementierung benutzerdefinierte Animationen für das Öffnen und Schließen der Schublade erstellen.

Ich weiß ehrlich nicht, ob ich das empfehlen sollte.


Wenn Sie ActionBarDrawerToggle#setHomeAsUpIndicator(...) mit dem Argument null aufrufen, sollte das Zeichnungsobjekt ausgewählt werden, das in Ihrem Design definiert ist:

<item name="Android:homeAsUpIndicator">@drawable/some_back_drawable</item>

Derzeit passiert dies nicht aufgrund eines möglichen Fehlers in ToolbarCompatDelegate#getThemeUpIndicator():

@Override
public Drawable getThemeUpIndicator() {
    final TypedArray a = mToolbar.getContext()
                 // Should be new int[]{Android.R.attr.homeAsUpIndicator}
                .obtainStyledAttributes(new int[]{Android.R.id.home});
    final Drawable result = a.getDrawable(0);
    a.recycle();
    return result;
}

Fehlerbericht, der dies lose behandelt (siehe Fall 4): Link


Wenn Sie sich für die Lösung entscheiden, die Sie bereits haben, sollten Sie CustomDrawerArrowDrawable anstelle von pngs (R.drawable.lib_ic_arrow_back_light & R.drawable.lib_ic_menu_light) in Betracht ziehen. Sie benötigen nicht mehrere Drawables für Dichte-/Größen-Buckets, und das Styling erfolgt in XML. Das Endprodukt ist auch das gleiche wie das Framework. 

mDrawerToggle.setDrawerIndicatorEnabled(false);

CustomDrawerArrowDrawable toolbarNavigationIcon 
                = new CustomDrawerArrowDrawable(this, mToolbar.getContext());    

if (showHomeAsUp) {
    toolbarNavigationIcon.setIndicator(
                           CustomDrawerArrowDrawable.HOME_AS_UP_INDICATOR);
    mDrawerToggle.setToolbarNavigationClickListener(view -> finish());
} else {
    mToolbarNavigationIcon.setIndicator(
                           CustomDrawerArrowDrawable.DRAWER_INDICATOR);
    mDrawerToggle.setToolbarNavigationClickListener(view -> toggleDrawer());
}

mDrawerToggle.setHomeAsUpIndicator(toolbarNavigationIcon);
3
Vikram

Nun gibt es eine spezielle Methode, um die Animation zu deaktivieren: toggle.setDrawerSlideAnimationEnabled(false)

Hier ist ein Ausschnitt, den ich benutze:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    [...]

    ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
            this, drawerLayout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
    toggle.setDrawerSlideAnimationEnabled(false);
    drawer.addDrawerListener(toggle);
    toggle.syncState();
}
3
Kamil Seweryn

Wenn Sie den Supper-Aufruf in der onDrawerSlide()-Methode deaktivieren, wird die Animation zwischen Arrow und Burger angehalten. Die Umschaltung (ohne Animation) wird nur angezeigt, wenn die Schublade vollständig geöffnet oder vollständig geschlossen ist.

mActionBarDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, mToolbar, R.string.open, R.string.closed) {
            @Override
            public void onDrawerSlide(View drawerView, float slideOffset) {
                  //super.onDrawerSlide(drawerView, slideOffset);
            }
        };
mDrawerLayout.setDrawerListener(mActionBarDrawerToggle);
1

Wenn Sie die Animation nicht möchten, verwenden Sie ActionBarDrawerToggle nicht. Verwenden Sie stattdessen den folgenden Code.

toolbar.setNavigationIcon(R.drawable.ic_menu);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    drawer.openDrawer(GravityCompat.START);
                }
            });
1
Mark Buikema

Um die Animation des Hamburger-Menüs zu entfernen, können Sie wie folgt vorgehen:

 ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, mDrawer,  mToolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);

 toggle.setDrawerSlideAnimationEnabled(false); 
0
makata