-
Notifications
You must be signed in to change notification settings - Fork 554
5.x | Selection Modes
- Introduction
- Configuration
- Simple selection (IDLE)
- Single selection (SINGLE)
- Multi selection (MULTI)
- Handling rotation
selectAll()
andclearSelection()
In this page all selection is managed without ActionModeHelper
.
Go to the page ActionModeHelper for the new way to handle selection.
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.
You can now call the new DrawableUtils
static methods to compose dynamic backgrounds, see wiki page Utils. You don't need XML anymore.
Create the tap selection with a State Drawable for API < 21 (drawable/selector_item_light.xml):
<?xml version="1.0" encoding="utf-8"?>
<selector android:exitFadeDuration="@android:integer/config_longAnimTime"
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true" android:drawable="@color/list_choice_pressed_bg_light"/>
<item android:state_pressed="true" android:drawable="@color/list_choice_pressed_bg_light"/>
<item android:state_activated="true" android:drawable="@color/list_choice_pressed_bg_light"/>
<item android:drawable="@android:color/transparent"/><!-- The default idle color desired -->
</selector>
And for API >= 21 (drawable-v21/selector_item_light.xml)
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:exitFadeDuration="@android:integer/config_shortAnimTime"
android:color="?android:attr/colorControlHighlight">
<item android:id="@android:id/mask"
android:drawable="@color/list_choice_pressed_bg_light" />
<item>
<selector>
<item android:state_activated="true" android:drawable="@color/list_choice_pressed_bg_light"/>
</selector>
</item>
</ripple>
Override the selectableItemBackground
attribute only in (values/style.xml):
<style name="AppTheme.Base" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- For selection -->
<item name="selectableItemBackground">@drawable/selector_item_light</item>
<item name="primaryTextSelector">@drawable/primary_text_selector_light</item>
</style>
Finally use the referenced style in your Item view (layout/recycler_item.xml):
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeightLarge"
...
android:background="?attr/selectableItemBackground">
...
</RelativeLayout>
ℹ️ Note: By using the above configuration, don't use
?android:attr
prefix or selection will not be visible.
Mode IDLE is the default value at the start up. However to switch from another mode to IDLE, write the following statement:
import eu.davidea.flexibleadapter.SelectableAdapter.Mode;
mAdapter.setMode(Mode.IDLE);
In your Activity/Fragment creation, set the Mode SINGLE.
import eu.davidea.flexibleadapter.SelectableAdapter.Mode;
mAdapter.setMode(Mode.SINGLE);
In onItemClick()
, call toggleSelection()
to register the selection on that position (it won't trigger notifyItemChanged()
) and return true for the itemView activation:
@Override
public boolean onItemClick(int position) {
if (position != mActivatedPosition) setActivatedPosition(position);
return true; //Important!
}
// Optional
// To evaluate if setActivatedPosition can be included in the library
private void setActivatedPosition(int position) {
mActivatedPosition = position;
mAdapter.toggleSelection(position); //Important!
}
That's it!
ℹ️ 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));
...
}
import eu.davidea.flexibleadapter.SelectableAdapter.Mode;
public class MainActivity extends AppCompatActivity implements
ActionMode.Callback,
FlexibleAdapter.OnClickListener, FlexibleAdapter.OnItemLongClickListener {
...
private FlexibleAdapter mAdapter;
private ActionMode mActionMode;
...
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
mode.getMenuInflater().inflate(R.menu.menu_context, menu);
mAdapter.setMode(Mode.MULTI);
return true;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
mAdapter.setMode(Mode.IDLE);
mActionMode = null;
}
// onPrepareActionMode + onActionItemClicked
...
}
@Override
public boolean onItemClick(int position) {
if (mActionMode != null && position != RecyclerView.NO_POSITION) {
// Mark the position selected
toggleSelection(position);
return true;
} else {
// Handle the item click listener
...
// We don't need to activate anything
return false;
}
}
@Override
public void onItemLongClick(int position) {
if (mActionMode == null) {
mActionMode = startSupportActionMode(this);
}
toggleSelection(position);
}
mAdapter.addListener(this);
/**
* Toggle the selection state of an item.
* If the item was the last one in the selection and is unselected, the ActionMode
* is stopped.
*/
private void toggleSelection(int position) {
// Mark the position selected
mAdapter.toggleSelection(position);
int count = mAdapter.getSelectedItemCount();
if (count == 0) {
mActionMode.finish();
} else {
setContextTitle(count);
}
}
private void setContextTitle(int count) {
mActionMode.setTitle(String.valueOf(count) + " " + (count == 1 ?
getString(R.string.action_selected_one) :
getString(R.string.action_selected_many)));
}
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) {
// Selection
mAdapter.onRestoreInstanceState(savedInstanceState);
if (mAdapter.getSelectedItemCount() > 0) {
mActionMode = startSupportActionMode(this);
setContextTitle(mAdapter.getSelectedItemCount());
}
}
}
No more notifyItemRangeChanged()
in selectAll()
and clearSelection()
methods: This means no more item binding!
Now, bound selectable ViewHolders will have the StateListDrawable
background switching status (activated/normal) when invoking internally FlexibleViewHolder.toggleSelection()
, so that, all inner views can complete animations with no interruptions.
ℹ️ Note:
- The cached ViewHolders are removed when they are recycled by the RecyclerView.
- The ViewHolders must extend
FlexibleViewHolder
, otherwise, item binding still occurs.
- Update Data Set
- Selection modes
- Headers and Sections
- Scrollable Headers and Footers
- Expandable items
- Drag&Drop and Swipe
- EndlessScroll / On Load More
- Search Filter
- FastScroller
- Adapter Animations
- Third party Layout Managers
- Payload
- Smooth Layout Managers
- Flexible Item Decoration
- Utils
- ActionModeHelper
- AnimatorHelper
- EmptyViewHelper
- UndoHelper
* = Under revision!