[英]Disable hamburger to back arrow animation on Toolbar
使用汉堡包实现Toolbar
以返回箭头动画非常容易。 我认为该动画毫无意义,因为根据材料设计规范,导航抽屉在打开时会覆盖Toolbar
。 我的问题是如何使用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
在汉堡包和后退箭头图标之间切换?
在创建抽屉式切换时,这将禁用动画,覆盖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函数的末尾。
我认为这个动画毫无意义
好吧, ActionBarDrawerToggle
是要动画的。
您可以通过在ActionBar主题中定义canvasArrowStyle来自定义动画切换。
有什么线索应该如何正确实施以仅使用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
,这意味着您想从Google官方材料设计图标包中设置的导航图标中获取ic_menu_white_24dp
或ic_menu_black_24dp
。
您也可以将DrawerArrowDrawable
复制到项目中,然后根据需要切换箭头或汉堡包状态。 它是自包含的,减去一些资源。
我有类似的要求,并花了一些时间浏览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)
时, 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
的传递,因此我们将完全控制显示的内容。
捕获:如果我们在此处将Toolbar
参数传递为null, ActionBarDrawerToggle
将让我们的Activity
充当委托:
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
将允许您在xml中设置其样式: barSize
, color
等。即使您不需要它, 上述实现也可以为抽屉打开和关闭提供自定义动画 。
老实说,我不知道我是否应该推荐这个。
如果使用参数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): 链接
如果您决定坚持使用已有的解决方案,请考虑使用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);
这是我用来控制位于NavigationDrawerFragment中的ActionBarDrawableToggle的函数,我在每个片段的onActivityCreated回调中调用该函数。 职位职能是必要的。 汉堡包图标变为后退箭头,并且可以单击后退箭头。 方向更改由处理程序正确处理。
...
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();
}
现在有一种禁用动画的专用方法: 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()
方法中禁用晚饭调用将停止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);
如果您不想动画,请不要使用ActionBarDrawerToggle
。 请改用下面的代码。
toolbar.setNavigationIcon(R.drawable.ic_menu);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
drawer.openDrawer(GravityCompat.START);
}
});
要删除汉堡包菜单动画,您可以执行以下操作:
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, mDrawer, mToolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
toggle.setDrawerSlideAnimationEnabled(false);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.