Question

I have some weird behavior in the contextual action bar.

Firstly:

One menu item is only shown every second time I click on the overflow button:

Clicking behavior overflow button of CAB

Secondly / thirdly:

Is there a way that the icons do not use so much space?

When I change add property android:showAsAction="always" to all items, there is actually enough space to show all icons - but my share icon is not clickable anymore:

All Items with showAsAction always

Clean Project does not help.

I use Android 4.2.2 on my test device (Galaxy S3).

I even tried to completely flash a new ROM on my XXX GS3 (CyanogenMod 10.1 now, before SlimBean, also removed the navigationbar at at the bottom) - did not help.

I also tried it on a Nexus 4. There is more space, so the share button and the delete button are visible. The share button is not clickable when I start action mode, but when I turn the device to landscape mode it works, and when I turn it back to portrait it still works. So basicially on the Nexus 4, the share button does not work before rotating.


Manifest:

<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="17" />

Compiling against minSdkVersion=17 makes no difference.

I start the Action Mode from a fragment like this:

mActionMode = activity.startActionMode(mMultipleCallback);

In the ActionMode.Callback I populate the menu:

@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
    MenuInflater inflater = mode.getMenuInflater();
    inflater.inflate(R.menu.management_cab, menu);
    MenuItem item = menu.findItem(R.id.managementCABShare);
    mShareActionProvider = (ShareActionProvider) item.getActionProvider();
    //...other stuff
    return true;
}

And here is the XML:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
    <item
        android:title="@string/checkAll"
        android:id="@+id/managementCABCheckAll"
        android:icon="@android:drawable/checkbox_on_background">
    </item>
    <item
        android:title="@string/enable"
        android:id="@+id/managementCABEnable"
        android:icon="@drawable/sphere_green">
    </item>
    <item
        android:title="@string/disable"
        android:id="@+id/managementCABDisable"
        android:icon="@drawable/sphere_red">
    </item>
    <item
        android:title="@string/delete"
        android:id="@+id/managementCABDelete"
        android:icon="@android:drawable/ic_menu_close_clear_cancel">
    </item>
    <item
        android:title="@string/share"
        android:id="@+id/managementCABShare"
        android:actionProviderClass="android.widget.ShareActionProvider"
        android:icon="@android:drawable/ic_menu_share">
    </item>
    <item
        android:title="@string/export"
        android:id="@+id/managementCABExport"
        android:icon="@drawable/explorer">
    </item>
</menu>

For the sake of completeness the whole callback:

protected ActionMode.Callback mMultipleCallback = new ActionMode.Callback() {

    private ShareActionProvider mShareActionProvider;

    @Override
    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
        MenuInflater inflater = mode.getMenuInflater();
        inflater.inflate(R.menu.management_cab, menu);
        MenuItem item = menu.findItem(R.id.managementCABShare);
        mShareActionProvider = (ShareActionProvider) item.getActionProvider();
        hideUnwantedCABItems(menu);
        return true;
    }

    @Override
    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
        List<Integer> checkedPositions = getAllCheckedPositions();
        switch (item.getItemId()) {
        case R.id.managementCABCheckAll:
            changeCheckedOfAllItems(true);
            return true;
        case R.id.managementCABEnable:
            changeEnabled(checkedPositions, true);
            return true;
        case R.id.managementCABDisable:
            changeEnabled(checkedPositions, false);
            return true;
        case R.id.managementCABDelete:
            if (deleteAlert == null)
                createDeleteDialog(checkedPositions);
            initDeleteDialog(checkedPositions);
            return true;
        case R.id.managementCABShare:
            Intent shareIntent = new Intent();
            shareIntent.setAction(Intent.ACTION_SEND_MULTIPLE);
            shareIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, exportItemsAndGetUris(checkedPositions));
            shareIntent.setType("application/xml");
            setShareIntent(shareIntent);
            return true;
        case R.id.managementCABExport:
            String message;
            if (StorageController.copyUriListToExportFolder(exportItemsAndGetUris(checkedPositions)))
                message = getActivity().getString(R.string.export_success);
            else
                message = getActivity().getString(R.string.export_fail);

            Toast.makeText(getActivity(), message + ":\n" + StorageController.getExternalExportApplicationFolder(), Toast.LENGTH_LONG).show();
            return true;
        default:
            return false;
        }
    }

    @Override
    public void onDestroyActionMode(ActionMode mode) {
        mActionMode = null;
        changeCheckedOfAllItems(false);
    }

    @Override
    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
        return false;
    }

    private void setShareIntent(Intent shareIntent) {
        if (mShareActionProvider != null) {
            mShareActionProvider.setShareIntent(shareIntent);
        }
    }
};
Was it helpful?

