5. 建立上下文動作
5.1 問題
需要為使用者在使用者介面中的選擇提供多個可供執行的動作。
5.2 解決方案
(API Level 11)
對於與單個項相關的上下文動作,使用PopupMenu顯示固定到相關檢視的這些動作,在應該影響多個項的示例中,可啟用ActionMode來響應使用者的動作。
注意:
該例使用AppCompat支援庫來實現最佳的版本相容性。如果應用程式單純地支援較早的平臺版本,可以使用原生的API實現相同的結果。關於在專案中包括支援庫的更多資訊,請參閱 ofollow,noindex">http://developer.android.com/tools/support-library/index.html 。
5.3 實現機制
對於本例,我們將構造如圖所示的Activity。

具有上下文動作

ActionMode的列表檢視
在點選項檢視右側的溢位按鈕時,列表中的每個項通過彈出列表提供上下文動作。長按任何項時,Activity將啟用ActionMode,從而可以將同一個動作應用於多個已選擇的項。
1.上下文彈出選單
PopupMenu用於獲得一個選項選單資源並將其作為小型彈出視窗輕鬆附加到任何檢視。首先,我們需要在res/menu中建立一個XML檔案以定義選單自身;將此檔案稱為contextmenu.xml(參見以下程式碼)。
res/menu/contextmenu.xml
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/menu_delete" android:icon="@android:drawable/ic_menu_delete" android:title="Delete Item" /> <item android:id="@+id/menu_edit" android:icon="@android:drawable/ic_menu_edit" android:title="Edit Item" /> </menu>
我們為駐留在此列表中的每一項將此資源擴充套件到PopupMenu例項中。為更好地封裝此邏輯,以下兩端程式碼為列表行佈局自定義了ContextListItem類。
自定義行項列表
public class ContextListItem extends LinearLayout implements PopupMenu.OnMenuItemClickListener, View.OnClickListener { private PopupMenu mPopupMenu; private TextView mTextView; public ContextListItem(Context context) { super(context); } public ContextListItem(Context context, AttributeSet attrs) { super(context, attrs); } public ContextListItem(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onFinishInflate() { super.onFinishInflate(); mTextView = (TextView) findViewById(R.id.text); //附加單擊處理程式 View contextButton = findViewById(R.id.context); contextButton.setOnClickListener(this); //建立上下文選單 mPopupMenu = new PopupMenu(getContext(), contextButton); mPopupMenu.setOnMenuItemClickListener(this); mPopupMenu.inflate(R.menu.contextmenu); } @Override public void onClick(View v) { //處理上下文按鈕單擊以顯示選單 mPopupMenu.show(); } @Override public boolean onMenuItemClick(MenuItem item) { String itemText = mTextView.getText().toString(); switch (item.getItemId()) { case R.id.menu_edit: Toast.makeText(getContext(), "Edit "+itemText, Toast.LENGTH_SHORT).show(); break; case R.id.menu_delete: Toast.makeText(getContext(), "Delete "+itemText, Toast.LENGTH_SHORT).show(); break; } return true; } }
res/layout/list_item.xml
<?xml version="1.0" encoding="utf-8"?> <com.examples.popupmenus.ContextListItem xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="?android:attr/listPreferredItemHeightSmall" android:paddingLeft="?android:attr/listPreferredItemPaddingLeft" android:paddingRight="?android:attr/listPreferredItemPaddingRight" android:background="?android:attr/activatedBackgroundIndicator" > <TextView android:id="@+id/text" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:textAppearance="?android:attr/textAppearanceListItemSmall" android:layout_gravity="center_vertical" /> <ImageView android:id="@+id/context" style="@style/Widget.AppCompat.Light.ActionButton.Overflow" android:layout_width="?android:attr/listPreferredItemHeightSmall" android:layout_height="match_parent" android:clickable="true" android:focusable="false"/> </com.examples.popupmenus.ContextListItem>
ContextListItem是包含上下文選單的文字項和圖片按鈕的LinearLayout。為實現平臺一致性,我們對按鈕應用Widget.AppCompat.Light.ActionButton.Overflow樣式,使其外觀和行為類似於標準的溢位選單按鈕。建立檢視時,我們構建一個PopupMenu來顯示R.menu.contextmenu,並且關聯該選單,使其在按鈕溢位按鈕時始終會顯示。行檢視也設定為OnMenuItemClickListener,以便處理從彈出選單中選擇適當的選項。
注意:
ListView內的可單擊項需要將android:focusable設定為false,否則就不能夠同時單擊頂層列表項。
為了將此檢視與列表中的資料繫結,我們需要建立引用列表項佈局的基本介面卡:
ArrayAdapter<String> adapter = new ArrayAdapter<String> (this,R.layout.list_item,R.id.text,ITEMS);
我們將很快看到將此功能與其他功能聯絡在一起的完整Activity程式碼,但首先讓我們檢視一下如何實現多選邏輯。
2.ActionMode
ActionMode API解決了類似的問題,即允許使用者對UI中的某個條目執行某種動作,但它的實現方式稍微有點不同。啟用ActionMode後會在視窗的ActionBar上出現一個包含所提供的選單選項的疊層,並出現一個可以退出ActionMode的額外選項。它還允許同時選擇多個條目來執行同一個動作。以下程式碼演示了這一功能。
使用了上下文ActionMode的Activity
public class ActionActivity extends ActionBarActivity implements AbsListView.MultiChoiceModeListener { private static final String[] ITEMS = { "Mom", "Dad", "Brother", "Sister", "Uncle", "Aunt", "Cousin", "Grandfather", "Grandmother"}; private ListView mList; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //為上下文事件註冊一個按鈕 mList = new ListView(this); ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.list_item, R.id.text, ITEMS); mList.setAdapter(adapter); mList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL); mList.setMultiChoiceModeListener(this); setContentView(mList, new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); } @Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { // 如果ActionMode一直是無效的,可以在這裡做些工作來更新選單 return true; } @Override public void onDestroyActionMode(ActionMode mode) { //退出ActionMode時會呼叫這個方法 } @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { MenuInflater inflater = mode.getMenuInflater(); inflater.inflate(R.menu.contextmenu, menu); return true; } @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { SparseBooleanArray items = mList.getCheckedItemPositions(); //通過條目的ID得到使用者選擇的動作 switch(item.getItemId()) { case R.id.menu_delete: //執行刪除動作 break; case R.id.menu_edit: //執行編輯動作 break; default: return false; } return true; } @Override public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) { int count = mList.getCheckedItemCount(); mode.setTitle(String.format("%d Selected", count)); } }
要使用我們的ListView來啟用一個多選擇項的ActionMode,需要將choiceMode屬性設定為CHOICE_MODE_MULTIPLE_MODAL。它和傳統的CHOICE_MODE_MULTIPLE不同,CHOICE_MODE_MULTIPLE是在每個列表條目上都提供一個可選擇的小部件。在ActionMode處於啟用狀態時,它的模式標識只應用這個選擇模式。
對於ActionMode,有很多的回撥方法需要實現它,因為它不像ContextMenu一樣是直接內建在Activity中的。我們需要實現ActionMode.Callback介面來響應建立選單和選擇選項的事件。ListView有一個名為MultiChoiceModeListener的特殊介面,它是ActionMode.Callback的子介面,本例中就實現了這個特殊的介面。
我們在onCreateActionMode()中的響應和onCreateContextMenu()類似,只是構建了疊層中要顯示的選單選項。這個選單不需要包含圖示;這時,ActionMode會顯示條目名。選中每個條目後,我們會在onItemCheckedStateChanged()方法中得到通知。這裡,我們會更新ActionMode的標題來顯示當前選中條目的個數。
當用戶結束選擇並單擊一個選單選項時會呼叫onActionItemClicked()方法。因為會同時選擇多個條目,所以我通過getCheckedItemPositions()得到所有選擇的條目,這樣就可以對所有選擇的條目進行操作。