How to recognize whether the Done button is clicked in ActionMode
Asked Answered
A

3

37

I use ActionMode to select items in a grid. The problem is that I cannot recognize whether exactly the Done button is clicked. The only I can is to know that ActionMode is finished. But pressing Back finishes the ActionMode too. The desired behavior is to accept selection on Done click, and exit ActionMode on Back press.

I tried to use ActionMode.setCustomView() but it doesn't affect the Done button. The Activity.onBackPressed() is not called when ActionMode is started.

The one solution I've found is to use ActionBarSherlock and get the Done button manually:

View closeButton = findViewById(R.id.abs__action_mode_close_button); 

But it works on Android 2.x-3.x only, because on 4.x a native action bar is used.

Anking answered 25/7, 2012 at 4:44 Comment(0)
H
40

Please don't do that as it's implementation specific and extremely non-standard.

You can use the onDestroyActionMode callback for when an action mode is dismissed.

Harvell answered 25/7, 2012 at 6:14 Comment(9)
I see, but I'm implementing a picker with multiselection support and I thought that the Done button is a best way to accept selection... Any way, thanks for you answer! It seems like I have to add a menu item for accepting selected items.Anking
But how can we distinguish from the done button and the back button? I need to implement different actions for those...Sherborn
You might be able to intercept onBackPressed. Otherwise, you can't.Harvell
@JakeWharton how to distinguish the destruction of ActionMode due to screen rotation or done button . actually i have the a list of ids to add to fav database table , so if the device orientation change i should retain this array and if the user click the done button i should clear it , please helpFavour
@JakeWharton but how about this scenario: I have a fragment with a list view in it, and that fragment is in a ViewPager. Tap and hold starts CAB, and pressing done cancels CAB. Say i'm in the middle of multiselecting things (so CAB is on). When I swipe to another fragment in the ViewPager, naturally I want to remove the CAB so to do this, I finish the actionmode on the first fragment's onPause method and when the user swipes back to that fragment, i start the CAB again. I think i should be able to differentiate dismissing CAB via swipe and via Done so that I can restart CAB onPauseParliament
If you dismissed the action mode then your app should know that it was done via swipe. If you want different behavior file a feature request on b.android.com for it. I'm simply telling you the existing behavior.Harvell
@Tanya: A menu item is absolutely the correct way to accept selected items. "Done" doesn't mean "done selecting items now do something" but rather "cancel all selections."Shevat
@JakeWharton thank you for answer. its useful for me.Marplot
my problem is that onDestroyActionMode gets called as soon as the ActionMode is created, therefore I cannot use the BS standard callback. Android sucksSeaborg
C
21

Here is the solution:

ActionMode mMode = MyActivityClass.this.startActionMode(some implementation);
int doneButtonId = Resources.getSystem().getIdentifier("action_mode_close_button", "id", "android");
View doneButton = MyActivityClass.this.findViewById(doneButtonId);
doneButton.setOnClickListener(new View.OnClickListener() {

    @Override
    public void onClick(View v) {
        // do whatever you want 
        // in android source code it's calling mMode.finish();
    }
});
Cumulate answered 30/12, 2012 at 12:32 Comment(5)
just a side note: pay attention that you call findViewById from the activity where you started the action mode and not from an other view, or you will get a null pointer exception.Fabron
This does not work with ABS on pre-ICS devices like Gingerbread.Jamikajamil
This code is only works on API LEVEL >= 11 You need to add API LEVEL condition: View doneButton; if (android.os.Build.VERSION.SDK_INT >= 11) { doneButton = Resources.getSystem().getIdentifier("action_mode_close_button", "id", "android"); } else { doneButton = findViewById(R.id.abs__action_mode_close_button); }Cumulate
WARNING: this is an inflexible solution and it is essentially accessing a private API. If Google ever changes the name of action_mode_close_button, this will cease to work.Northampton
This solution works perfectly except on orientation change button looses its click listener. I am not destroying activity on orientation change.Spermatid
L
11

Here is my implementation, and it's a proper hack but it works and I can't really find an alternative to doing something specific when the ActionMode DONE is clicked. I find it really weird that you can't capture this event more elegantly.

Any suggestions to making this slightly less ugly would be greatly appreciated...

In my activity..

boolean mActionModeIsActive = false;
boolean mBackWasPressedInActionMode = false;

@Override
public boolean dispatchKeyEvent(KeyEvent event)
{
    mBackWasPressedInActionMode = mActionModeIsActive && event.getKeyCode() == KeyEvent.KEYCODE_BACK;
    return super.dispatchKeyEvent(event);
}

@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu)
{
    mActionModeIsActive = true;
    return true;
}

@Override
public void onDestroyActionMode(ActionMode mode)
{
    mActionModeIsActive = false;

    if (!mBackWasPressedInActionMode)
        onActionModeDoneClick();

    mBackWasPressedInActionMode = false;
}

public void onActionModeDoneClick();
{
    // Do something here.
}

If you are using Fragments with your Activity then some of this code will probably need to be in the Fragment, and the other bits in the Activity.

@JakeWharton (and other ActionBarSherlock users) if you see this on your travels. I'd be interested to know if the above is compatible with ABS as I have yet to integrate ABS with my current project.

Loci answered 28/3, 2013 at 20:57 Comment(4)
This did work, but I am using action modes in a few places in my project. So I decided to add MenuItems to the action modes and use these as the actual "confirm" items. The main I converted the icon into a dismiss. This makes the code a lot less hacky.Loci
It looks like this would also detect when normal menu items are clicked, not just when the Done button was pressed.Serb
@Serb what makes you think that?Onslaught
As for the answer, looks good but it works without mActionModeIsActive as well: instead just be sure to set mBackWasPressedInActionMode = false in onCreateActionMode and then remove the && in dispatchKeyEvent so it's just the second test.Onslaught

© 2022 - 2024 — McMap. All rights reserved.