12

I have a menu item in the action bar. Along with the menu item image, I need to show some number associated with it which will change often. I am not using Action bar sherlock. I don't want to use that. Other than this everything else just works fine. In the shown image, the white icon color icon is mine. I need to generate the number with the red color background dynamically. How can I do that in Android?

Here is the sample image:

enter image description here

Update:

I have this menu item in my menu.xml. This should work like a notification menu item which shows the number of notification count. I set the menu icon like,

 menuItem.setIcon(image);

Now, on top of the menu item I need to place one text view which has the total count of notifications.

is it possible to implement this functionality with viewbadger? Github url

intrepidkarthi
  • 3,104
  • 8
  • 42
  • 75

4 Answers4

7

I discovered how to add an actionView to a menu item and retrieve as set values to the view in code.

See here: https://stackoverflow.com/a/16648170/857681

Community
  • 1
  • 1
speedynomads
  • 2,632
  • 1
  • 26
  • 24
5

After a lot of trying of nearly all resources on SO I turned to blogs; succesfully. I want to share what worked for me (Api >= 13); source.

Let's start with the sweet code, the way it's used:

 public boolean onCreateOptionsMenu(Menu menu) {
    //inflate menu
    getMenuInflater().inflate(R.menu.menu_my, menu);

    // Get the notifications MenuItem and LayerDrawable (layer-list)
    MenuItem item = menu.findItem(R.id.action_notifications);
    LayerDrawable icon = (LayerDrawable) item.getIcon();

    // Update LayerDrawable's BadgeDrawable
    Utils2.setBadgeCount(this, icon, 2);

    return true;
}

The menu_my.xml:

<menu 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"
    tools:context=".MainActivity">
    <item
        android:id="@+id/action_notifications"
        android:icon="@drawable/ic_menu_notifications"
        android:title="Notifications"
        app:showAsAction="always" />
</menu>

This class that conveniently makes a BadgeDrawable; its appearance can be modified as well:

public class BadgeDrawable extends Drawable {

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

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

    public BadgeDrawable(Context context) {
        //mTextSize = context.getResources().getDimension(R.dimen.badge_text_size);
        mTextSize = 12F;

        mBadgePaint = new Paint();
        mBadgePaint.setColor(Color.RED);
        mBadgePaint.setAntiAlias(true);
        mBadgePaint.setStyle(Paint.Style.FILL);

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

    @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.
        float radius = ((Math.min(width, height) / 2) - 1) / 2;
        float centerX = width - radius - 1;
        float centerY = radius + 1;

        // Draw badge circle.
        canvas.drawCircle(centerX, centerY, radius, 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);
        canvas.drawText(mCount, centerX, textY, mTextPaint);
    }

    /*
    Sets the count (i.e notifications) to display.
     */
    public void setCount(int count) {
        mCount = Integer.toString(count);

        // Only draw a badge if there are notifications.
        mWillDraw = count > 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;
    }
}

This class that helps to set the number. I recommend implementing even more thods to set badge as date, etc:

public class Utils2 {
    public static void setBadgeCount(Context context, LayerDrawable icon, int count) {

        BadgeDrawable badge;

        // Reuse drawable if possible
        Drawable reuse = icon.findDrawableByLayerId(R.id.ic_badge);
        if (reuse != null && reuse instanceof BadgeDrawable) {
            badge = (BadgeDrawable) reuse;
        } else {
            badge = new BadgeDrawable(context);
        }

        badge.setCount(count);
        icon.mutate();
        icon.setDrawableByLayerId(R.id.ic_badge, badge);
    }


}

And mui importante a drawable (like a layout) in res/drawable:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/ic_notification"
        android:drawable="@drawable/ice_skate"
        android:gravity="center" />

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

Good lucks!

msysmilu
  • 2,015
  • 23
  • 24
  • Is your ic_menu_notifications is layer list file you have mentioned in the bottom? – Lips_coder Oct 26 '16 at 04:45
  • You just copied the code from the source. Please stop doing that. – mahoriR May 03 '17 at 19:22
  • @Ravi, that is not what had I done; the source link doesn't even work anymore; and I can assure you this code was in production on my app before I posted it here; it's important to explain how this 5 blocks of code work as well. – msysmilu Jun 29 '17 at 21:54
  • Link is not working. – Pratik Butani Dec 28 '17 at 12:47
  • @PratikButani, true; that's why I pasted in the snippets. – msysmilu Jan 06 '18 at 16:47
  • 1
    I was getting ClassCastException for conversion from BitmapDrawable to LayerDrawable. follow this for this error https://stackoverflow.com/questions/37386027/java-lang-classcastexception-android-graphics-drawable-bitmapdrawable-cannot-be – Virat18 Jan 08 '19 at 14:29
1

Here is one thing you can try:

Create a custom Drawable that paint you image in the background and text on top of the image. Check out this post for sample.

Then set this Drawable as the MenuItem background dynamically...

Community
  • 1
  • 1
Praful Bhatnagar
  • 7,425
  • 2
  • 36
  • 44
0

Use action view. It works with both: default ActionBar and ActionBarSherlock.

Here is an example

With this approach you can just create your own View (by inflating some layout for example) and then do whatever you want (change background, change content, add another views dynamically if your action view is subclass of ViewGroup etc.).

Dmitry Zaytsev
  • 23,650
  • 14
  • 92
  • 146