[android] Отключить гамбургер для анимации спины на панели инструментов


2 Answers

По-моему, эта анимация бессмысленна

Ну, ActionBarDrawerToggle предназначен для анимирования.

Из документов:

Вы можете настроить анимированный переключатель, указав drawerArrowStyle в теме ActionBar.

Любые подсказки, как это должно быть правильно реализовано, чтобы использовать только 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 или инфраструктуре обработать остальные. Из комментариев, которые вы сделали, я уверен, что это то, что вы ищете.

Если вы не знаете, какой значок использовать, размер 24dp по умолчанию - 24dp , что означает, что вы захотите захватить ic_menu_white_24dp или ic_menu_black_24dp из значка навигации, установленного в официальном пакете официальных материалов Google.

Вы также можете скопировать DrawerArrowDrawable в свой проект и позволить затем переключаться со стрелками или гамбургерными состояниями по мере необходимости. Это самодостаточно, минус несколько ресурсов.

Question

Очень легко реализовать 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 для переключения между значками гамбургера и обратной стрелки?




У меня было аналогичное требование, и я потратил некоторое время на выполнение кода ActionBarDrawerToggle . То, что у вас сейчас есть, - лучший способ продвижения вперед.

Еще не все:

Анимация от гамбургера до стрелки обеспечивается DrawerArrowDrawableToggle реализацией - DrawerArrowDrawableToggle . В настоящее время у нас нет большого контроля над тем, как этот drawable реагирует на состояния ящика. Вот что конструктор доступа к actionVarDrawerToggle для 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) - это выделение здесь - все изменения состояния ящика setPosition(float) его, чтобы обновить индикатор ящика.

Для поведения, которое вы хотите, setPosition(float position) реализации slider ничего не сделает.

Вам все равно понадобятся:

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) , OnClickListener вы устанавливаете с помощью setToolbarNavigationClickListener(view -> finish()); не будет срабатывать.

Что мы можем сделать прямо сейчас ?

При ближайшем рассмотрении я обнаружил, что в ActionBarDrawerToggle есть условие для вашего требования. Я нахожу это положение еще более взломанным, чем то, что у вас есть. Но, я позволю тебе решить.

ActionBarDrawerToggle позволяет вам контролировать элемент указателя через интерфейс Delegate . Вы можете реализовать свой интерфейс для этого интерфейса следующим образом:

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 , у нас есть полный контроль над тем, что будет отображаться.

Catch: ActionBarDrawerToggle позволяет нашей Activity действовать как делегат, если мы передаем параметр Toolbar как null здесь:

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 & hamburger) были точно такими же, как и по умолчанию, вам необходимо реализовать его как таковое:

/**
 * 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 и урезана, чтобы разрешить рисование только двух состояний: homeAsUp & hamburger. Вы можете переключаться между этими состояниями, вызывая setIndicator(float) . Мы используем это в Delegate мы реализовали. Более того, использование CustomDrawerArrowDrawable позволит вам CustomDrawerArrowDrawable его в xml: barSize , color и т. Д. Несмотря на то, что вам это не нужно, приведенная выше реализация позволяет вам создавать пользовательские анимации для открытия и закрытия ящика .

Я честно не знаю, рекомендую ли я это.

Если вы вызываете ActionBarDrawerToggle#setHomeAsUpIndicator(...) с аргументом null , он должен выбрать drawable, определенный в вашей теме:

<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

Если вы решите придерживаться решения, которое у вас уже есть, подумайте об использовании CustomDrawerArrowDrawable вместо pngs (R.drawable.lib_ic_arrow_back_light & R.drawable.lib_ic_menu_light). Вам не понадобятся несколько чертежей для корзин плотности и размера, а стиль будет выполнен в 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);



Отключение вызова ужина в 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);



Related