42

I want something like this:

enter image description here

The 3rd icon is for notifications and it is just a png image now. Is it possible to do something, so that i can change the text/number ie.., 03 programatically to show the actual no.of notifications.

Thank You

Archie.bpgc
  • 23,812
  • 38
  • 150
  • 226

4 Answers4

125

Here is some example code that worked for me.

1: Create a layout for your badge menu item.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="48dp"
    android:layout_height="fill_parent"
    android:layout_gravity="right" >

    <!-- Menu Item Image -->
    <ImageView
        android:layout_width="48dp"
        android:layout_height="fill_parent"
        android:clickable="true"
        android:src="@drawable/bkg_actionbar_notify_off" />

    <!-- Badge Count -->    
    <TextView
        android:id="@+id/actionbar_notifcation_textview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:padding="@dimen/padding_small"
        android:text="99"
        android:textColor="@color/holo_orange_dark" />

</RelativeLayout>

2: Create a menu item in res/menu and set the actionLayout to your layout

<menu xmlns:android="http://schemas.android.com/apk/res/android" >
    <item
        android:id="@+id/badge"
        android:actionLayout="@layout/actionbar_badge_layout"
        android:icon="@drawable/icn_menu_posts"
        android:showAsAction="always">
    </item>
</menu>

3: Then in onCreateOptionsMenu of your activity or fragment you can do something like this...

public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    inflater.inflate(R.menu.badge, menu);

    RelativeLayout badgeLayout = (RelativeLayout) menu.findItem(R.id.badge).getActionView();
    TextView tv = (TextView) badgeLayout.findViewById(R.id.actionbar_notifcation_textview);
    tv.setText("12");
}

Note: If you wanted to change the badge count later on, you could store a reference to the Menu object passed to onCreateOptionsMenu and use the same code to get the required view and set a value.

=== ApCompat Warning ==================================================

If using the AppCompatActivity then you must set the actionView in onCreateOptionsMenu

 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
      getMenuInflater().inflate(R.menu.main_menu, menu);
      MenuItem item = menu.findItem(R.id.badge);
      MenuItemCompat.setActionView(item, R.layout.actionbar_badge_layout);
      RelativeLayout notifCount = (RelativeLayout) MenuItemCompat.getActionView(item);

    TextView tv = (TextView) notifCount.findViewById(R.id.actionbar_notifcation_textview);
    tv.setText("12");

    return super.onCreateOptionsMenu(menu);
Noman Rafique
  • 3,735
  • 26
  • 29
