web-dev-qa-db-ja.com

ハンバーガーを無効にしてツールバーの矢印アニメーションを戻す

Toolbarをハンバーガーで逆矢印アニメーションに実装するのは非常に簡単です。私の意見では、このアニメーションは無意味です。なぜなら、マテリアルデザイン仕様に従って、ナビゲーションドロワーは開いたときにToolbarをカバーするからです。私の質問は、このアニメーションを適切に無効にして、getSupportActionBar().setDisplayHomeAsUpEnabled(true);を使用してハンバーガーまたは戻る矢印を表示する方法です。

これは私がそれをやった方法ですが、汚いハックのように見えます:

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());
}

ハンバーガーと戻る矢印アイコンを切り替えるためにsetDisplayHomeAsUpEnabledだけを使用するためにこれを適切に実装する方法の手がかりはありますか?

29
tomrozb

これにより、drawerToggleの作成時にアニメーションが無効になり、onDrawerSlide()がオーバーライドされます。

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 
    }
};

矢印を完全に削除する場合は、追加できます

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

onDrawerOpened関数の最後。

47
Frank

私の意見では、このアニメーションは無意味です

まあ、ActionBarDrawerToggleはアニメーション化するためのものです。

ドキュメントから:

ActionBarテーマでdrawerArrowStyleを定義することにより、animatedトグルをカスタマイズできます。

SetDisplayHomeAsUpEnabledを使用してハンバーガーと戻る矢印アイコンを切り替えるために、これを適切に実装する手がかりはありますか?

ActionBarDrawerToggleは、単に _ActionBar.setHomeAsUpIndicator_ を呼び出すための派手な方法です。そのため、いずれにしても、表示するには_ActionBar.setDisplayHomeAsUpEnabled_をtrueに呼び出す必要があります。

使用する必要があると確信している場合は、それぞれ ActionBarDrawerToggle.onDrawerOpened(View drawerView) および ActionBarDrawerToggle.onDrawerClosed(View drawerView) を呼び出すことをお勧めします。

これにより、 DrawerIndicator の位置が_1_または_0_に設定され、矢印と DrawerArrowDrawable のハンバーガー状態が切り替わります。

そして、あなたの場合、ActionBarDrawerToggle_DrawerLayout.DrawerListener_ としてアタッチする必要さえありません。次のように:

_mYourDrawer.setDrawerListener(mYourDrawerToggle);
_

しかし、はるかに前向きなアプローチは、_ActionBar.setHomeAsUpIndicator_を一度呼び出して、独自のハンバーガーアイコンを適用することです。これは、スタイルを介して行うこともできます。次に、戻る矢印を表示する場合は、_ActionBar.setDisplayHomeAsUpEnabled_を呼び出して、AppCompatまたはフレームワークに残りを処理させます。あなたが行ったコメントから、私はこれがあなたが探しているものであると確信しています。

どのアイコンを使用するのかわからない場合は、 デフォルトのDrawerArrowDrawableサイズは_24dp_ です。つまり、_ic_menu_white_24dp_または_ic_menu_black_24dp_を取得します。 ナビゲーションアイコンセット Googleの公式マテリアルデザインアイコンパック。

DrawerArrowDrawableをプロジェクトにコピーして、必要に応じて矢印またはハンバーガーの状態を切り替えることもできます。自己完結型で、いくつかのリソースがありません。

12
adneal

これは、すべてのフラグメントのonActivityCreatedコールバックで呼び出すNavigationDrawerFragmentにあるActionBarDrawableToggleを制御するための私の関数です。投稿機能が必要です。ハンバーガーのアイコンが戻る矢印に変わり、戻る矢印がクリック可能になります。向きの変更は、ハンドラーによって適切に処理されます。

...

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();
                }
            }
        });      
    }
}

これは、ベースフラグメントのonActivityCreatedメソッドです。

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

同様の要件があり、ActionBarDrawerToggleコードの検証に時間を費やしました。あなたが現在持っているものは前進する最良の方法です。

今後の詳細:

ハンバーガーから矢印へのアニメーションは、描画可能な実装(DrawerArrowDrawableToggle)によって提供されます。現在、このドロアブルがドロワー状態にどのように反応するかについてはあまり制御できません。 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)
_

sliderの独自の実装を提供することにより、引き出しの状態に対する反応を制御できます。 sliderが実装しなければならないインターフェース:

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

    public void setPosition(float position);

    public float getPosition();
}
_

setPosition(float)はここのハイライトです-すべての引き出しの状態の変更は、引き出しインジケータを更新するために呼び出します。

必要な動作については、slider実装のsetPosition(float position)は何もしません。

あなたはまだ必要です:

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

setDrawerIndicatorEnabled(false)を実行しない場合、setToolbarNavigationClickListener(view -> finish());で設定したOnClickListenerは起動しません。

正しいことは何ですかnow

よく調べてみると、ActionBarDrawerToggleに要件の規定があることがわかりました。私は、この規定があなたが現在持っているものよりもさらにハッキングだと思います。しかし、私はあなたに決めさせます。

ActionBarDrawerToggleを使用すると、インターフェイス デリゲート を介してドロワーインジケータをsome制御できます。次の方法で、アクティビティにこのインターフェイスを実装できます。

_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は、ここで提供されるsetActionBarUpIndicator(Drawable, int)を使用します。渡されるDrawableを無視しているため、表示されるものを完全に制御できます。

キャッチ:ActionBarDrawerToggleパラメータをnullとしてここに渡すと、ActivityToolbarをデリゲートとして機能させます。

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

また、アクティビティでgetV7DrawerToggleDelegate()をオーバーライドする必要があります。

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

ご覧のとおり、適切な方法で作業を行うには多くの追加作業が必要です。そして、まだ完了していません。

アニメーション化DrawerArrowDrawableToggleは、 これらの属性 を使用してスタイル設定できます。デフォルトのように描画可能な状態(homeAsUpハンバーガー)exactlyが必要な場合は、次のように実装する必要があります。

_/**
 * 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;
        }
    }
}
_

_CustomDrawerArrowDrawable's_実装はAOSPから借用され、2つの状態(homeAsUpハンバーガー)のみを描画できるように削除されました。 setIndicator(float)を呼び出すことにより、これらの状態を切り替えることができます。これを実装したDelegateで使用します。さらに、CustomDrawerArrowDrawableを使用すると、xmlでスタイルを設定できます:barSizecolorなど。これは必要ありませんが、上記の実装により、引き出しの開閉にカスタムアニメーションを提供できます

私はこれを推薦すべきかどうか正直に知りません。


引数nullを指定してActionBarDrawerToggle#setHomeAsUpIndicator(...)を呼び出す場合、テーマで定義されたドロウアブルを選択する必要があります。

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

現在、これは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;
}
_

これについて大まかに説明しているバグレポート(ケース4をお読みください): Link


すでに持っているソリューションを使用することにした場合は、pngs(R.drawable.lib_ic_arrow_back_light R.drawable.lib_ic_menu_light)の代わりにCustomDrawerArrowDrawableを使用することを検討してください。密度/サイズバケットに複数のドロウアブルを必要とすることはなく、スタイリングはxmlで行われます。また、最終製品はフレームワークと同じになります。

_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

アニメーションを無効にする専用の方法があります:toggle.setDrawerSlideAnimationEnabled(false)

私が使用するスニペットは次のとおりです。

@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

onDrawerSlide()メソッドで夕食の呼び出しを無効にすると、ArrowとBurgerの間のアニメーションが停止します。ドロワーが完全に開いているか完全に閉じている場合にのみ、切り替えが表示されます(アニメーションなし)。

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