Search code examples
androidnotificationsactionbarsherlockandroid-custom-view

How to add a clickable "events" action item to actionBarSherlock?


Background

many apps (including google plus and facebook) have an action bar item showing the number of in-app-events (or "notifications").

This action item has a number within it and you can click it in order to show the events the app has for the user.

something like that (taken from here) :

enter image description here

The problem

I wish to make it work on old android versions, so i use actionBarSherlock.

Sadly, each solution i use has its disadvantages, and i couldn't find any solution here (on stackOverflow) that handles this with actionBarSherlock (found other solutions, but not with this library).

I've also found a post about it (here) , claiming it's an issue on this library, but it's very old and seems to be closed and marked as fixed, but I can't find out how to use it now.

What I've tried

i've tried the next solutions:

  • actionLayout . it showed fine, but clicking on it didn't show the clicking effect.
  • actionViewClass - it didn't even work for some reason.
  • adding the menu item and its view programmatically.

The question

What's the best way to achieve this ?


EDIT: this is what i've tried using actionLayout :

"action_item_notification.xml" - for now it's the same as "abs__action_menu_item_layout.xml" (here). later i will add a textView to hold the number of notifications.

in the menu xml file, i have this as one of the items:

<item
android:id="@+id/activity_main__menuItem_notifications"
android:actionLayout="@layout/action_item_notification"
android:icon="@drawable/notification_button"
android:showAsAction="always"
android:title="@string/notifications"/>

not only it doesn't show the icon, but long clicking on the item will crash the app, with a NPE on the ActionMenuItemView.java file.


EDIT:ok, so i've found a solution that is almost perfect.

it shows the action item nicely and it even reacts to clicking as the other action items.

I've sadly had one missing feature - long clicking on action item to show the toast of its title. sadly, i couldn't find a way to overcome this so what i did (that works) is handling the long clicking on the view itself, and call a similar code that is used for ActionMenuItemView::onLongClick .

if anyone has a better and nicer solution, please write it down.

i've written this solution in a new answer here.


Solution

  • here's my solution, but it's a bit messy and calls the same code of showing a toast for the action item as the one of actionBarSherlock.

    if anyone has a better, cleaner solution, please write it down.

    menu file (activity_main.xml) :

    ...
    <item
        android:id="@+id/activity_main__menuItem_notifications"
        android:showAsAction="always"
        android:title="@string/notifications"/>
    ...
    

    MainActivity.java :

    public boolean onCreateOptionsMenu(...){
    ...
    getSupportMenuInflater().inflate(R.menu.activity_main, menu);
    //
    final MenuItem notificationsMenuItem = menu.findItem(R.id.activity_main__menuItem_notifications);
    notificationsMenuItem.setActionView(R.layout.action_item_notification);
    setEnableLongClickOnCustomActionItem(notificationsMenuItem,true);
    ...
    public static void setEnableLongClickOnCustomActionItem(final MenuItem menuItem, final boolean enable) {
        final View actionView = menuItem.getActionView();
        if (actionView == null)
            return;
        final CharSequence title = menuItem.getTitle();
        if (!enable || Strings.isEmpty(title))
            actionView.setOnLongClickListener(null);
        actionView.setOnLongClickListener(new OnLongClickListener() {
    
            @Override
            public boolean onLongClick(final View v) {
                final int[] screenPos = new int[2];
                final Rect displayFrame = new Rect();
                actionView.getLocationOnScreen(screenPos);
                actionView.getWindowVisibleDisplayFrame(displayFrame);
    
                final Context context = actionView.getContext();
                final int width = actionView.getWidth();
                final int height = actionView.getHeight();
                final int midy = screenPos[1] + height / 2;
                final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
    
                final Toast cheatSheet = Toast.makeText(context, title, Toast.LENGTH_SHORT);
                if (midy < displayFrame.height()) {
                    // Show along the top; follow action buttons
                    cheatSheet.setGravity(Gravity.TOP | Gravity.RIGHT, screenWidth - screenPos[0] - width / 2, height);
                } else {
                    // Show along the bottom center
                    cheatSheet.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, height);
                }
                cheatSheet.show();
                return true;
            }
        });
    

    layout file of the action item ( action_item_notification.xml) :

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        style="?attr/actionButtonStyle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:addStatesFromChildren="true"
        android:focusable="true" >
    
        <ImageView
            android:id="@+id/imageView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:adjustViewBounds="true"
            android:background="@null"
            android:focusable="false"
            android:scaleType="fitCenter"
            android:src="@drawable/notification_button" />
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignBottom="@+id/imageView1"
            android:layout_alignRight="@+id/imageView1"
            android:background="@drawable/action_item_notification_counter_background"
            android:paddingLeft="1dp"
            android:paddingRight="1dp"
            android:text="88"
            android:textColor="#FFffffff"
            tools:ignore="HardcodedText" />
    
    </RelativeLayout>
    

    and a nice drawable for the background of the textView ("action_item_notification_counter_background.xml") :

    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="oval" >
    
        <solid android:color="#FFff0000" />
    
    </shape>