Solution

Okay, the solution to the unclickable share icon. This was kind of a misunderstanding from me to the API.

I thought you could treat a SharedActionProvider-Item like every other item in your menu XML file. But actually you can't. onActionItemClicked is not even triggered for this icon when it's shown as an action (this is why it's not clickable when you add showAsAction=always). Funny enough, the click event is triggered when the icon is not shown, but it is visible in the overflow menu. This may be an actual bug in the contextual action bar!

Now I finally figured out how you should trigger a SharedActionProvider-Item:

You have to (!) put a Intent in the onCreateActionMode method:

public boolean onCreateActionMode(ActionMode mode, Menu menu) {
    MenuInflater inflater = mode.getMenuInflater();
    inflater.inflate(R.menu.management_cab, menu);
    MenuItem item = menu.findItem(R.id.managementCABShare);
    initSharedActionProvider(item); //Check out this method in the next code fragment
    return false;
}

Now you may say: "You idiot, this was obvious, and it's even better to set the intent always there instead as in the onActionItemClickedMethod as you did before".

Sorry, but I disagree: It does not really make sense to set it here. The reason is: For me the intent changes with every additional item you check. It creates an export-XML file for each item you check, and I really don't want to create an XML file each time an icon is clicked. This makes no sense, and I want all XML files to be created only when a user really wants to export the items.

So basically I made a workaround for this. At the beginning, I make an Intent and add an empty List<Uri>. This list is saved as a member variable in my class, so if I add items to it, the items will also be in the intent. Then when the user clicks the share item, the list is populated with all selected items. To accomplish this I overrode the OnShareTargetSelectedListener. This method is triggered when a user clicks on a concrete share target (like email, dropbox, etc.).

Now here's the whole code for that (the method is only called once from onCreateActionMode):

private void initSharedActionProvider(MenuItem item) {
    mShareActionProvider = (ShareActionProvider) item.getActionProvider();
    mShareActionProvider.setOnShareTargetSelectedListener(new OnShareTargetSelectedListener() {
        @Override
        public boolean onShareTargetSelected(ShareActionProvider source, Intent intent) {

            //Here is the exportedFiles list populated
            exportItemsAndSetList(getAllCheckedPositions());
            return true;
        }
    });

    Intent shareIntent = new Intent();
    shareIntent.setAction(Intent.ACTION_SEND_MULTIPLE);

    //exportedFiles is a member Variable which I populate with the selected items, with the exportItemsAndSetList method
    shareIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, exportedFiles);
    shareIntent.setType("application/xml");
    mShareActionProvider.setShareIntent(shareIntent);
}

I hope you understand what I did there. If not, feel free to ask.

With all this above, one of my problems is solved (that the share icon is not clickable when shown as an icon) - but the other two remain open (icons shouln't use so much space and the first problem).


Problem 2 kind of solved:

Seems like Android needs the icons which are in the higher resolution folders (hdpi, xhdpi) really to be in a higher resolution - my enable / disable icon had only a size of 32x32 pixels (and I just put them in all folders) and therefore Android made a big mess somehow, so only three icons did fit on the action bar. I just removed all icons, but the original ones of 32x32 pixels in mdpi. Now Android upscales the 32x32 pixels icons and can display five items in the actionbar. It is kind of strange.


Problem 1 kind of solved:

Looks like this was directly related to problem 2, as soon as I solved problem 2, the delete icon was placed directly on the action bar.

Also with some testing I saw that the text was always there if I added showAsAction=never to the delete icon. I really think it had something to do with problem 2 (the icons really did bad stuff there).


My problems are almost solved.

I think I got a (new) real bug now: the most recently used share action is floating over the overflow icon. When clicking there, the overflow menu still opens, but it looks pretty shitty:

Most recent share icon above overflow icon

How did I fix this?

Well I'm done with messing around with this ****, so I just added the showAsAction=never to the share icon. (And yes I saw this, but I get an exception if I do this, also it's another change of the normal lifecycle...)

Feel free to comment if you a know better solution than I used :>

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top