一直想抽時(shí)間把些較為基礎(chǔ)的控件統(tǒng)一系統(tǒng)化抽取出來形成Demo豺瘤,方便記錄以及解答,以下是項(xiàng)目的效果听诸,如果有欠缺的坐求,歡迎小伙伴評論區(qū)留言,項(xiàng)目的GitHub地址:AndroidMenuDemo
Menu的分類
菜單是Android應(yīng)用中非常重要且常見的組成部分晌梨,主要分為三類:選項(xiàng)菜單
桥嗤、上下文菜單
、彈出菜單
仔蝌。
使用XML定義菜單
對于所有菜單類型泛领,Android提供了標(biāo)準(zhǔn)的XML格式來定義菜單項(xiàng)。定義菜單項(xiàng)方法可以在XML菜單資源中定義菜單及其所有項(xiàng)敛惊,也可通過代碼方式進(jìn)行構(gòu)建渊鞋,推薦前者。定義后瞧挤,可以在Activity或Fragment中擴(kuò)充菜單資源(將其作為Menu對象加載)锡宋。
使用菜單資源是一種很好的做法,原因如下:
- 更易于使用XML可視化菜單結(jié)構(gòu)
- 將菜單內(nèi)容與應(yīng)用的行為代碼分離
- 允許利用應(yīng)用資源框架特恬,為不同的平臺(tái)版本执俩、屏幕尺寸和其他配置創(chuàng)建備用菜單配置。
要定義菜單癌刽,需在項(xiàng)目res/menu/
目錄內(nèi)創(chuàng)建一個(gè)XML文件役首,并使用以下元素構(gòu)建菜單:
<menu>標(biāo)簽
定義Menu
,即菜單項(xiàng)的容器尝丐。<menu>
元素必須是該文件的根節(jié)點(diǎn),并且能夠包含一個(gè)或多個(gè)<item>
和<group>
元素衡奥。
<item>標(biāo)簽
<item>
是菜單項(xiàng)爹袁,用于創(chuàng)建MenuItem
,可能包含嵌套的<menu>
元素矮固,以便創(chuàng)建子菜單呢簸。常見屬性如下:
- android:id:菜單項(xiàng)(MenuItem)的唯一標(biāo)識。
- android:icon:菜單項(xiàng)的圖標(biāo)(可選)乏屯,在溢出菜單中需要顯示圖標(biāo)以及標(biāo)題根时,需要額外代碼中配置。
- android:title:菜單項(xiàng)的標(biāo)題(必選)
- android:titleCondensed:菜單項(xiàng)的短標(biāo)題(可選)辰晕,當(dāng)菜單項(xiàng)標(biāo)題太長時(shí)會(huì)顯示該屬性值
-
android:onClick:方法名稱蛤迎。單擊此菜單項(xiàng)時(shí)要調(diào)用的方法。該方法必須在Activity中聲明為
public
,并將menuItem作為唯一參數(shù)含友,該參數(shù)指示單擊的項(xiàng)替裆。此方法優(yōu)先于OnOptionsItemSelected() 的標(biāo)準(zhǔn)回調(diào)。
public void onGroupItemClick(MenuItem item) {}
警告:如果混淆代碼時(shí)窘问,請確保在混淆規(guī)則中對此屬性方法進(jìn)行排除辆童,因?yàn)榭赡軙?huì)破壞其功能。
-
android:showAsAction:指定菜單項(xiàng)的顯示方式,多個(gè)屬性值之間可以使用
|
隔開惠赫,參數(shù)值有:
有效值 | 描述 |
---|---|
ifRoom | 在空間足夠時(shí)把鉴,菜單項(xiàng)會(huì)顯示在菜單欄中,否則收納入溢出菜單中儿咱。 |
always | 菜單項(xiàng)永遠(yuǎn)不會(huì)被收納到溢出菜單中庭砍,因此在菜單項(xiàng)過多的情況下可能超出菜單欄的顯示范圍。 |
never | 菜單項(xiàng)永遠(yuǎn)只會(huì)出現(xiàn)在溢出菜單中混埠。 |
withText | 無論菜單項(xiàng)是否定義了icon屬性怠缸,都只會(huì)顯示它的標(biāo)題,而不會(huì)顯示圖標(biāo)钳宪。使用這種方式的菜單項(xiàng)默認(rèn)會(huì)被收納入溢出菜單中揭北。 |
collapseActionView | 此選項(xiàng)是在Api14 引入的屬性,搭配android:actionLayout 或者android:actionViewClass 使用,可起到折疊視圖的效果 |
- android:actionLayout:布局資源吏颖,動(dòng)作視圖使用的布局文件搔体。
-
android:actionViewClass:類名,所使用的動(dòng)作視圖的全類名名稱侦高。例如當(dāng)你使用
SearchView
只需要引入android.widget.SearchView
嫉柴。在Api 11
引入的。
警告:如果項(xiàng)目混淆記得添加對應(yīng)的忽略文件奉呛。 -
android:actionProviderClass:操作提供器類名计螺,例如使用
ShareActionProvider
需要引入"android.widget.ShareActionProvider",當(dāng)然也可以自行自定義ActionProvider
的子類,此選項(xiàng)在Api14引入
瞧壮。
警告:如果項(xiàng)目混淆記得添加對應(yīng)的忽略文件登馒。 - android:numericShortcut:數(shù)字快捷鍵。
- android:alphabeticShortcut:字母快捷鍵咆槽。
-
android:alphabeticModifiers:字母快捷鍵的修飾符陈轿,默認(rèn)值為
Control
,有效值:
值 | 描述 |
---|---|
META | 對應(yīng)Meta鍵 |
CTRL | 對應(yīng)Control鍵 |
ALT | 對應(yīng)Alt鍵 |
SHIFT | 對應(yīng)Shift鍵 |
SYM | 對應(yīng)Sym鍵 |
FUNTION | 對應(yīng)Function鍵 |
注意: 可以在屬性中指定多個(gè)關(guān)鍵字秦忿。例如麦射,android:alphabeticModifiers="CTRL|SHIFT",表示要觸發(fā)相應(yīng)的菜單項(xiàng)灯谣,用戶需要同時(shí)按下兩個(gè)Control和Shift鍵以及快捷鍵潜秋。
- android:numericModifiers:數(shù)字快捷鍵的修飾符,用法同上胎许。
- android:checkable:是否可選中
- android:checked:是否選中
- android:visible:是否可見
- android:enabled:是否啟用
- android:menuCategory:定義組優(yōu)先級峻呛,常見值如下:
值 | 描述 |
---|---|
container | 對于屬于容器的項(xiàng)目 |
system | 對于系統(tǒng)提供的項(xiàng)目。 |
secondary | 對于用戶提供的輔助(不經(jīng)常使用)選項(xiàng)的項(xiàng)目辜窑。 |
alternative | 對于對當(dāng)前顯示的數(shù)據(jù)執(zhí)行備用操作的項(xiàng)目钩述。 |
- orderInCategory:組內(nèi)的重要性順序
<group>標(biāo)簽
<group>
是<item>
元素的不可見容器(可選)∧滤椋可以使用它對菜單項(xiàng)進(jìn)行分組牙勘,使一組菜單項(xiàng)共享可用性和可見性等屬性。常見屬性不過多見解所禀,可參考<item>標(biāo)簽
谜悟。
- android:checkableBehavior:為組內(nèi)項(xiàng)目實(shí)現(xiàn)單選或多選的選擇模式。有三種可選值北秽。
值 | 描述 |
---|---|
none | 所有項(xiàng)目均無法選中葡幸,默認(rèn)值 |
single | 組中只有一個(gè)項(xiàng)目可以選中(單選按鈕) |
all | 所有項(xiàng)目均可選中(復(fù)選框) |
XML方面需要注意點(diǎn):當(dāng)使用appcompat library時(shí),菜單資源應(yīng)引用app:namespace方式(showAsAction贺氓、actionViewClass蔚叨、actionProviderClass),而不是android:namespace方式,對應(yīng)引入的資源也需要調(diào)用兼容類辙培;而相應(yīng)如果不使用appcompat library蔑水,即需要使用android:namespace方式調(diào)用,這塊涉及到Android Menu兼容配置扬蕊。
選項(xiàng)菜單
選項(xiàng)菜單(OptionMenu):是應(yīng)用的主菜單項(xiàng)搀别,用于放置對應(yīng)用起全局影響的操作,如搜索/設(shè)置等操作按鈕尾抑。
選項(xiàng)菜單一般需要使用到以下幾個(gè)方法:
public boolean onCreateOptionsMenu(Menu menu): 初始化Activity選項(xiàng)菜單方法歇父,將要設(shè)置的菜單關(guān)聯(lián)到menu中蒂培。在
Fragment
中調(diào)用public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)。public boolean onOptionsItemSelected(MenuItem item): 選項(xiàng)菜單點(diǎn)擊事件的處理方法榜苫。
public void onOptionsMenuClosed(Menu menu): 當(dāng)選項(xiàng)菜單關(guān)閉時(shí)(用戶使用后退/菜單按鈕取消菜單)护戳,都會(huì)調(diào)用此方法。
public boolean onPrepareOptionsMenu(Menu menu):選項(xiàng)菜單顯示前調(diào)用的方法垂睬,一般在此方法調(diào)整菜單媳荒,在高版本顯示icon以及一些邏輯處理。
-
public boolean onMenuOpened(int featureId, Menu menu): 當(dāng)用戶打開溢出菜單選項(xiàng)時(shí)調(diào)用驹饺,當(dāng)菜單從一種類型更改為另一種類型(例如钳枕,從圖標(biāo)菜單更改為拓展菜單),也調(diào)用此方法赏壹。一般用此方法監(jiān)聽拓展菜單是否被打開鱼炒,用于重置一些菜單操作。此方法僅Activity存在卡儒√锶幔‘
圖1
圖2
XML實(shí)現(xiàn)方式
采用XML是實(shí)現(xiàn)菜單主要方式,官方也推薦此種方案骨望,具體如下:
- 實(shí)現(xiàn)option_menu.xml文件
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/menu_search"
android:orderInCategory="1"
android:title="搜索菜單"
android:icon="@android:drawable/ic_menu_search"
app:showAsAction="always|collapseActionView"
app:actionViewClass="android.support.v7.widget.SearchView"/>
<item android:id="@+id/menu_share"
android:title="分享菜單"
android:icon="@android:drawable/ic_menu_share"
android:orderInCategory="1"
app:showAsAction="never"
app:actionProviderClass="android.support.v7.widget.ShareActionProvider"/>
<item android:id="@+id/menu_collect"
android:orderInCategory="1"
android:title="收藏菜單"
android:icon="@android:drawable/btn_star_big_on"
app:actionLayout="@layout/layout_collect"
app:showAsAction="never|collapseActionView"/>
<item android:id="@+id/menu_previous"
android:title="這是上一步的菜單展示效果"
android:titleCondensed="上一步"
android:orderInCategory="1"
app:showAsAction="always"
android:onClick="onPreviousMenu"/>
<item android:id="@+id/menu_next"
android:title="這是下一步的菜單展示效果"
android:titleCondensed="下一步"
android:orderInCategory="1"
app:showAsAction="always"
android:onClick="onNextMenu"/>
<item android:id="@+id/menu_single_check"
android:title="單選按鈕"
android:orderInCategory="1"
app:showAsAction="withText">
<menu>
<group android:id="@+id/group_single"
android:checkableBehavior="single"
android:menuCategory="system">
<item android:id="@+id/single_menu_01"
android:title="單選按鈕01"
android:checked="true"/>
<item android:id="@+id/single_menu_02"
android:title="單選按鈕02"/>
<item android:id="@+id/single_menu_03"
android:title="單選按鈕03"/>
</group>
</menu>
</item>
<item android:id="@+id/menu_all_check"
android:title="多選按鈕"
android:orderInCategory="1"
app:showAsAction="withText">
<menu>
<group android:id="@+id/group_all"
android:checkableBehavior="all"
android:menuCategory="system"
android:enabled="true">
<item
android:id="@+id/all_menu_01"
android:title="多選按鈕01"
android:checked="true"/>
<item
android:id="@+id/all_menu_02"
android:title="多選按鈕02"/>
<item
android:id="@+id/all_menu_03"
android:title="多選按鈕03"/>
</group>
</menu>
</item>
</menu>
- 在Activity或Fragment的
onCreateOptionsMenu
關(guān)聯(lián)Option_menu.xml文件硬爆。
Activity中:
//創(chuàng)建選項(xiàng)菜單
getMenuInflater().inflate(R.menu.option_menu,menu);
Fragment中:
//創(chuàng)建選項(xiàng)菜單
inflater.inflate(R.menu.option_menu,menu);
- 要讓Fragment中的菜單項(xiàng)顯示出來,還需要在Fragment中調(diào)用setHasOptionsMenu(true)方法擎鸠。傳入true作為參數(shù)表明Fragment需要加載菜單項(xiàng)缀磕。建議在Fragment的onCreate方法中調(diào)用這個(gè)方法
/**
* 要讓Fragment中的菜單項(xiàng)顯示出來,還需要在Fragment中調(diào)用setHasOptionsMenu(true)方法劣光。
* 傳入true作為參數(shù)表明Fragment需要加載菜單項(xiàng)袜蚕。
* 建議在Fragment的onCreate方法中調(diào)用這個(gè)方法
* @param savedInstanceState
*/
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
代碼實(shí)現(xiàn)方式
代碼方式也是在onCreateOptionsMenu
掛載添加menu子項(xiàng),主要核心代碼
//添加普通菜單
public MenuItem add(int groupId, int itemId, int order, CharSequence title);
//添加子菜單
SubMenu addSubMenu(final int groupId, final int itemId, int order, final CharSequence title);
參數(shù)說明:
-
groupId
為組id绢涡,一般在主菜單中都是相同的組id牲剃,如果有subMenu的組id可以設(shè)置不同; -
itemId
為菜單項(xiàng)的唯一標(biāo)識雄可,參考xml布局方式中的id,一般用于菜單點(diǎn)擊事件的區(qū)分凿傅; -
order
為序號,主要為組內(nèi)排列順序数苫,相當(dāng)于xml中的orderInCategory
屬性聪舒。 -
title
菜單標(biāo)題。
//通知系統(tǒng)刷新Menu
invalidateOptionsMenu();
上下文菜單
上下文菜單
上下文菜單: 是用戶長按某一元素出現(xiàn)的浮動(dòng)菜單虐急。它提供的操作將影響所選內(nèi)容箱残,主要應(yīng)用于列表中的每一項(xiàng)元素(如長按表項(xiàng)彈出刪除對話框)。
上下文菜單在view實(shí)現(xiàn)方式(這里只貼出核心代碼止吁,具體可以在底部下載gitHub下載完整案例):
- ListView或RecycleView上下文選擇菜單(有兩種方法實(shí)現(xiàn)),單個(gè)View相同
- 方法一:通過registerForContextMenu方法實(shí)現(xiàn)注冊
//在onCreate中對列表進(jìn)行注冊 registerForContextMenu(recyclerView); //在onDestroy中對列表進(jìn)行解綁 unregisterForContextMenu(recyclerView); //重寫方法onCreateContextMenu @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { if(menuInfo instanceof RecyclerViewWithContextMenu.RecyclerViewContextMenuInfo){ RecyclerViewWithContextMenu.RecyclerViewContextMenuInfo contextMenuInfo = (RecyclerViewWithContextMenu.RecyclerViewContextMenuInfo) menuInfo; if(contextMenuInfo != null && contextMenuInfo.getPostion() >= 0){ menu.setHeaderTitle("點(diǎn)擊:"+mAdapter.getItem(contextMenuInfo.getPostion())); getMenuInflater().inflate(R.menu.context_menu,menu); } } super.onCreateContextMenu(menu, v, menuInfo); }
- 方法二:列表對象對onCreateContextMenu實(shí)現(xiàn)
以上是列表對于上下文菜單兩種實(shí)現(xiàn)方式被辑,監(jiān)聽可用過onContextItemSelected方法進(jìn)行監(jiān)聽:recyclerView.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() { @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { if(menuInfo instanceof RecyclerViewWithContextMenu.RecyclerViewContextMenuInfo){ RecyclerViewWithContextMenu.RecyclerViewContextMenuInfo contextMenuInfo = (RecyclerViewWithContextMenu.RecyclerViewContextMenuInfo) menuInfo; if(contextMenuInfo != null && contextMenuInfo.getPostion() >= 0){ menu.setHeaderTitle("點(diǎn)擊:"+mAdapter.getItem(contextMenuInfo.getPostion())); getMenuInflater().inflate(R.menu.context_menu,menu); } } } });
@Override public boolean onContextItemSelected(MenuItem item) { if(item.getMenuInfo() instanceof RecyclerViewWithContextMenu.RecyclerViewContextMenuInfo){ RecyclerViewWithContextMenu.RecyclerViewContextMenuInfo contextMenuInfo = (RecyclerViewWithContextMenu.RecyclerViewContextMenuInfo) item.getMenuInfo(); if(contextMenuInfo != null && contextMenuInfo.getPostion() >= 0){ switch (item.getItemId()){ case R.id.context_menu_add: Toast.makeText(this,mAdapter.getItem(contextMenuInfo.getPostion())+":添加菜單被點(diǎn)擊" , Toast.LENGTH_SHORT).show(); return true; case R.id.context_menu_del: Toast.makeText(this,mAdapter.getItem(contextMenuInfo.getPostion())+":刪除菜單被點(diǎn)擊" , Toast.LENGTH_SHORT).show(); return true; case R.id.context_menu_save: Toast.makeText(this,mAdapter.getItem(contextMenuInfo.getPostion())+":保存菜單被點(diǎn)擊" , Toast.LENGTH_SHORT).show(); return true; } } } return super.onContextItemSelected(item); }
針對于RecycleView列表燎悍,因?yàn)镚oogle只是更加傾向其布局的重用性,提倡視圖自定義敷待,所以這里需要自己實(shí)現(xiàn)對于ContextMenuInfo的實(shí)現(xiàn)间涵,不然RecycleView無法實(shí)現(xiàn)上下文菜單仁热。
public class RecyclerViewWithContextMenu extends RecyclerView { private RecyclerViewContextMenuInfo mContextMenuInfo = new RecyclerViewContextMenuInfo(); public RecyclerViewWithContextMenu(Context context) { super(context); } public RecyclerViewWithContextMenu(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public RecyclerViewWithContextMenu(Context context, @Nullable AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public boolean showContextMenuForChild(View originalView) { getPositionByChild(originalView); return super.showContextMenuForChild(originalView); } @Override public boolean showContextMenuForChild(View originalView, float x, float y) { getPositionByChild(originalView); return super.showContextMenuForChild(originalView, x, y); } /** * 重寫實(shí)現(xiàn)ContextMenuInfo返回榜揖,不然在onCreateContextMenu無法獲取到menuInfo信息 * @return */ @Override protected ContextMenu.ContextMenuInfo getContextMenuInfo() { return mContextMenuInfo; } /** * 記錄當(dāng)前RecycleView中Item上下文菜單的Postion * @param originalView */ private void getPositionByChild(View originalView){ LayoutManager layoutManager = getLayoutManager(); if(layoutManager != null){ int position = layoutManager.getPosition(originalView); mContextMenuInfo.setPostion(position); } } public class RecyclerViewContextMenuInfo implements ContextMenu.ContextMenuInfo{ private int mPostion = -1; public int getPostion() { return mPostion; } public void setPostion(int mPostion) { this.mPostion = mPostion; } } }
上下文選擇模式
上下文操作模式: 將在屏幕頂部欄(菜單欄)顯示影響所選內(nèi)容的操作選項(xiàng),并允許用戶選擇多項(xiàng)抗蠢,一般用于對列表類型的數(shù)據(jù)進(jìn)行批量操作举哟。上下文選擇模式針對不同的列表(ListView、RecycleView)或單視圖實(shí)現(xiàn)方式有些區(qū)別迅矛,具體核心代碼如下所示:
-
ListView實(shí)現(xiàn)上下文選擇模式
ListView上下文選擇模式
//為Listview配置上下文操作模式
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
listView.setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() {
@Override
public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
//當(dāng)列表中的項(xiàng)目選中或取消勾選時(shí)妨猩,這個(gè)方法會(huì)被觸發(fā)
//可以在這個(gè)方法中做一些更新操作,比如更改上下文操作欄的標(biāo)題
//這里顯示已選中的項(xiàng)目數(shù)
mode.setTitle("已選中:"+listView.getCheckedItemCount()+"項(xiàng)");
}
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
mode.getMenuInflater().inflate(R.menu.context_menu,menu);
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
//可以對上下文操作欄做一些更新操作(會(huì)被ActionMode的invalidate方法觸發(fā))
return false;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()){
case R.id.context_menu_add:
StringBuilder sb = new StringBuilder();
for (long id:listView.getCheckedItemIds()) {
sb.append(id);
}
Toast.makeText(ContextMenu2Activity.this, "點(diǎn)擊了添加按鈕"+sb.toString(), Toast.LENGTH_SHORT).show();
//關(guān)閉上下文操作欄
mode.finish();
return true;
case R.id.context_menu_del:
Toast.makeText(ContextMenu2Activity.this, "點(diǎn)擊了刪除按鈕", Toast.LENGTH_SHORT).show();
//關(guān)閉上下文操作欄
mode.finish();
return true;
case R.id.context_menu_save:
Toast.makeText(ContextMenu2Activity.this, "點(diǎn)擊了保存按鈕", Toast.LENGTH_SHORT).show();
//關(guān)閉上下文操作欄
mode.finish();
return true;
default:
return false;
}
}
@Override
public void onDestroyActionMode(ActionMode mode) {
//在上下文操作欄被移除時(shí)會(huì)觸發(fā)秽褒,可以對Activity做一些必要的更新
//默認(rèn)情況下壶硅,此時(shí)所有的選中項(xiàng)將會(huì)被取消選中
}
});
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(ContextMenu2Activity.this, "點(diǎn)擊了菜單", Toast.LENGTH_SHORT).show();
}
});
-
RecycleView實(shí)現(xiàn)上下文選擇模式,在RecycleView中官方并未對齊支持上下文菜單以及上下文操作模式销斟,但官方針對這個(gè)情況庐椒,官方于19年3月提出
RecyclerView Selection
插件庫,試圖解決此問題蚂踊。
RecycleView上下文選擇模式
要將RecyclerView Selection庫添加到Android Studio項(xiàng)目约谈,請implementation 在app 模塊的build.gradle 文件中提及以下依賴項(xiàng):
//recycleView
implementation 'com.android.support:recyclerview-v7:28.0.0'
//recyclerview-selection(如果采用androidx可采用對應(yīng)目錄下的版本)
implementation 'com.android.support:recyclerview-selection:28.0.0'
在Adapter中明確指定指出此適配器的每個(gè)項(xiàng)目將具有類型的唯一穩(wěn)定標(biāo)識符。
//在adapter構(gòu)造函數(shù)中實(shí)現(xiàn)
//明確指出此適配器的每個(gè)項(xiàng)目將具有類型的唯一穩(wěn)定標(biāo)識符非常重要Long犁钟。setHasStableIds(true);
/**
* 為了能夠使用項(xiàng)目的位置作為其唯一標(biāo)識符,需重寫getItemId
* @param position
* @return
*/
@Override
public long getItemId(int position) {
return position;
}
在ViewHolder中實(shí)現(xiàn)可以調(diào)用以唯一標(biāo)識所選列表項(xiàng)的方法棱诱。
public ItemDetailsLookup.ItemDetails getItemDetails(){
return new LongItemDetails(getAdapterPosition(),getItemId());
}
在onBindViewHolder()方法(如果采用BaseRecyclerViewAdapterHelper需要在convert和convertPayloads)實(shí)現(xiàn)調(diào)用此代碼塊。
if(mSelectionTracker != null){
if(mSelectionTracker.isSelected(getItemId(helper.getLayoutPosition()))){
helper.getConvertView().setBackgroundColor(Color.parseColor("#80deea"));
if(helper.tv instanceof CheckedTextView){
((CheckedTextView)helper.tv).setChecked(true);
}
}else {
helper.getConvertView().setBackgroundColor(Color.WHITE);
if(helper.tv instanceof CheckedTextView){
((CheckedTextView)helper.tv).setChecked(false);
}
}
}
并且掛載選擇跟蹤器
public void setSelectionTracker(SelectionTracker mSelectionTracker) {
this.mSelectionTracker = mSelectionTracker;
}
實(shí)現(xiàn)ItemDetailsLookup
這個(gè)類將為選擇庫提供有關(guān)與用戶選擇關(guān)聯(lián)的項(xiàng)目的信息涝动,該選擇基于MotionEvent迈勋,所以我們必須映射到我們的ViewHolders,返回產(chǎn)生MotionEvent事件的item的信息
public class MyItemDetailsLookup extends ItemDetailsLookup<Long> {
private RecyclerView recyclerView;
public MyItemDetailsLookup(RecyclerView recyclerView) {
this.recyclerView = recyclerView;
}
@Nullable
@Override
public ItemDetails<Long> getItemDetails(@NonNull MotionEvent motionEvent) {
View childViewUnder = recyclerView.findChildViewUnder(motionEvent.getX(), motionEvent.getY());
if(childViewUnder != null){
RecyclerView.ViewHolder childViewHolder = recyclerView.getChildViewHolder(childViewUnder);
if(childViewHolder instanceof SelectionQuickAdapter.SelectionQickViewHolder){
return ((SelectionQuickAdapter.SelectionQickViewHolder)childViewHolder).getItemDetails();
}
}
return null;
}
}
在activity中創(chuàng)建選擇跟蹤器以及創(chuàng)建選擇觀察器
//創(chuàng)建選擇跟蹤器
mSelectionTracker = new SelectionTracker.Builder(
"mySelection",
recyclerView,
new StableIdKeyProvider(recyclerView), //密鑰提供者
new MyItemDetailsLookup(recyclerView),
StorageStrategy.createLongStorage())
.withSelectionPredicate(SelectionPredicates.<Long>createSelectAnything())
.build();
mAdapter.setSelectionTracker(mSelectionTracker);
//創(chuàng)建選擇觀察器
mSelectionTracker.addObserver(new SelectionTracker.SelectionObserver() {
@Override
public void onItemStateChanged(@NonNull Object key, boolean selected) {
super.onItemStateChanged(key, selected);
Log.i(TAG, "onItemStateChanged: "+key+" to "+selected);
setSelectionTitle();
}
@Override
public void onSelectionRefresh() {
super.onSelectionRefresh();
}
@Override
public void onSelectionChanged() {
super.onSelectionChanged();
Log.i(TAG, "onSelectionChanged: ");
setSelectionTitle();
}
@Override
public void onSelectionRestored() {
super.onSelectionRestored();
}
});
至此醋粟,RecycleView創(chuàng)建上下文模式核心代碼已完成靡菇,詳情可參考RecyclerView-Selection
-
為單個(gè)View設(shè)置上下文選擇模式
單View上下文選擇模式
為單個(gè)View設(shè)置上下文操作模式同樣可以分為兩步:
- 實(shí)現(xiàn)ActionMode.Callback接口。在這個(gè)接口的回調(diào)方法中昔穴,可以為上下文操作欄加載Menu資源镰官,也可以響應(yīng)操作項(xiàng)目的點(diǎn)擊事件,還可以處理其他需要的操作吗货。
- 當(dāng)需要顯示操作欄時(shí)(例如泳唠,用戶長按視圖),調(diào)用Activity的startActionMode方法宙搬,并傳入前面創(chuàng)建的Callback對象作為參數(shù)笨腥。
btnContextMode.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if(actionMode == null){
actionMode = startSupportActionMode(callback);
v.setSelected(true); //設(shè)置View為選中狀態(tài)
return true;
}
return false;
}
});
private ActionMode.Callback callback = new ActionMode.Callback() {
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
mode.getMenuInflater().inflate(R.menu.context_menu,menu);
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem menuItem) {
switch (menuItem.getItemId()){
case R.id.context_menu_add:
Toast.makeText(ContextMenu4Activity.this, "點(diǎn)擊了添加按鈕", Toast.LENGTH_SHORT).show();
return true;
case R.id.context_menu_del:
Toast.makeText(ContextMenu4Activity.this, "點(diǎn)擊了刪除按鈕", Toast.LENGTH_SHORT).show();
return true;
case R.id.context_menu_save:
Toast.makeText(ContextMenu4Activity.this, "點(diǎn)擊了保存按鈕", Toast.LENGTH_SHORT).show();
return true;
default:
return false;
}
}
@Override
public void onDestroyActionMode(ActionMode mode) {
actionMode = null;
}
};
彈出菜單
彈出菜單: 以垂直列表形式顯示一系列操作選項(xiàng)拓哺,一般由某一控件觸發(fā),彈出菜單將顯示在對應(yīng)控件的上方或下方脖母。它適用于提供與特定內(nèi)容相關(guān)的大量操作士鸥。
主要核心代碼:
private void createPopupMenu(View view){
PopupMenu popupMenu = new PopupMenu(this,view);
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH){
//寫法1:getMenuInflater().inflate(R.menu.context_menu,popupMenu.getMenu());
popupMenu.getMenuInflater().inflate(R.menu.context_menu,popupMenu.getMenu());
}else {
//在 API 級別 14 及更高版本中,您可以將兩行合并在一起谆级,使用 PopupMenu.inflate() 擴(kuò)充菜單烤礁。
popupMenu.inflate(R.menu.context_menu);
}
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
switch (menuItem.getItemId()){
case R.id.context_menu_add:
Toast.makeText(PopupMenuActivity.this, "點(diǎn)擊保存菜單", Toast.LENGTH_SHORT).show();
return true;
case R.id.context_menu_del:
Toast.makeText(PopupMenuActivity.this, "點(diǎn)擊刪除菜單", Toast.LENGTH_SHORT).show();
return true;
case R.id.context_menu_save:
Toast.makeText(PopupMenuActivity.this, "點(diǎn)擊保存菜單", Toast.LENGTH_SHORT).show();
return true;
default:
return false;
}
}
});
popupMenu.show();
}