【问题标题】:Badge notification over drawer toggle in toolbar - Android工具栏中抽屉切换的徽章通知 - Android
【发布时间】:2015-10-18 17:06:11
【问题描述】:

我想通知用户有关应用程序中新的未读消息,该消息可通过导航抽屉访问。我正在考虑通知徽章类似于Apple的东西,但在工具栏中的抽屉切换。

这就是我现在拥有的:

这就是我想要的:

我怎样才能做到这一点?

【问题讨论】:

标签: android android-layout navigation-drawer android-toolbar


【解决方案1】:

我在互联网上发现了这个非常酷的 BadgeDrawable 类,您可以使用该类将徽章计数添加到任何可绘制对象。请按照以下步骤操作。

第 1 步:首先将以下类添加到您的项目中。

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.util.TypedValue;


/**
 * Created by Admin on 2/25/2016.
 */
public class BadgeDrawable extends Drawable {

    private float mTextSize;
    private Paint mBadgePaint;
    private Paint mBadgePaint1;
    private Paint mTextPaint;
    private Rect mTxtRect = new Rect();

    private String mCount = "";
    private boolean mWillDraw = false;


    public BadgeDrawable(Context context) {
        mTextSize = dpToPx(context, 8); //text size
        mBadgePaint = new Paint();
        mBadgePaint.setColor(Color.RED);
        mBadgePaint.setAntiAlias(true);
        mBadgePaint.setStyle(Paint.Style.FILL);
        mBadgePaint1 = new Paint();
        mBadgePaint1.setColor(Color.parseColor("#EEEEEE"));
        mBadgePaint1.setAntiAlias(true);
        mBadgePaint1.setStyle(Paint.Style.FILL);

        mTextPaint = new Paint();
        mTextPaint.setColor(Color.WHITE);
        mTextPaint.setTypeface(Typeface.DEFAULT);
        mTextPaint.setTextSize(mTextSize);
        mTextPaint.setAntiAlias(true);
        mTextPaint.setTextAlign(Paint.Align.CENTER);
    }

    private float dpToPx(Context context, float value) {
        Resources r = context.getResources();
        float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, r.getDisplayMetrics());
        return px;
    }


    @Override
    public void draw(Canvas canvas) {
        if (!mWillDraw) {
            return;
        }
        Rect bounds = getBounds();
        float width = bounds.right - bounds.left;
        float height = bounds.bottom - bounds.top;
        // Position the badge in the top-right quadrant of the icon.

  /*Using Math.max rather than Math.min */
//        float radius = ((Math.max(width, height) / 2)) / 2;
        float radius = width * 0.15f;
        float centerX = (width - radius - 1) +10;
        float centerY = radius -5;
        if(mCount.length() <= 2){
            // Draw badge circle.
            canvas.drawCircle(centerX, centerY, radius+9, mBadgePaint1);
            canvas.drawCircle(centerX, centerY, radius+7, mBadgePaint);
        }
        else{
            canvas.drawCircle(centerX, centerY, radius+10, mBadgePaint1);
            canvas.drawCircle(centerX, centerY, radius+8, mBadgePaint);
        }
        // Draw badge count text inside the circle.
        mTextPaint.getTextBounds(mCount, 0, mCount.length(), mTxtRect);
        float textHeight = mTxtRect.bottom - mTxtRect.top;
        float textY = centerY + (textHeight / 2f);
        if(mCount.length() > 2)
        canvas.drawText("99+", centerX, textY, mTextPaint);
        else
        canvas.drawText(mCount, centerX, textY, mTextPaint);
    }

    /*
     Sets the count (i.e notifications) to display.
      */
    public void setCount(String count) {
        mCount = count;
        // Only draw a badge if there are notifications.
        mWillDraw = !count.equalsIgnoreCase("0");
        invalidateSelf();
    }

    @Override
    public void setAlpha(int alpha) {
        // do nothing
    }

    @Override
    public void setColorFilter(ColorFilter cf) {
        // do nothing
    }

    @Override
    public int getOpacity() {
        return PixelFormat.UNKNOWN;
    }
}

第 2 步:现在创建一个可绘制对象(在我的例子中是 ic_badge_drawable.xml)。然后复制并粘贴到xml文本下方。

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:id="@+id/ic_main_icon"
        android:drawable="@drawable/ic_burger"
        android:gravity="center" />


    <!-- set a place holder Drawable so android:drawable isn't null -->
    <item android:id="@+id/ic_badge"
        android:drawable="@drawable/ic_burger" />
