Android — 長按ListView 利用操作功能表(ActionMode) 進行批量事件處理

來源:互聯網
上載者:User

標籤:abs   interrupt   dom   沒有   ati   網路   val   nts   優先順序   

好久沒寫部落格拉```````

近期最終略微閑一點了```````

無聊拿手機清理簡訊。發現批量事件的處理還是挺管用的``````

那麼自己也來山寨一記看看效果吧`````


閑話少說,首先,我們來看下手機內建的簡訊功能裡運行大量刪除時的效果:



然後  是我們自己簡單山寨的效果:

     


類比的操作過程非常easy,但也非常有代表性。

我們假定我們所處的情境為。進入一個存放連絡人清單的介面。


於是,首先我們定義了一個進度框,類比提示正在從網路上下載資料。

接著。當網路資料成功下載到行動裝置上後,將資料繫結顯示到相應的ListView之中。

然後,就是我們這篇部落格提到的:長按該連絡人清單的ListView觸發事件。

彈出使用ActionMode的操作功能表。並讓該ListView中的清單項目支援複現,實現大量操作。

最後。就是當使用者選擇了一定數量的選項後。點擊菜單中的Item進行某項大量操作後,運行相應的操作,並重新整理ListView。


理清了我們想要實現的大致效果,接著我們要做的

就是整理一下思路,然後逐步的去編寫代碼,完畢實現工作。let‘s do it !


首先,我們已經知道了自己 想要以一個連絡人清單作為情境。

那麼,自然我們會須要一個ListView來綁定和存放這些連絡人資料。

於是,我們先將存放ListView以及定義該ListView的Item的細節的布局檔案搞出來,分別為:


context_menu_action_mode.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical" >    <ListView        android:id="@+id/context_menu_listView"        android:layout_width="match_parent"        android:layout_height="match_parent" /></LinearLayout>

context_menu_action_mode_item.xml
<?

xml version="1.0" encoding="utf-8"?

><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" > <ImageView android:id="@+id/user_head" android:layout_width="55dp" android:layout_height="55dp" android:contentDescription="@string/user_head_description" android:src="@drawable/headimage_default" /> <TextView android:id="@+id/user_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:layout_marginStart="20dp" android:layout_toEndOf="@id/user_head" android:layout_toRightOf="@id/user_head" android:textSize="25sp" /> <TextView android:id="@+id/phone_number" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignBottom="@id/user_head" android:layout_marginLeft="20dp" android:layout_marginStart="20dp" android:layout_toEndOf="@id/user_head" android:layout_toRightOf="@id/user_head" /> <CheckBox android:id="@+id/contact_selected_checkbox" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentEnd="true" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:clickable="false" android:focusable="false" /></RelativeLayout>


關於布局的定義。並沒有什麼痛點。

唯一須要注意的是,我們為了更加友好的互動體驗,所以在使用者長按ListView進入可複選的模式後,

在每一個列表的最右側加入顯示了一個CheckBox,以提示使用者是否成功選擇到了想要操作的清單項目。

CheckBox僅僅有在使用者進入複選模式後,才顯示,所以我們須要在後面注意在代碼中動態控制其顯示情況。

而且!更須要注意的是,記得將CheckBox的clickable與focusable兩個屬性的值設定為false!

這樣做的原因是由於CheckBox(定義在作為ListView的Item檔案其中)自身的響應焦點及點擊事件的優先順序高於ListView自身。

所以。假設忘記設定的話,焦點及響應事件將被攔截在CheckBox,無法到達ListView。


第二步,當我們定義好了ListView的相關程式之後,自然忘不了它的好基友:適配器Adapter

MyContactAdapter.java:

package com.example.android_menu_test_demo.adapter;import java.util.ArrayList;import com.example.android_menu_test_demo.R;import com.example.android_menu_test_demo.domain.Contact;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.CheckBox;import android.widget.ImageView;import android.widget.TextView;public class MyContactAdapter extends BaseAdapter {private Context mContext;private ArrayList<Contact> contacts;private ViewHolder mViewHolder;private ArrayList<Contact> selected_contacts = new ArrayList<Contact>();private boolean itemMultiCheckable;public MyContactAdapter(Context mContext, ArrayList<Contact> contacts) {this.mContext = mContext;this.contacts = contacts;}@Overridepublic int getCount() {return contacts.size();}@Overridepublic Object getItem(int position) {return contacts.get(position);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {if (convertView == null) {convertView = LayoutInflater.from(mContext).inflate(R.layout.contact_listview_item, null);mViewHolder = new ViewHolder();mViewHolder.user_head = (ImageView) convertView.findViewById(R.id.user_head);mViewHolder.user_name_text = (TextView) convertView.findViewById(R.id.user_name);mViewHolder.phone_number_text = (TextView) convertView.findViewById(R.id.phone_number);mViewHolder.item_seleted = (CheckBox) convertView.findViewById(R.id.contact_selected_checkbox);convertView.setTag(mViewHolder);} else {mViewHolder = (ViewHolder) convertView.getTag();}// ************對於控制項的詳細處理****************// 設定checkbox是否可見if (itemMultiCheckable) {mViewHolder.item_seleted.setVisibility(View.VISIBLE);// 假設checkbox可見。證明當前處於可多選操作情況下,則依據使用者選擇情況設定checkbox被選中狀態if (selected_contacts.contains(contacts.get(position))) {mViewHolder.item_seleted.setChecked(true);} else {mViewHolder.item_seleted.setChecked(false);}} else {mViewHolder.item_seleted.setVisibility(View.GONE);}// 控制項賦值Contact contact = contacts.get(position);mViewHolder.user_name_text.setText(contact.getUserName());mViewHolder.phone_number_text.setText(contact.getPhoneNumber());return convertView;}public void setItemMultiCheckable(boolean flag) {itemMultiCheckable = flag;}public void addSelectedContact(int position) {selected_contacts.add(contacts.get(position));}public void cancelSeletedContact(int position) {selected_contacts.remove(contacts.get(position));}public void clearSeletedContacts() {selected_contacts = new ArrayList<Contact>();}public void deleteSeletedContacts() {for (Contact contact : selected_contacts) {contacts.remove(contact);}}static class ViewHolder {ImageView user_head;TextView user_name_text, phone_number_text;CheckBox item_seleted;}}

適配器類的定義與我們開發中最常見的定義並沒有太多差別。

值得注意的的代碼,無非就是前面談到的,做好動態控制CheckBox顯示狀態的工作。

另外,我們在適配器的定義中,為了讓listview要顯示的資料,更便於裝載和傳遞。

一般會定義封裝資料的實體類,正如上面的Contact類。只是這個太簡單,就沒貼代碼的必要了。


接下來。就是我們想要實現的功能的重點了,

我們說到希望通過ListView的長點擊事件,來觸發一個操作功能表來進行事件處理。

在Android 3.0之後加入的ActionMode相對於之前的普通操作功能表。

顯然更適合對於批量事件的處理。有著更好的互動體驗。


所以說。既然將要使用到上下文 菜單,那麼。廢話少說。

先定義一個我們須要的簡單的菜單檔案:

multi_acitonmode_menu.xml:

<?xml version="1.0" encoding="utf-8"?

><menu xmlns:android="http://schemas.android.com/apk/res/android" > <item android:id="@+id/menu_cancle" android:showAsAction="always" android:title="@string/item_cancle"/> <item android:id="@+id/menu_delete" android:showAsAction="always" android:title="@string/item_delete"/></menu>


緊接著,一切準備 工作我們都已經基本就緒,

那麼接下來要做的。自然就是Activity的代碼編寫工作了。

ContextMenuActionModeActivity.java

package com.example.android_menu_test_demo;import android.view.ActionMode;import android.view.LayoutInflater;import android.view.Menu;import android.view.MenuItem;import android.view.View;import android.widget.ListView;import android.widget.TextView;import android.widget.AbsListView.MultiChoiceModeListener;import java.util.ArrayList;import com.example.android_menu_test_demo.adapter.MyContactAdapter;import com.example.android_menu_test_demo.domain.Contact;import android.annotation.TargetApi;import android.app.Activity;import android.app.ProgressDialog;import android.os.AsyncTask;import android.os.Build;import android.os.Bundle;@TargetApi(Build.VERSION_CODES.HONEYCOMB)public class ContextMenuActionModeActivity extends Activity {private ListView contact_list_view;private ProgressDialog mDialog;private MyContactAdapter mAdpater;private MultiModeCallback mCallback;// 類比資料private ArrayList<Contact> contacts;private String[] userNames = new String[] { "Jack", "Rose", "Matt", "Adam", "Xtina", "Blake", "Tupac", "Biggie","T.I", "Eminem" };private String[] phoneNumbers = new String[] { "138-0000-0001", "138-0000-0002", "138-0000-0003", "138-0000-0004","138-0000-0005", "138-0000-0006", "138-0000-0007", "138-0000-0008", "138-0000-0009", "138-0000-0010" };@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.context_menu_action_mode);initView();new ContactsDownloadTask().execute();}private void initView() {contact_list_view = (ListView) this.findViewById(R.id.context_menu_listView);mDialog = new ProgressDialog(this);mDialog.setTitle("提示資訊");mDialog.setMessage("下載連絡人清單中...");mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);mCallback = new MultiModeCallback();contact_list_view.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);contact_list_view.setMultiChoiceModeListener(mCallback);}private void downloadContactsFromServer() {if (contacts == null) {contacts = new ArrayList<Contact>();}for (int i = 0; i < userNames.length; i++) {contacts.add(new Contact(userNames[i], phoneNumbers[i]));}}private class ContactsDownloadTask extends AsyncTask<Void, Integer, Void> {private int currentlyProgressValue;@Overrideprotected void onPreExecute() {mDialog.show();super.onPreExecute();}@Overrideprotected Void doInBackground(Void... params) {while (currentlyProgressValue < 100) {publishProgress(++currentlyProgressValue);try {Thread.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}}// download data from serverdownloadContactsFromServer();return null;}@Overrideprotected void onProgressUpdate(Integer... values) {super.onProgressUpdate(values);mDialog.setProgress(values[0]);}@Overrideprotected void onPostExecute(Void result) {super.onPostExecute(result);mAdpater = new MyContactAdapter(ContextMenuActionModeActivity.this, contacts);contact_list_view.setAdapter(mAdpater);mDialog.dismiss();}}private class MultiModeCallback implements MultiChoiceModeListener {private View mMultiSelectActionBarView;private TextView mSelectedCount;@Overridepublic boolean onCreateActionMode(ActionMode mode, Menu menu) {mode.getMenuInflater().inflate(R.menu.multi_acitonmode_menu, menu);mAdpater.setItemMultiCheckable(true);mAdpater.notifyDataSetChanged();if (mMultiSelectActionBarView == null) {mMultiSelectActionBarView = LayoutInflater.from(ContextMenuActionModeActivity.this).inflate(R.layout.list_multi_select_actionbar, null);mSelectedCount = (TextView) mMultiSelectActionBarView.findViewById(R.id.selected_conv_count);}mode.setCustomView(mMultiSelectActionBarView);((TextView) mMultiSelectActionBarView.findViewById(R.id.title)).setText(R.string.select_item);return true;}@Overridepublic boolean onPrepareActionMode(ActionMode mode, Menu menu) {// TODO Auto-generated method stubreturn false;}@Overridepublic boolean onActionItemClicked(ActionMode mode, MenuItem item) {switch (item.getItemId()) {case R.id.menu_cancle:mAdpater.setItemMultiCheckable(false);mAdpater.clearSeletedContacts();mAdpater.notifyDataSetChanged();mode.finish();break;case R.id.menu_delete:mAdpater.deleteSeletedContacts();mAdpater.notifyDataSetChanged();mode.invalidate();mode.finish();break;default:break;}return false;}@Overridepublic void onDestroyActionMode(ActionMode mode) {mAdpater.setItemMultiCheckable(false);mAdpater.clearSeletedContacts();mAdpater.notifyDataSetChanged();}@Overridepublic void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {if (checked) {mAdpater.addSelectedContact(position);} else {mAdpater.cancelSeletedContact(position);}mAdpater.notifyDataSetChanged();updateSeletedCount();mode.invalidate();}public void updateSeletedCount() {mSelectedCount.setText(Integer.toString(contact_list_view.getCheckedItemCount()) + "條");}}}

對於我們這種菜鳥來說,上面activity代碼中值得注意的可能是:

1、基本上,我們首先會定義一個非同步任務類。類比從網路下載資料的過程。有助於Adapter的API的使用的掌握。

2、我們在上面代碼中定義的實現了MultiChoiceModeListener介面的內部類,MultiModeCallback就是協助我們實現長按ListView(也試用於GridView)。而且監聽處理MultiChoice事件的關鍵。

     —  簡單來說,能夠看到。我們在該內部類的回調方法onCreateActionMode中,處理長按ListView後,ActionMode菜單相關的建立工作。而且在此控制ListView中的CheckBox顯示,告知使用者,我們已經進入到了能夠進行大量操作的模式下。

     —  onActionItemClicked方法 用於監聽和響應菜單上相應的選項的點擊事件,你能夠在此依據自己的需求,為相應的菜單選項編寫響應代碼。

     —  onDestroyActionMode方法 用於處理菜單銷毀時,所要啟動並執行動作。

     —  而onItemCheckedStateChanged方法 則就是用於監聽處理ListView中每一個清單項目的選中狀態改變時的回調了。我們會在這裡依據需求完畢相應的編碼工作。

3、到了這裡,我們對於我們想要的功能的實現,能夠說已經是基本搞定了。可是,你可能已經在上面的MultiModeCallback類的某些代碼中注意點到:

為了更加友善的互動感受,我們 還能夠以ActionBar的形式。在菜單條上,加入一段內容。正如 簡訊功能裡所使用的那樣,用以提示使用者類似於“您當前已經選擇了XX條內容”的資訊。所以我們還會定義一個類似ActionBar的布局檔案。例如以下:

list_multi_select_actionbar.xml:

<LinearLayout    xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/custom_title_root"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="horizontal">    <TextView        android:id="@+id/title"        android:layout_gravity="center_vertical"        android:layout_height="wrap_content"        android:layout_width="wrap_content"        android:textColor="#ffffff" />    <TextView        android:id="@+id/selected_conv_count"        android:layout_gravity="center_vertical"        android:layout_width="wrap_content"        android:layout_height="wrap_content"         android:textColor="#ffffff"/></LinearLayout>

在上面的代碼中,你能夠看到,我們相同是在onCreateActionMode方法中完畢ActionMode操作功能表的裝載工作的同一時候,也會進行對於該作為ActionBar使用的View的裝載與顯示控制工作。

在該View裝載和顯示工作完畢之後,我們要做的就非常easy了,僅僅須要在onItemCheckedStateChanged中進行監聽,當使用者選中某個清單項目時,對用於顯示提示資訊的TextView的顯示內容進行更新,則OK了。


走到這一步,我們能夠說是已經山寨完成了,下一步要做的則能夠將Demo編譯到模擬器或者手機上,看看效果了~


Android — 長按ListView 利用操作功能表(ActionMode) 進行批量事件處理

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.