25

I am trying to add a custom ActionView to my ActionBar.

I am trying to add the common refresh button. (ImageButton, ProgressBar inside a FrameLayout) but if I use an ActionView onOptionsItemSelected() is never called.

Here's the code:

In my Activity:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.messages_actionbar, menu);
mRefreshView = (RefreshView) menu.findItem(R.id.messages_refresh).getActionView();

return super.onCreateOptionsMenu(menu);
}

messages_actionbar's src:

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

    <item android:id="@+id/messages_refresh"
        android:title="title"
        android:icon="@drawable/icon"
        android:showAsAction="always"
        android:actionViewClass="com.blabla.RefreshView"/>
</menu>

RefreshView's code:

public class RefreshView extends FrameLayout {

    private ImageView mButton;
    private ProgressBar mProgressBar;
    private boolean mLoading;

    public RefreshView(Context context) {
        super(context, null);
        initView(context);
    }

    public RefreshView(Context context, AttributeSet attrs) {
        super(context, attrs, 0);
        initView(context);
    }

    public RefreshView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initView(context);
    }

    private void initView(Context context) {
        LayoutInflater inflator = LayoutInflater.from(context);
        View v = inflator.inflate(R.layout.actionbar_refresh, this);
        mProgressBar = (ProgressBar) v.findViewById(R.id.action_refresh_progress);
        mButton = (ImageView) v.findViewById(R.id.action_refresh_button);
    }

    public void setLoading(boolean loading) {
        if (loading != mLoading) {
            mProgressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
            mButton.setVisibility(loading ? View.GONE : View.VISIBLE);
            mLoading = loading;
        }
    }
}

actionbar_refresh's src code:

<?xml version="1.0" encoding="utf-8" ?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <ImageView
        android:id="@+id/action_refresh_button"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:scaleType="center"
        android:background="@drawable/icon" />

    <ProgressBar
        android:id="@+id/action_refresh_progress"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:visibility="gone"
        android:indeterminate="true" />
</FrameLayout>

On the other hand, if I set a clickListener to the ImageView inside the RefreshView class it gets called.

Anyone did this already?

Programmer Bruce
  • 64,977
  • 7
  • 99
  • 97
Macarse
  • 91,829
  • 44
  • 175
  • 230
  • 2
    "I am trying to add the common refresh button. (ImageButton, ProgressBar inside a FrameLayout) but if I use an ActionView onOptionsItemSelected() is never called." -- what makes you think it is supposed to be called? It is not a menu choice anymore -- it's your custom `View` -- so I would not expect `onOptionsItemSelected()` to be called. – CommonsWare Mar 26 '11 at 23:42
  • 1
    @CommonsWare: Why not? The view is the `ActionView` of an existing menu item. `menu.findItem(R.id.messages_refresh).getActionView();` returns the correct view. If what you are saying is correct, how does the Gmail app do that? Do you think they set the `clickListener` and the custom view notifies the `Activity` somehow? – Macarse Mar 26 '11 at 23:48
  • That would be my guess. Think about it: suppose I had an action view of 17 Buttons. Would they all trigger `onOptionsItemSelected()`? If so, how would you tell them apart? – CommonsWare Mar 26 '11 at 23:54

3 Answers3

8

onOptionsItemSelected() should only be called if the action item is in the overflow menu which you should also handle. (you have it forced to "always" on action bar, so onOptionsItemSelected() won't get called).

At onCreateOptionsMenu() after inflating, you must setup a OnMenuItemClickListener for the menu item.

Macarse
  • 91,829
  • 44
  • 175
  • 230
CEO
  • 270
  • 3
  • 6
  • I take part of above back. I did some additional testing. onOptionsItemSelected() is not called for action views, only for action items (in the action bar or overflow menu). This explains what you are seeing. But when action view is placed in the overflow menu, onOptionsItemSelected() is be called for it, so onMenuItemClick() if one defined. – CEO Apr 17 '11 at 13:28
3

I ended up using the src code from http://code.google.com/p/styled-action-bar/.

Macarse
  • 91,829
  • 44
  • 175
  • 230
  • That works however I implemented this in a very similar way to how you had it setup since I required more control over the actual view. You should have just implemented an onClickListener on your view and then a callback for the base activity or fragment to implement. Again if all you need is a simple refresh spinner I would suggest the method you went with, I just wanted to say it could have worked with your original code with some slight changes. – Maurycy Mar 31 '12 at 01:28
  • 1
    I've made a tweak from your [blog post](http://android-argentina.blogspot.mx/search?updated-max=2011-11-14T01:32:00-03:00&max-results=7) and now i've uploaded in [Github](https://github.com/nRike/Style-action-bar) – Enrique Diaz Apr 04 '12 at 08:47
1

I have found a working solution for me and I want to share it with you. It's based on the first approach of (@Macarse), with some important changes.

Important: adapt the initView method accordingly

  1. Set an onClickListener to the ImageView (mButton)

  2. Set loading to true & inform the Activity (MyActivity) about the click

    private void initView(final Context context) {
        final LayoutInflater inflator = LayoutInflater.from(context);
    
        final View actionView = inflator.inflate(
                R.layout.action_refresh_progress, this);
        mProgressBar = (ProgressBar) actionView
                .findViewById(R.id.action_refresh_progress);
    
        mButton = (ImageView) actionView
                .findViewById(R.id.action_refresh_button);
    
        mButton.setOnClickListener(new OnClickListener() {
    
            @Override
            public void onClick(final View view) {
                setLoading(true);
                ((MyActivity) context).handleRefreshButtonClick();
            }
        });
    }
    
  3. React accordingly to the click in the Activity (MyActivity)

    public void handleRefreshButtonClick() {
        // Start refreshing...
    }
    

I hope that my approach could save you some time finding a working solution!

droide_91
  • 197
  • 2
  • 6