禁用汉堡包在工具栏上退出箭头animation

用汉堡来实现Toolbar后退箭头animation非常简单。 在我看来,这个animation是没有意义的,因为按照材料devise规格,抽屉在打开时会覆盖Toolbar 。 我的问题是如何正确地禁用此animation,并使用getSupportActionBar().setDisplayHomeAsUpEnabled(true);或向后箭头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来切换汉堡和后退箭头图标?

Solutions Collecting From Web of "禁用汉堡包在工具栏上退出箭头animation"

这将禁用animation,创builddrawerToggle时,覆盖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函数的末尾。

在我看来这个animation是毫无意义的

那么, ActionBarDrawerToggle是为了animation。

从文档:

您可以通过在ActionBar主题中定义drawerArrowStyle来自定义animation切换。

任何线索如何正确使用setDisplayHomeAsUpEnabled来切换汉堡和后退箭头图标?

ActionBarDrawerToggle只是一个调用ActionBar.setHomeAsUpIndicator的奇特方式。 所以,无论哪种方式,你将不得不调用ActionBar.setDisplayHomeAsUpEnabledtrue以显示它。

如果你确信你必须使用它,那么我build议分别调用ActionBarDrawerToggle.onDrawerOpened(View drawerView)ActionBarDrawerToggle.onDrawerClosed(View drawerView)

这将设置DrawerIndicator位置为10 ,在DrawerIndicator的箭头和汉堡状态之间切换。

而在你的情况下,甚至不需要将一个ActionBarDrawerToggle附加为DrawerLayout.DrawerListener 。 如:

 mYourDrawer.setDrawerListener(mYourDrawerToggle); 

但是更为前进的方法是调用ActionBar.setHomeAsUpIndicator一次并应用自己的汉堡包图标,也可以通过样式来实现。 然后,当你想显示后退箭头时,只需调用ActionBar.setDisplayHomeAsUpEnabled然后让AppCompat或框架处理其余部分。 从你所做的评论,我很确定这是你要找的。

如果您不确定要使用哪个图标, 则默认DrawerArrowDrawable大小为24dp ,这意味着您需要从Google官方Material Design图标包中设置的导航图标中获取ic_menu_white_24dpic_menu_black_24dp

您也可以将DrawerArrowDrawable复制到您的项目中,然后根据需要切换箭头或汉堡包状态。 它是自成体系的,减去一些资源。

这是我的控制位于NavigationDrawerFragment的ActionBarDrawableToggle的函数,我在每个片段的onActivityCreatedcallback中调用它。 职位function是必要的。 汉堡包图标变成后退箭头,后退箭头可点击。 处理程序正确处理方向更改。

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

我有一个类似的要求,并花了一些时间通过ActionBarDrawerToggle代码。 你现在拥有的是最好的前进方向。

更多来:

箭头animation的汉堡由一个可绘制的实现提供 – DrawerArrowDrawableToggle 。 目前,我们对这个可绘制对抽屉状态的反应没有太多的控制。 以下是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)是这里的亮点 – 所有抽屉状态更改都会调用它来更新抽屉指示器。

对于你想要的行为,你的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());设置的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被传递,所以我们完全控制了将要显示的内容。

捕获: 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; } 

正如你所看到的,走正确的路是很多额外的工作。 而我们还没有完成。

animationDrawerArrowDrawableToggle可以使用这些属性进行样式设置。 如果你希望你的可绘制状态(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将允许您在xml: barSizecolor等中设置风格。即使您不需要这些, 上面的实现可以让您为抽屉打开和closures提供自定义animation

我真的不知道我是否应该推荐这个。


如果你用参数null调用ActionBarDrawerToggle#setHomeAsUpIndicator(...) ,它应该select你的主题中定义的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): 链接


如果您决定坚持使用您已有的解决scheme,请考虑使用CustomDrawerArrowDrawable来代替png(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); 

现在有专门的方法来禁用animation: 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(); } 

禁用onDrawerSlide()方法中的晚餐调用将停止箭头和汉堡之间的animation。 当抽屉完全打开或完全closures时,您只会看到切换(无animation)。

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

如果你不想要animation,不要使用ActionBarDrawerToggle 。 改为使用下面的代码。

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