static class ViewHolder { TextView text; TextView timestamp; ImageView icon; ProgressBar progress; int position; }
mSelectionHelper = new SelectionHelper(mHolderTracker); mSelectionHelper.registerSelectionObserver(this);
@Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int position) { LayoutInflater inflater = LayoutInflater.from(viewGroup.getContext()); ImageViewHolder holder = new ImageViewHolder( inflater.inflate(R.layout.gallery_item, viewGroup, false)); return mSelectionHelper.wrapSelectable(holder); }
The wrapSelectable (ViewHolder) method of SelectionHelper: public <H extends RecyclerView.ViewHolder> H wrapSelectable(H holder) { new ViewHolderMultiSelectionWrapper(holder); return holder; }
@Override public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) { Image image = mDataSet.getItem(position); ImageViewHolder imageViewHolder = (ImageViewHolder) viewHolder; imageViewHolder.bindInfo(image); Checkable view = (Checkable) viewHolder.itemView; view.setChecked(mSelectionHelper.isItemSelected(position)); mHolderTracker.bindHolder(imageViewHolder, position); }
private abstract class ViewHolderWrapper implements android.view.View.OnClickListener { protected final WeakReference<RecyclerView.ViewHolder> mWrappedHolderRef; protected ViewHolderWrapper(RecyclerView.ViewHolder holder) { mWrappedHolderRef = new WeakReference<>(holder); } }
public class WeakHolderTracker { private final SparseArray<WeakReference<RecyclerView.ViewHolder>> mHoldersByPosition = new SparseArray<>(); public void bindHolder(RecyclerView.ViewHolder holder, int position) { mHoldersByPosition.put(position, new WeakReference<>(holder)); } @Nullable private RecyclerView.ViewHolder getHolder(int position) { WeakReference<RecyclerView.ViewHolder> holderRef = mHoldersByPosition.get(position); if (holderRef == null) { mHoldersByPosition.remove(position); return null; } RecyclerView.ViewHolder holder = holderRef.get(); if (holder == null || (holder.getAdapterPosition() != position && holder.getAdapterPosition() != RecyclerView.NO_POSITION)) { mHoldersByPosition.remove(position); return null; } return holder; } public List<RecyclerView.ViewHolder> getTrackedHolders() { List<RecyclerView.ViewHolder> holders = new ArrayList<>(); for (int i = 0; i < mHoldersByPosition.size(); i++) { int key = mHoldersByPosition.keyAt(i); RecyclerView.ViewHolder holder = getHolder(key); if (holder != null) { holders.add(holder); } } return holders; } }
Checkable view = (Checkable) viewHolder.itemView; view.setChecked(mSelectionHelper.isItemSelected(position));
<com.bejibx.android.recyclerview.example.ui.widget.CheckableAutofitHeightFrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="80dp" android:layout_height="80dp" android:background="#AAA" android:foreground="@drawable/gallery_item_foreground" tools:ignore="ContentDescription,RtlHardcoded"> <ImageView android:id="@+id/image" android:layout_width="match_parent" android:layout_height="match_parent" /> </com.bejibx.android.recyclerview.example.ui.widget.CheckableAutofitHeightFrameLayout>
public class CheckableAutofitHeightFrameLayout extends FrameLayout implements Checkable { private static final int[] CHECKED_STATE_SET = {android.R.attr.state_checked}; private boolean mIsChecked; private boolean mIsCheckable; public CheckableAutofitHeightFrameLayout(Context context) { super(context); } public CheckableAutofitHeightFrameLayout(Context context, AttributeSet attrs) { super(context, attrs); } public CheckableAutofitHeightFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public CheckableAutofitHeightFrameLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override protected int[] onCreateDrawableState(int extraSpace) { final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); if (isChecked()) { mergeDrawableStates(drawableState, CHECKED_STATE_SET); } return drawableState; } @Override public boolean isCheckable() { return mIsCheckable; } @Override public void setCheckable(boolean isCheckable) { boolean wasCheckable = isCheckable(); mIsCheckable = isCheckable; if (!isCheckable && isChecked()) { setChecked(false); } else if (wasCheckable ^ mIsCheckable) { refreshDrawableState(); } } @Override public void setChecked(boolean isChecked) { boolean wasChecked = isChecked(); mIsChecked = isCheckable() && isChecked; if (wasChecked ^ mIsChecked) { refreshDrawableState(); } } @Override public boolean isChecked() { return mIsChecked; } @Override public void toggle() { setChecked(!mIsChecked); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //noinspection SuspiciousNameCombination super.onMeasure(widthMeasureSpec, widthMeasureSpec); } }
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true"> <shape xmlns:android="http://schemas.android.com/apk/res/android"> <stroke android:color="@color/accent" android:width="1dp" /> </shape> </item> <item android:state_checked="true"> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <stroke android:color="@color/accent" android:width="1dp" /> <solid android:color="@color/accent_alpha" /> </shape> </item> <item android:drawable="@android:color/transparent" /> </selector>
and all the details of the display crawl into the xml-ki, in Java only need to set the appropriate state. @Override public void onSelectableChanged(boolean isSelectable) { if (isSelectable) { mActivity.startActionMode(); } }
public class GalleryActivity extends Activity { private final ActionModeCallback mActionModeCallback = new ActionModeCallback(); private SelectableRecyclerViewAdapter mAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); RecyclerView recyclerView = (RecyclerView) findViewById(R.id.gallery); int columnWidth = getResources().getDimensionPixelSize(R.dimen.column_width); int vSpacing = getResources().getDimensionPixelSize(R.dimen.grid_spacing_vertical); int hSpacing = getResources().getDimensionPixelSize(R.dimen.grid_spacing_horizontal); recyclerView.setLayoutManager(new GridAutofitLayoutManager(this, columnWidth)); recyclerView.addItemDecoration(new GridSimpleSpacingDecoration(hSpacing, vSpacing)); DataSet<Image> dataSet = new DummyImagesDataSet(); mAdapter = new SelectableRecyclerViewAdapter(this, dataSet); recyclerView.setAdapter(mAdapter); } public void startActionMode() { startActionMode(mActionModeCallback); } private class ActionModeCallback implements ActionMode.Callback, SelectionObserver { private ActionMode mActionMode; @Override public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) { return false; } @Override public void onDestroyActionMode(ActionMode actionMode) { SelectionHelper selectionHelper = mAdapter.getSelectionHelper(); selectionHelper.unregisterSelectionObserver(this); mActionMode = null; selectionHelper.setSelectable(false); } @Override public boolean onCreateActionMode(ActionMode actionMode, Menu menu) { mActionMode = actionMode; mActionMode.getMenuInflater().inflate(R.menu.gallery_selection, menu); mAdapter.getSelectionHelper().registerSelectionObserver(this); return true; } @Override public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) { switch (menuItem.getItemId()) { case R.id.menu_toast: Toast.makeText(GalleryActivity.this, R.string.text_simple_toast, Toast.LENGTH_SHORT).show(); break; } return true; } @Override public void onSelectedChanged(RecyclerView.ViewHolder holder, boolean isSelected) { if (mActionMode != null) { int checkedImagesCount = mAdapter.getSelectionHelper().getSelectedItemsCount(); mActionMode.setTitle(String.valueOf(checkedImagesCount)); } } @Override public void onSelectableChanged(boolean isSelectable) { if (!isSelectable) { mActionMode.finish(); } } } }
public class SelectableRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements SelectionObserver { private final SelectionHelper mSelectionHelper; public SelectableRecyclerViewAdapter() { mSelectionHelper = new SelectionHelper(); mSelectionHelper.registerSelectionObserver(this); } public SelectionHelper getSelectionHelper() { return mSelectionHelper; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int position) { LayoutInflater inflater = LayoutInflater.from(viewGroup.getContext()); ImageViewHolder holder = new ImageViewHolder( inflater.inflate(R.layout.gallery_item, viewGroup, false)); return mSelectionHelper.wrapClickable(holder); } @Override public void onHolderClick(RecyclerView.ViewHolder holder) { // perform item click } @Override public boolean onHolderLongClick(RecyclerView.ViewHolder holder) { // perform item long click return false; } //... }
public class GridAutofitLayoutManager extends GridLayoutManager { private int mColumnWidth; private boolean mColumnWidthChanged = true; public GridAutofitLayoutManager(Context context, int columnWidth) { /* Initially set spanCount to 1, will be changed automatically later. */ super(context, 1); setColumnWidth(checkedColumnWidth(context, columnWidth)); } public GridAutofitLayoutManager(Context context, int columnWidth, int orientation, boolean reverseLayout) { /* Initially set spanCount to 1, will be changed automatically later. */ super(context, 1, orientation, reverseLayout); setColumnWidth(checkedColumnWidth(context, columnWidth)); } private int checkedColumnWidth(Context context, int columnWidth) { if (columnWidth <= 0) { context.getResources().getDimensionPixelSize(R.dimen.rv_def_column_width); } return columnWidth; } public void setColumnWidth(int newColumnWidth) { if (newColumnWidth > 0 && newColumnWidth != mColumnWidth) { mColumnWidth = newColumnWidth; mColumnWidthChanged = true; } } /* I don't actually remember why I choose to set span count in onLayoutChildren, I wrote this class some time ago. But the point is we need to do so after view get measured, so we can get its height and width. */ @Override public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { if (mColumnWidthChanged && mColumnWidth > 0) { int totalSpace; if (getOrientation() == VERTICAL) { totalSpace = getWidth() - getPaddingRight() - getPaddingLeft(); } else { totalSpace = getHeight() - getPaddingTop() - getPaddingBottom(); } int spanCount = Math.max(1, totalSpace / mColumnWidth); setSpanCount(spanCount); mColumnWidthChanged = false; } super.onLayoutChildren(recycler, state); } }
public class GalleryActivity extends Activity { private final ActionModeCallback mActionModeCallback = new ActionModeCallback(); private SelectableRecyclerViewAdapter mAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); RecyclerView recyclerView = (RecyclerView) findViewById(R.id.gallery); int columnWidth = getResources().getDimensionPixelSize(R.dimen.column_width); int vSpacing = getResources().getDimensionPixelSize(R.dimen.grid_spacing_vertical); int hSpacing = getResources().getDimensionPixelSize(R.dimen.grid_spacing_horizontal); recyclerView.setLayoutManager(new GridAutofitLayoutManager(this, columnWidth)); recyclerView.addItemDecoration(new GridSimpleSpacingDecoration(hSpacing, vSpacing)); DataSet<Image> dataSet = new DummyImagesDataSet(); mAdapter = new SelectableRecyclerViewAdapter(this, dataSet); recyclerView.setAdapter(mAdapter); } //...
Source: https://habr.com/ru/post/258195/
All Articles