</layer-list>

这里你现在可以传递任何drawable,以后我们可以传递任何drawable给那些。这些就像占位符一样。

第 3 步:我们已经完成了所有设置。现在您可以使用以下方法为任何可绘制对象设置徽章计数。

 private Drawable setBadgeCount(Context context, int res, int badgeCount){
        LayerDrawable icon = (LayerDrawable) ContextCompat.getDrawable(context, R.drawable.ic_badge_drawable);
        Drawable mainIcon = ContextCompat.getDrawable(context, res);
        BadgeDrawable badge = new BadgeDrawable(context);
        badge.setCount(String.valueOf(badgeCount));
        icon.mutate();
        icon.setDrawableByLayerId(R.id.ic_badge, badge);
        icon.setDrawableByLayerId(R.id.ic_main_icon, mainIcon);

        return icon;
    }

第 4 步:我使用它来更改我的默认汉堡图标。

setSupportActionBar(toolbar);
getSupportActionBar().setHomeAsUpIndicator(setBadgeCount(this,R.drawable.ic_burger, 3));
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowCustomEnabled(true);   // enable overriding the default toolbar layout
getSupportActionBar().setDisplayShowTitleEnabled(false);// disable the default title element here (for centered title)

【讨论】:

    【解决方案2】:

    我在 android.support.design.widget.AppBarLayout 中使用了简单的 TextView,请查看下面的完整代码

    MainActivity.java

    import android.os.Bundle;
    import android.support.v4.view.GravityCompat;
    import android.support.v4.widget.DrawerLayout;
    import android.support.v7.app.ActionBar;
    import android.support.v7.app.AppCompatActivity;
    import android.support.v7.widget.Toolbar;
    import android.view.Menu;
    import android.widget.TextView;
    
    public class MainActivity extends AppCompatActivity {
    
         private DrawerLayout mDrawerLayout;
    
         @Override
         protected void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
             setContentView(R.layout.activity_main);
             initViews();
         }
    
         private void initViews() {
             mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
             setUpToolbar();
         }
    
         private void setUpToolbar() {
             Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
             setSupportActionBar(toolbar);
             final ActionBar ab = getSupportActionBar();
             ab.setHomeAsUpIndicator(R.drawable.navigation_drawericon);
             ab.setDisplayHomeAsUpEnabled(true);
         }
    
         @Override
         public boolean onCreateOptionsMenu(Menu menu) {
             getMenuInflater().inflate(R.menu.home_menu, menu);
             return true;
         }
    
         @Override
         public boolean onOptionsItemSelected(android.view.MenuItem item) {
             switch (item.getItemId()) {
                 case android.R.id.home:
                     mDrawerLayout.openDrawer(GravityCompat.START);
                     return true;
             }
             return super.onOptionsItemSelected(item);
         }
    
         private void updateCounter(int count) {
             ((TextView) findViewById(R.id.tv_nav_drawer_count)).setText(count + "");
         }
    
         public void closeDrawer() {
             mDrawerLayout.closeDrawers();
         }
      }
    

    activity_main.xml

        <?xml version="1.0" encoding="utf-8"?>
        <android.support.v4.widget.DrawerLayout
            xmlns:android="http://schemas.android.com/apk/res/android"
            android:id="@+id/drawer_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@android:color/white"
            android:fitsSystemWindows="true">
    
            <include layout="@layout/container_layout"/>
    
            <android.support.design.widget.NavigationView
                android:id="@+id/navigation_view"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_gravity="start"
                android:fitsSystemWindows="true">
    
            </android.support.design.widget.NavigationView>
    
    
        </android.support.v4.widget.DrawerLayout>
    

    container_layout.xml

    <android.support.design.widget.CoordinatorLayout
                xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:app="http://schemas.android.com/apk/res-auto"
                android:id="@+id/main_content"
                android:layout_width="match_parent"
                android:layout_height="match_parent">
    
                <android.support.design.widget.AppBarLayout
                    android:id="@+id/appbar"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
    
                    <android.support.v7.widget.Toolbar
                        android:id="@+id/toolbar"
                        android:layout_width="match_parent"
                        android:layout_height="?attr/actionBarSize"
                        android:background="?attr/colorPrimary"
                        app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
    
    
                    <TextView
                        android:id="@+id/tv_nav_drawer_count"
                        android:layout_width="15dp"
                        android:layout_height="15dp"
                        android:layout_marginLeft="30dp"
                        android:layout_marginTop="-45dp"
                        android:background="@drawable/menu_text_bg"
                        android:gravity="center"
                        android:text="10"
                        android:textColor="@android:color/white"
                        android:textSize="8dp"/>
    
    
                </android.support.design.widget.AppBarLayout>
    
                <FrameLayout
                    android:id="@+id/home_frame_container"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:layout_marginTop="?actionBarSize"/>
    
    
            </android.support.design.widget.CoordinatorLayout>
    

    home_menu.xml

         <?xml version="1.0" encoding="utf-8"?>
            <menu xmlns:android="http://schemas.android.com/apk/res/android"
                  xmlns:app="http://schemas.android.com/apk/res-auto">
    
                <item
                    android:id="@+id/menu_search"
                    android:icon="@android:drawable/ic_menu_search"
                    android:orderInCategory="101"
                    android:title="Search"
                    app:showAsAction="always"/>
            </menu>
    

    menu_text_bg.xml

    <vector xmlns:android="http://schemas.android.com/apk/res/android"
            android:width="24dp"
            android:height="24dp"
            android:viewportHeight="24.0"
            android:viewportWidth="24.0">
        <path
            android:fillColor="@android:color/holo_red_dark"
            android:pathData="M12,12m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"/>
    </vector>
    

    【讨论】:

    • 感谢您的回答。您的解决方案适用于一个屏幕(老实说,这是我的第一个想法,我什至使用 textviewtoolbar 中编写了与您类似的代码。但是当我意识到我需要重写布局时在应用程序的每个屏幕上,我开始寻找更通用的解决方案。
    • 如果您的 TextView 像我一样在工具栏下方滑动,请将 android:elevation 值设置为高于工具栏的值以修复绘图顺序。
    • 谢谢,它有效,但我不得不在 menu_text_bg.xml 文件中使用形状而不是矢量 schemas.android.com/apk/res/android" android:shape="oval"> 跨度>
    • 非常简单高效的技术
    【解决方案3】:

    我知道它已经晚了,但仍然。 如果您使用 android Side Navigation Menu 作为启动器,那么会有一个文件名 app_bar_main.xml 在这个文件中,你会看到一些类似下面的代码,除了 TextView 这个 TextView 负责显示徽章,只需在 MainActivity 中初始化它正在初始化您的工具栏并根据您的需要更改其可见性(当计数为正数时可见,否则消失),因为您可以看到它的可见性 走了

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.design.widget.CoordinatorLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.thumbsol.beakns.activities.MainActivity">
    
    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">
    
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay"></android.support.v7.widget.Toolbar>
    
        <TextView
            android:id="@+id/hamburger_count"
            android:layout_width="25dp"
            android:layout_height="25dp"
            android:layout_marginLeft="30dp"
            android:layout_marginTop="-45dp"
            android:background="@drawable/red_circle_bacground"
            android:gravity="center"
            android:text="10"
            android:textColor="@android:color/white"
            android:visibility="gone" />
    
        </android.support.design.widget.AppBarLayout>
    
      <include layout="@layout/content_main" />
    

    这里是red_circle_bacground.xml的代码放在drawable中

    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#f00" />
    <corners
        android:bottomLeftRadius="30dp"
        android:bottomRightRadius="30dp"
        android:topLeftRadius="30dp"
        android:topRightRadius="30dp" />
    
      <size
        android:height="25dp"
        android:width="25dp"/>
    </shape>
    

    【讨论】:

    • 如此简单而伟大的解决方案!谢谢
    【解决方案4】:

    根据 TDSoft 的回答,您可以通过扩展 DrawerArrowDrawable 来进一步缩短过程。

    第1步:(除了DrawerArrowDrable,基本同TDSoft的Badge类)。

    public class BadgeNavigationDrawable extends DrawerArrowDrawable{
    
    private float mTextSize;
    private Paint mBadgePaint;
    private Paint mBadgePaint1;
    private Paint mTextPaint;
    private Rect mTxtRect = new Rect();
    
    private String mCount = "";
    private boolean mWillDraw = false;
    
    
    
    /**
     * @param context used to get the configuration for the drawable from
     */
    public BadgeNavigationDrawable(Context context) {
        super(context);
    
        setColor(context.getResources().getColor(R.color.accent));
    
        mTextSize = dpToPx(context, 8); //text size
        mBadgePaint = new Paint();
        mBadgePaint.setColor(Color.RED);
        mBadgePaint.setAntiAlias(true);
        mBadgePaint.setStyle(Paint.Style.FILL);
        mBadgePaint1 = new Paint();
        mBadgePaint1.setColor(Color.parseColor("#EEEEEE"));
        mBadgePaint1.setAntiAlias(true);
        mBadgePaint1.setStyle(Paint.Style.FILL);
    
        mTextPaint = new Paint();
        mTextPaint.setColor(Color.WHITE);
        mTextPaint.setTypeface(Typeface.DEFAULT);
        mTextPaint.setTextSize(mTextSize);
        mTextPaint.setAntiAlias(true);
        mTextPaint.setTextAlign(Paint.Align.CENTER);
    
    }
    
    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);
    
        if (!mWillDraw) {
            return;
        }
        Rect bounds = getBounds();
        float width = bounds.right - bounds.left;
        float height = bounds.bottom - bounds.top;
        // Position the badge in the top-right quadrant of the icon.
    /*Using Math.max rather than Math.min */
    //  float radius = ((Math.max(width, height) / 2)) / 2;
        float radius = width * 0.15f;
        float centerX = (width - radius - 1) +10;
        float centerY = radius -5;
        if(mCount.length() <= 2){
            // Draw badge circle.
            canvas.drawCircle(centerX, centerY, radius+9, mBadgePaint1);
            canvas.drawCircle(centerX, centerY, radius+7, mBadgePaint);
        }
        else{
            canvas.drawCircle(centerX, centerY, radius+10, mBadgePaint1);
            canvas.drawCircle(centerX, centerY, radius+8, mBadgePaint);
        }
        // Draw badge count text inside the circle.
        mTextPaint.getTextBounds(mCount, 0, mCount.length(), mTxtRect);
        float textHeight = mTxtRect.bottom - mTxtRect.top;
        float textY = centerY + (textHeight / 2f);
        if(mCount.length() > 2)
            canvas.drawText("99+", centerX, textY, mTextPaint);
        else
            canvas.drawText(mCount, centerX, textY, mTextPaint);
    }
    
    /*
     Sets the count (i.e notifications) to display.
      */
    public void setCount(String count) {
        mCount = count;
        // Only draw a badge if there are notifications.
        mWillDraw = !count.equalsIgnoreCase("0");
        invalidateSelf();
    }
    
    @Override
    public void setAlpha(int alpha) {
        // do nothing
    }
    
    @Override
    public void setColorFilter(ColorFilter cf) {
        // do nothing
    }
    
    @Override
    public int getOpacity() {
        return PixelFormat.UNKNOWN;
    }
    private float dpToPx(Context context, float value) {
        Resources r = context.getResources();
        float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, r.getDisplayMetrics());
        return px;
    }
    

    }

    第 2 步:这就是您所需要的,如果您需要更新导航抽屉图标,请调用 - 您的事件全部保留。

    BadgeNavigationDrawable drawerIcon = new BadgeNavigationDrawable(MainActivity.this);
    drawerIcon.setCount(String.valueOf(count));
    
    mDrawerToggle.setDrawerArrowDrawable(drawerIcon);
                        mDrawerToggle.syncState();
    

    【讨论】:

      【解决方案5】:

      我通过在我的主布局中添加TextView 并定位切换来实现这一点。 在我的 mainactivity.xml 我添加了这个

       <RelativeLayout 
          android:layout_width="match_parent"
          android:layout_height="match_parent">
          <TextView
              android:id="@+id/badge_ham"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:layout_marginLeft="26dp"
              android:layout_marginTop="14dp"
              android:textSize="9sp" />
      </RelativeLayout>
      

      以及添加徽章:

      private  void addBadgeToHamburger(int badge){
          badge_ham.setVisibility(View.VISIBLE);
          String counter = Integer.toString(badge);
          String s =  "   " + counter + " ";
          SpannableString sColored = new SpannableString(s);
      
          sColored.setSpan(new RoundedBackgroundSpan(Color.RED, Color.WHITE), s.length() - 3, s.length(), 0);
          badge_ham.setText(sColored);
      }
      

      【讨论】:

      • 能够在 Kitkat 中显示徽章...但对于 Lollipop,奇怪的是它在引用徽章视图时给出了 NullPointerException,在您的情况下为“badge_ham”。你遇到过这种情况吗?
      • -1 不完整的答案。 RoundedBackgroundSpan 是我们必须想出的东西吗?找到了一些类似的实现,但没有使用相同的构造函数。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多