RecyclerView的多選模式
在ListView中實現多選並不困難,因為它是自帶多選模式的,主要包括CHOICE_MODE_MULTIPLE
和CHOICE_MODE_MULTIPLE_MODAL
。如果你想要更深入的瞭解這兩者是如何工作的。那麼這篇文章是非常適合你的。
他們之間的區別大概就是CHOICE_MODE_MULTIPLE模式的特點在於他本身沒有排斥性,在能選擇item的情況下,也可以響應普通點選事件。
CHOICE_MODE_MULTIPLE_MODAL和CHOICE_MODE_MULTIPLE恰恰相反,他是對普通點選操作和多選操作是排斥的,一旦有一個item被選中,即進入到多選狀態,item的onclick事件被遮蔽。這種排斥性也是他比CHOICE_MODE_MULTIPLE多了個MODAL的原因
我們也可以利用這個多選模式完成RecyclerView的多選。
使用庫實現選擇模式
首先,匯入庫。將以下行新增到您的build.gradle:
compile 'com.bignerdranch.android:recyclerview-multiselect:+'
接下來,建立一個MultiSelector例項。在我的示例應用程式中,我在我的Fragment新增如下程式碼:
public class SDTxtListActivity extends AppCompatActivity {
RecyclerView recycler;
ArrayList<FileMsgModel> txtFiles;
private MultiSelector multiSelector = new MultiSelector();
...
}
MultiSelector是管理多選的核心物件。但是,您必須告訴MultiSelector何時進入/離開選擇模式。
MultiSelector multiSelector = new MultiSelector();
multiSelector.setSelectable(true); // enter selection mode
MultiSelector multiSelector = new MultiSelector();
multiSelector.setSelectable(false ); // leave selection mode
當選擇某一項時,您還必須將ViewHolder告訴MultiSelector。
MultiSelector multiSelector = new MultiSelector();
multiSelector.setSelectable(true); // enter selection mode
multiSelector.setSelected(myViewHolder, true); // set myViewHolder to selected
SwappingHolder
您的ViewHolder必須實現SelectableHolder介面。該庫提供了一個名為SwappingHolder的預設實現,用於處理被選擇/未選擇的視覺效果。
public class TxtFileHodler extends SwappingHolder implements View.OnClickListener,View.OnLongClickListener{ //(1)
private FileMsgModel dm;
private TextView textView;
public TxtFileHodler(View itemView) {
super(itemView,multiSelector); //(2)
textView = (TextView) itemView.findViewById(R.id.txtName);
itemView.setLongClickable(true);
itemView.setOnClickListener(this);
itemView.setOnLongClickListener(this);
}
public void bindView(FileMsgModel fm){
this.dm = fm;
String txtName = dm.getFileName();
textView.setText(txtName);
}
@Override
public void onClick(View view) {
....
}
@Override
public boolean onLongClick(View view) { //(6)
if(!multiSelector.isSelectable()){ //(3)
multiSelector.setSelectable(true); //(4)
multiSelector.setSelected(TxtFileHodler.this,true); //(5)
return true;
}
return false;
}
}
建立一個子類SwappingHolder(1)的ViewHolder。當選擇/取消選擇時,MultiSelector可以通過SelectableHolder介面與ViewHolder進行通訊。因此,您必須在ViewHolder的建構函式(2)中將MultiSelector傳遞給SwappingHolder。確保MultiSelector尚未處於選擇模式(3),然後進入選擇模式(4)。最後選擇專案(5)。在這個例子中,我們選擇長按(6)進入選擇模式。您可以決定什麼觸發選擇模式。
在選擇模式下單擊專案時,還可以通知MultiSelector。但是,您只需在選擇模式下通知MultiSelector。當不在選擇模式時,您可以正常處理專案點選。
該庫提供了一種名為tapSelection()的方便方法。如果MultiSelector處於選擇模式,則此方法將切換專案的選定狀態並返回true。如果MultiSelector未處於選擇模式,則此方法返回false。
public class TxtFileHodler extends SwappingHolder implements View.OnClickListener,View.OnLongClickListener{
private FileMsgModel dm;
private TextView textView;
public TxtFileHodler(View itemView) {
super(itemView,multiSelector);
textView = (TextView) itemView.findViewById(R.id.txtName);
itemView.setLongClickable(true);
itemView.setOnClickListener(this);
itemView.setOnLongClickListener(this);
}
public void bindView(FileMsgModel fm){
this.dm = fm;
String txtName = dm.getFileName();
textView.setText(txtName);
}
@Override
public void onClick(View view) {
if(!multiSelector.tapSelection(TxtFileHodler.this)){
selectTxt(dm);
}
}
....
}
模擬多選模式
要獲得CHOICE_MODE_MULTIPLE_MODAL同樣的效果,您可以實現ActionMode.Callback。
但是,該庫提供了一個名為ModalMultiSelectorCallback的抽象類。
private ModalMultiSelectorCallback mActionModeCallback
= new ModalMultiSelectorCallback(mMultiSelector) { // (1)
...
};
private class TxtFileHodler extends SwappingHolder
implements View.OnClickListener, View.OnLongClickListener {
...
@Override
public boolean onLongClick(View view) {
if (!mMultiSelector.isSelectable()) {
((AppCompatActivity) getActivity()).startSupportActionMode(mActionModeCallback); // (2)
mMultiSelector.setSelectable(true);
mMultiSelector.setSelected(MyViewHolder.this, true);
return true;
}
return false;
}
}
首先建立一個ModalMultiSelectorCallback的例項,傳入你的MultiSelector(1)。然後在啟動ActionMode(2)時將此回撥傳遞到系統中。
ModalMultiSelectorCallback公開ActionMode.Callback介面中通常用於建立/準備/響應/銷燬ActionMode選單項的所有方法:
public boolean onCreateActionMode(ActionMode mode, Menu menu);
public boolean onPrepareActionMode(ActionMode mode, Menu menu);
public boolean onActionItemClicked(ActionMode mode, MenuItem item);
public void onDestroyActionMode(ActionMode mode);
對於這個例子,我們使用想要在選擇模式中這樣做:
private ModalMultiSelectorCallback mActionModeCallback = new ModalMultiSelectorCallback(multiSelector) {
/**
* actionmode的選單處理
* 進入多選模式後,ActionBar/ToolBar上會彈出一個選單,這個選單需要我們自定義
*
*/
@Override
public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
MenuInflater inflater = actionMode.getMenuInflater();
inflater.inflate(R.menu.sd_card_txt_menu,menu);
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
return false;
}
//檢查是否選中了某一項
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()){
case R.id.action_delete:
mode.finish();
List<FileMsgModel> files = new ArrayList<>();
for(int i = txtFiles.size()-1;i>=0;i--){
if(multiSelector.isSelected(i,0)){ // (1)
FileMsgModel fileModel = txtFiles.get(i);
files.add(fileModel);
}
}
Intent intent=new Intent();
intent.putExtra("MESSAGE", (Serializable) files);
setResult(2,intent);
finish();
multiSelector.clearSelections(); // (2)
return true;
default:
break;
}
return false;
}
/**
* 關閉多選模式
*/
@Override
public void onDestroyActionMode(ActionMode actionMode) {
new TxtFileAdapter(txtFiles).clearSelections();
}
};
private class TxtFileAdapter extends RecyclerView.Adapter<TxtFileHodler> {
private ArrayList<FileMsgModel> files;
public TxtFileAdapter(ArrayList<FileMsgModel> txtFiles) {
this.files = txtFiles;
}
@Override
public TxtFileHodler onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(SDTxtListActivity.this);
View view = inflater.inflate(R.layout.item,parent,false);
return new TxtFileHodler(view);
}
@Override
public void onBindViewHolder(TxtFileHodler holder, int position) {
FileMsgModel txt = files.get(position);
//我們可以用這行程式碼替換選中後的背景
holder.setSelectionModeBackgroundDrawable(getResources().getDrawable(R.drawable.sd_txt_back_drawable));
holder.bindView(txt);
}
@Override
public int getItemCount() {
return files.size();
}
//關閉Model
public void clearSelections() {
multiSelector.setSelectable(false);
notifyDataSetChanged();
}
}
我們可以訪問每個選定的專案(1)並對其做處理。完成對所有專案的操作後,還需要清除選擇(2)。