speedynomads
  • 2,632
  • 1
  • 26
  • 24
  • 2
    Thanks your is working good but the problem is notification_menu becomes unclickable.... where I might be wrong? – Lalit Sharma Nov 27 '13 at 07:38
  • 1
    You can override onCreateOptions in an activity which passes you the a Menu object, then call menu.getItem(i).getActionView().setOnClickListener where i is the index of your visible menu item, then then in your OnClickListener check that v.getId() == R.id.badge (this is the id of your menu item not any of the views)... – speedynomads Nov 27 '13 at 12:15
  • working fine..Still unable to get the onclick working on the menu item click. Could you post that code as well? – TharakaNirmana Dec 11 '13 at 11:05
  • 7
    I got onclick working by changing ImageView in actionbar_badge_layout with ImageButton with id imagebuttonid and then adding onClick listener to that button: MenuItemCompat.getActionView(menu.findItem(R.id.actionbar_badge_layout)).findViewById(R.id.imagebuttonid).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { System.out.println("clicked"); } }); – gile Dec 16 '13 at 15:56
  • 1
    Hey I used your technique and it worked but I also want multiple action icons but one 1 with badge.. the problem here is that the other icons are not being shown.. – d3m0li5h3r May 23 '14 at 11:29
  • 1
    I'm getting a null pointer at `RelativeLayout badgeLayout = (RelativeLayout) menu.findItem(R.id.action_choose_programs).getActionView();`, any thoughts? – msysmilu Feb 10 '15 at 21:15
  • Is action_choose_programs the id of your menu > item or the id of the actionView layout? – speedynomads Feb 11 '15 at 12:51
  • 1
    In the below line I am getting null pointer exception. TextView tv = (TextView) badgeLayout.findViewById(R.id.actionbar_notifcation_textview);... I think the badgeLayout is null...Any idea? – sarath Feb 12 '15 at 10:31
  • 1
    Have you set a layout that contains a textview with the id actionbar_notifcation_textview to be the actionLayout of the menu item in question? – speedynomads Feb 12 '15 at 16:36
  • I copied exactly same code but `TextView tv = (TextView) badgeLayout.findViewById(R.id.actionbar_notifcation_textview);` here I am getting nullpointer error do you have any idea? – Pushpendra Jul 23 '15 at 17:50
  • Do you have a menu item xml file in res/menu that has an with an id of badge? If not you will get a null pointer trying to access badgeLayout. If that does exist then you need to make sure you have a layout xml file with a textview that has an id of actionbar_notifcation_textview – speedynomads Jul 24 '15 at 10:36
  • Those getting a null pointer just make sure that when using `setText` on the textview, you are using a string. – Deepak Negi Aug 04 '15 at 10:34
  • 1
    Thanks so much for the great answer! Just to add, for AppCompat, instead of using code to `setActionView`, you can just use the `app:` namespace to make it work in xml, something like: `app:actionLayout="@layout/actionbar_badge_layout"` – P.L. Nov 11 '15 at 07:32
  • 3
    You have to redirect the click event: // Manually handle the click item.getActionView().setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { onOptionsItemSelected(item); } }); – Denny Weinberg Jun 29 '16 at 18:13
  • Thanks, a good solution. If you want to realize onClickListener, see http://stackoverflow.com/a/39174874/2914140. – CoolMind Aug 26 '16 at 21:33
8

One option is to create your own action view for this. Use android:actionLayout in the XML and getActionView() in Java after inflation to manipulate it. Your action view would be an ImageView (for the icon) and... something for the badge. I suspect that you will find that trying to make that badge via text will be difficult, and that you are better served with a bunch of badge images, one of which you layer on top of the icon (e.g., via RelativeLayout or FrameLayout, or by wrapping the icon in a LayerListDrawable).

Another option is simply to have N versions of the icon+badge, perhaps wrapped in a LevelListDrawable, that you choose from at runtime.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
1

Had to make some changes to domji84's answers. For some reason the clicking on imageview was not calling the click event, it works find after removing the clicklabe = "true" from image view.

menu_item_cart.xml

    <!-- Menu Item Image -->
    <ImageView
        android:layout_width="30dp"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_launcher" />

    <!-- Badge Count -->
    <TextView
        android:id="@+id/cartCountTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:padding="5dp"
        android:layout_marginTop="5dp"
        android:text="99"
        android:textColor="@android:color/white"
        android:textStyle="bold"/>

</RelativeLayout>

menu_main.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_settings"
        android:title="@string/action_settings"
        android:orderInCategory="100"
        app:showAsAction="never" />

    <item
        android:id="@+id/action_cart"
        android:title="test"
        app:actionLayout="@layout/menu_item_cart_count"
        app:showAsAction="always">
    </item>
</menu>

Activity code

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.menu_main, menu);
    menu.findItem(R.id.action_cart).getActionView().setOnClickListener(this);
    return true;
}
Vihaan Verma
  • 12,815
  • 19
  • 97
  • 126
0

Here you can use Custom action bar with default action bar... firstly prepare layout by xml or java.then use display matrics and get window manager.After this inflate the layout. like this

            DisplayMetrics dm = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(dm);
    ActionBar ab = getSupportActionBar();
    View mactionbar = getLayoutInflater().inflate(R.layout.main2, null);
Prashant
  • 13
  • 3