在 Android 中将徽章计数器添加到汉堡导航菜单图标
Add badge counter to hamburger navigation menu icon in Android
我的问题与 (which is not a duplicate of this question)相同。
那个问题的 对我不起作用,而不是将默认的汉堡包图标更改为 left activity 的标题,它只是在我的 activity 的 右边 添加了一个额外的汉堡包图标'标题.
那么我实际上是如何得到这个的:
我整天都在摸索,但一无所获。
我看到 Toolbar
有一个 setNavigationIcon(Drawable drawable)
方法。理想情况下,我想使用 layout
(包含汉堡包图标和徽章视图)而不是 Drawable
,但我不确定 if/how 这是可以实现的 - 或者如果有更好的方法吗?
注意 - 这不是关于如何创建 徽章视图的问题。我已经创建了它并在导航菜单项上实现了它。所以我现在只需要为默认的汉堡包图标添加一个类似的徽章视图。
自支持库版本 24.2.0 起,ActionBarDrawerToggle
的 v7 版本提供了 setDrawerArrowDrawable()
方法作为自定义切换图标的方法。 DrawerArrowDrawable
是提供默认图标的 class,可以根据需要对其进行子class更改。
例如,BadgeDrawerArrowDrawable
class 覆盖 draw()
方法以在 superclass 绘制自身后添加基本的红色和白色徽章。这允许汉堡包箭头动画保留在下方。
import android.content.Context;
import android.graphics.Color;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.support.v7.graphics.drawable.DrawerArrowDrawable;
import java.util.Objects;
public class BadgeDrawerArrowDrawable extends DrawerArrowDrawable {
// Fraction of the drawable's intrinsic size we want the badge to be.
private static final float SIZE_FACTOR = .3f;
private static final float HALF_SIZE_FACTOR = SIZE_FACTOR / 2;
private Paint backgroundPaint;
private Paint textPaint;
private String text;
private boolean enabled = true;
public BadgeDrawerArrowDrawable(Context context) {
super(context);
backgroundPaint = new Paint();
backgroundPaint.setColor(Color.RED);
backgroundPaint.setAntiAlias(true);
textPaint = new Paint();
textPaint.setColor(Color.WHITE);
textPaint.setAntiAlias(true);
textPaint.setTypeface(Typeface.DEFAULT_BOLD);
textPaint.setTextAlign(Paint.Align.CENTER);
textPaint.setTextSize(SIZE_FACTOR * getIntrinsicHeight());
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
if (!enabled) {
return;
}
final Rect bounds = getBounds();
final float x = (1 - HALF_SIZE_FACTOR) * bounds.width();
final float y = HALF_SIZE_FACTOR * bounds.height();
canvas.drawCircle(x, y, SIZE_FACTOR * bounds.width(), backgroundPaint);
if (text == null || text.length() == 0) {
return;
}
final Rect textBounds = new Rect();
textPaint.getTextBounds(text, 0, text.length(), textBounds);
canvas.drawText(text, x, y + textBounds.height() / 2, textPaint);
}
public void setEnabled(boolean enabled) {
if (this.enabled != enabled) {
this.enabled = enabled;
invalidateSelf();
}
}
public boolean isEnabled() {
return enabled;
}
public void setText(String text) {
if (!Objects.equals(this.text, text)) {
this.text = text;
invalidateSelf();
}
}
public String getText() {
return text;
}
public void setBackgroundColor(int color) {
if (backgroundPaint.getColor() != color) {
backgroundPaint.setColor(color);
invalidateSelf();
}
}
public int getBackgroundColor() {
return backgroundPaint.getColor();
}
public void setTextColor(int color) {
if (textPaint.getColor() != color) {
textPaint.setColor(color);
invalidateSelf();
}
}
public int getTextColor() {
return textPaint.getColor();
}
}
可以在实例化后随时在切换器上设置它的一个实例,并根据需要直接在可绘制对象上设置徽章的属性。
正如下面提到的 OP,用于自定义 DrawerArrowDrawable
的 Context
应该通过 ActionBar#getThemedContext()
或 Toolbar#getContext()
获得,以确保使用正确的样式值。例如:
private ActionBarDrawerToggle toggle;
private BadgeDrawerArrowDrawable badgeDrawable;
...
toggle = new ActionBarDrawerToggle(this, ...);
badgeDrawable = new BadgeDrawerArrowDrawable(getSupportActionBar().getThemedContext());
toggle.setDrawerArrowDrawable(badgeDrawable);
badgeDrawable.setText("1");
...
为了简化一些事情,最好也使用 subclass ActionBarDrawerToggle
,并通过切换实例处理所有事情。
import android.app.Activity;
import android.content.Context;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.widget.Toolbar;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class BadgeDrawerToggle extends ActionBarDrawerToggle {
private BadgeDrawerArrowDrawable badgeDrawable;
public BadgeDrawerToggle(Activity activity, DrawerLayout drawerLayout,
int openDrawerContentDescRes,
int closeDrawerContentDescRes) {
super(activity, drawerLayout, openDrawerContentDescRes,
closeDrawerContentDescRes);
init(activity);
}
public BadgeDrawerToggle(Activity activity, DrawerLayout drawerLayout,
Toolbar toolbar, int openDrawerContentDescRes,
int closeDrawerContentDescRes) {
super(activity, drawerLayout, toolbar, openDrawerContentDescRes,
closeDrawerContentDescRes);
init(activity);
}
private void init(Activity activity) {
Context c = getThemedContext();
if (c == null) {
c = activity;
}
badgeDrawable = new BadgeDrawerArrowDrawable(c);
setDrawerArrowDrawable(badgeDrawable);
}
public void setBadgeEnabled(boolean enabled) {
badgeDrawable.setEnabled(enabled);
}
public boolean isBadgeEnabled() {
return badgeDrawable.isEnabled();
}
public void setBadgeText(String text) {
badgeDrawable.setText(text);
}
public String getBadgeText() {
return badgeDrawable.getText();
}
public void setBadgeColor(int color) {
badgeDrawable.setBackgroundColor(color);
}
public int getBadgeColor() {
return badgeDrawable.getBackgroundColor();
}
public void setBadgeTextColor(int color) {
badgeDrawable.setTextColor(color);
}
public int getBadgeTextColor() {
return badgeDrawable.getTextColor();
}
private Context getThemedContext() {
// Don't freak about the reflection. ActionBarDrawerToggle
// itself is already using reflection internally.
try {
Field mActivityImplField = ActionBarDrawerToggle.class
.getDeclaredField("mActivityImpl");
mActivityImplField.setAccessible(true);
Object mActivityImpl = mActivityImplField.get(this);
Method getActionBarThemedContextMethod = mActivityImpl.getClass()
.getDeclaredMethod("getActionBarThemedContext");
return (Context) getActionBarThemedContextMethod.invoke(mActivityImpl);
}
catch (Exception e) {
return null;
}
}
}
这样,自定义徽章可绘制对象将自动设置,所有与切换相关的内容都可以通过单个对象进行管理。
BadgeDrawerToggle
是 ActionBarDrawerToggle
的直接替代品,其构造函数完全相同。
private BadgeDrawerToggle badgeToggle;
...
badgeToggle = new BadgeDrawerToggle(this, ...);
badgeToggle.setBadgeText("1");
...
我的问题与
那个问题的
那么我实际上是如何得到这个的:
我整天都在摸索,但一无所获。
我看到 Toolbar
有一个 setNavigationIcon(Drawable drawable)
方法。理想情况下,我想使用 layout
(包含汉堡包图标和徽章视图)而不是 Drawable
,但我不确定 if/how 这是可以实现的 - 或者如果有更好的方法吗?
注意 - 这不是关于如何创建 徽章视图的问题。我已经创建了它并在导航菜单项上实现了它。所以我现在只需要为默认的汉堡包图标添加一个类似的徽章视图。
自支持库版本 24.2.0 起,ActionBarDrawerToggle
的 v7 版本提供了 setDrawerArrowDrawable()
方法作为自定义切换图标的方法。 DrawerArrowDrawable
是提供默认图标的 class,可以根据需要对其进行子class更改。
例如,BadgeDrawerArrowDrawable
class 覆盖 draw()
方法以在 superclass 绘制自身后添加基本的红色和白色徽章。这允许汉堡包箭头动画保留在下方。
import android.content.Context;
import android.graphics.Color;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.support.v7.graphics.drawable.DrawerArrowDrawable;
import java.util.Objects;
public class BadgeDrawerArrowDrawable extends DrawerArrowDrawable {
// Fraction of the drawable's intrinsic size we want the badge to be.
private static final float SIZE_FACTOR = .3f;
private static final float HALF_SIZE_FACTOR = SIZE_FACTOR / 2;
private Paint backgroundPaint;
private Paint textPaint;
private String text;
private boolean enabled = true;
public BadgeDrawerArrowDrawable(Context context) {
super(context);
backgroundPaint = new Paint();
backgroundPaint.setColor(Color.RED);
backgroundPaint.setAntiAlias(true);
textPaint = new Paint();
textPaint.setColor(Color.WHITE);
textPaint.setAntiAlias(true);
textPaint.setTypeface(Typeface.DEFAULT_BOLD);
textPaint.setTextAlign(Paint.Align.CENTER);
textPaint.setTextSize(SIZE_FACTOR * getIntrinsicHeight());
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
if (!enabled) {
return;
}
final Rect bounds = getBounds();
final float x = (1 - HALF_SIZE_FACTOR) * bounds.width();
final float y = HALF_SIZE_FACTOR * bounds.height();
canvas.drawCircle(x, y, SIZE_FACTOR * bounds.width(), backgroundPaint);
if (text == null || text.length() == 0) {
return;
}
final Rect textBounds = new Rect();
textPaint.getTextBounds(text, 0, text.length(), textBounds);
canvas.drawText(text, x, y + textBounds.height() / 2, textPaint);
}
public void setEnabled(boolean enabled) {
if (this.enabled != enabled) {
this.enabled = enabled;
invalidateSelf();
}
}
public boolean isEnabled() {
return enabled;
}
public void setText(String text) {
if (!Objects.equals(this.text, text)) {
this.text = text;
invalidateSelf();
}
}
public String getText() {
return text;
}
public void setBackgroundColor(int color) {
if (backgroundPaint.getColor() != color) {
backgroundPaint.setColor(color);
invalidateSelf();
}
}
public int getBackgroundColor() {
return backgroundPaint.getColor();
}
public void setTextColor(int color) {
if (textPaint.getColor() != color) {
textPaint.setColor(color);
invalidateSelf();
}
}
public int getTextColor() {
return textPaint.getColor();
}
}
可以在实例化后随时在切换器上设置它的一个实例,并根据需要直接在可绘制对象上设置徽章的属性。
正如下面提到的 OP,用于自定义 DrawerArrowDrawable
的 Context
应该通过 ActionBar#getThemedContext()
或 Toolbar#getContext()
获得,以确保使用正确的样式值。例如:
private ActionBarDrawerToggle toggle;
private BadgeDrawerArrowDrawable badgeDrawable;
...
toggle = new ActionBarDrawerToggle(this, ...);
badgeDrawable = new BadgeDrawerArrowDrawable(getSupportActionBar().getThemedContext());
toggle.setDrawerArrowDrawable(badgeDrawable);
badgeDrawable.setText("1");
...
为了简化一些事情,最好也使用 subclass ActionBarDrawerToggle
,并通过切换实例处理所有事情。
import android.app.Activity;
import android.content.Context;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.widget.Toolbar;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class BadgeDrawerToggle extends ActionBarDrawerToggle {
private BadgeDrawerArrowDrawable badgeDrawable;
public BadgeDrawerToggle(Activity activity, DrawerLayout drawerLayout,
int openDrawerContentDescRes,
int closeDrawerContentDescRes) {
super(activity, drawerLayout, openDrawerContentDescRes,
closeDrawerContentDescRes);
init(activity);
}
public BadgeDrawerToggle(Activity activity, DrawerLayout drawerLayout,
Toolbar toolbar, int openDrawerContentDescRes,
int closeDrawerContentDescRes) {
super(activity, drawerLayout, toolbar, openDrawerContentDescRes,
closeDrawerContentDescRes);
init(activity);
}
private void init(Activity activity) {
Context c = getThemedContext();
if (c == null) {
c = activity;
}
badgeDrawable = new BadgeDrawerArrowDrawable(c);
setDrawerArrowDrawable(badgeDrawable);
}
public void setBadgeEnabled(boolean enabled) {
badgeDrawable.setEnabled(enabled);
}
public boolean isBadgeEnabled() {
return badgeDrawable.isEnabled();
}
public void setBadgeText(String text) {
badgeDrawable.setText(text);
}
public String getBadgeText() {
return badgeDrawable.getText();
}
public void setBadgeColor(int color) {
badgeDrawable.setBackgroundColor(color);
}
public int getBadgeColor() {
return badgeDrawable.getBackgroundColor();
}
public void setBadgeTextColor(int color) {
badgeDrawable.setTextColor(color);
}
public int getBadgeTextColor() {
return badgeDrawable.getTextColor();
}
private Context getThemedContext() {
// Don't freak about the reflection. ActionBarDrawerToggle
// itself is already using reflection internally.
try {
Field mActivityImplField = ActionBarDrawerToggle.class
.getDeclaredField("mActivityImpl");
mActivityImplField.setAccessible(true);
Object mActivityImpl = mActivityImplField.get(this);
Method getActionBarThemedContextMethod = mActivityImpl.getClass()
.getDeclaredMethod("getActionBarThemedContext");
return (Context) getActionBarThemedContextMethod.invoke(mActivityImpl);
}
catch (Exception e) {
return null;
}
}
}
这样,自定义徽章可绘制对象将自动设置,所有与切换相关的内容都可以通过单个对象进行管理。
BadgeDrawerToggle
是 ActionBarDrawerToggle
的直接替代品,其构造函数完全相同。
private BadgeDrawerToggle badgeToggle;
...
badgeToggle = new BadgeDrawerToggle(this, ...);
badgeToggle.setBadgeText("1");
...