Skip to content

5.x | ActionModeHelper

Davide Steduto edited this page Jul 22, 2016 · 11 revisions

In this page

  • Introduction
  • Configuration & Initialization
  • Simple selection (IDLE)
  • Single selection (SINGLE)
  • Multi selection (MULTI)
  • Handling rotation

Introduction

This Helper is available only from version 5.0.0-beta6. It incredibly simplifies the development of the ActionMode. With this helper it has never been so easy to start and manage the ActionMode.

From Android suggestion I decided to use view Activation state instead of view Selection state as others libraries mistakenly apply:

A view can be activated or not. Note that activation is not the same as selection. Selection is a transient property, representing the view (hierarchy) the user is currently interacting with.
Activation is a longer-term state that the user can move views in and out of. For example, in a list view with single or multiple selection enabled, the views in the current selection set are activated. (Um, yeah, we are deeply sorry about the terminology here.) The activated state is propagated down to children of the view it is set on.

Configuration & Initialization

No XML configuration (New! From beta7) is recommended. You can now call the new DrawableUtils static methods to compose dynamic backgrounds, see wiki page Utils. You don't need XML anymore.

From the Activity, initialize the ActionMode as following:

public class MainActivity extends AppCompatActivity implements
			ActionMode.Callback,
			FlexibleAdapter.OnItemClickListener,
			FlexibleAdapter.OnItemLongClickListener {

	private FlexibleAdapter<IFlexible> mAdapter;
	private ActionModeHelper mActionModeHelper;

	private void initializeActionModeHelper(int mode) {
		//this = ActionMode.Callback instance
		mActionModeHelper = new ActionModeHelper(mAdapter, R.menu.menu_context, this) {
			//Override to customize the title
			@Override
			public void updateContextTitle(int count) {
				//You can use the internal mActionMode instance
				if (mActionMode != null) {
					mActionMode.setTitle(count == 1 ?
							getString(R.string.action_selected_one, count) :
							getString(R.string.action_selected_many, count));
				}
			}
		}.withDefaultMode(mode);
	}
}

The ActionMode.Callback has to continue to be implemented in the Activity:

//Change the status bar color when ActionMode is starting and ending
public boolean onCreateActionMode(ActionMode mode, Menu menu);
public void onDestroyActionMode(ActionMode mode);
//Adjust and handle the Context menu
public boolean onPrepareActionMode(ActionMode mode, Menu menu);
public boolean onActionItemClicked(ActionMode mode, MenuItem item);

Simple selection (IDLE)

Mode IDLE is the default value at the start up. However to switch from another mode to IDLE, write the following statements, change the default mode as well:

//from SINGLE to IDLE
mAdapter.setMode(SelectableAdapter.MODE_IDLE);
mActionModeHelper.withDefaultMode(SelectableAdapter.MODE_IDLE);

Single selection (SINGLE)

When you need to switch mode from IDLE to SINGLE, change the default mode as well:

//from IDLE to SINGLE
mAdapter.setMode(SelectableAdapter.MODE_SINGLE);
mActionModeHelper.withDefaultMode(SelectableAdapter.MODE_SINGLE);

Single selection can work in conjuction with ActionMode for Multi selection. See Multi selection (MULTI) below here to know how they can work together.

Multi selection (MULTI)

In the method mActionModeHelper.onClick() the modes SINGLE and MULTI are handled automatically! 👍

@Override
public boolean onItemClick(int position) {
	//Action on elements are allowed if Mode is IDLE, otherwise selection has priority
	if (mAdapter.getMode() != SelectableAdapter.MODE_IDLE && mActionModeHelper != null) {
		return mActionModeHelper.onClick(position);
	} else {
		//Handle the item click listener
		...
		//We don't need to activate anything
		return false;
	}
}

@Override
public void onItemLongClick(int position) {
	mActionModeHelper.onLongClick(this, position);
}

When it is needed, the following methods are very useful. For example, after items have been deleted OR a restoration is performing OR the button back has been pressed (onBackPressed()).

//To restore the selection after a restoration
if (mAdapter.isRestoreWithSelection()) {
	mActionModeHelper.restoreSelection(this);
}

//Finish the action mode
mActionModeHelper.destroyActionModeIfCan();

//New title for context
mActionModeHelper.updateContextTitle(mAdapter.getSelectedItemCount());

Note: With version 5.x the ViewHolder automatically handles the view activation. Check the wiki page ViewHolders for more details.
Optionally, you can now set View Elevation for the activation, API compatibility is maintained.

/**
 * Allows to set elevation while the view is activated.
 * Override to return desired value of elevation on this itemView.
 *
 * @return never elevate, returns 0dp if not overridden
 */
public float getActivationElevation() {
	return 0f;
}

Note: Also the binding of the selection status (when user scrolls) is automatically handled by the Adapter, you don't have to call any view activation. This is an extract of the pre-implemented onBindViewHolder():

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position, List payloads) {
	//When user scrolls, this line binds the correct selection status
	holder.itemView.setActivated(isSelected(position));
	...
}

Handling rotation

Preserve the current selection state, together with other flags:

@Override
public void onSaveInstanceState(Bundle outState) {
	mAdapter.onSaveInstanceState(outState);
	super.onSaveInstanceState(outState);
}

Restore the previous selection state:

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
	super.onRestoreInstanceState(savedInstanceState);
	//Restore previous state
	if (savedInstanceState != null && mAdapter != null) {
		//Selection
		mAdapter.onRestoreInstanceState(savedInstanceState);
		mActionModeHelper.restoreSelection(this);
	}
}
Clone this wiki locally