一、简述定制方法
AppCompat 早在v7和eclipse的时代就可以自定义魔改。那时要改支持库非常简单,因为传统的eclipse项目不会自动引入依赖,一切要靠开发者架好,一开始固然麻烦,后面就简单多了。
现在支持库更新为AndroidX模块,但仍然可以定制,方法是利用Gradle脚本的排除语句,阻止其他AndroidX模块自动引入AppCompat支持库,取而代之的可以是本地项目里的一个子模块。(还没学会如何把library模块上传github)
比如,build.gradle里可以这样写:
if (use_compat_official == '1') { //如果使用原版支持库
api(libs_compat)
} else { //使用定制版支持库
implementation project(':AppCompat')
implementation(libs_appres){
exclude module:"core"
}
configurations { //这个写法还没试对
//all*.exclude group: 'androidx.appcompat'
}
}
if (use_mat_official == '1') { //原版
api(libs_mat) { //利用排除语句排除自带的appcompat依赖
exclude module: "appcompat"
exclude module: "appcompat-resources"
}
} else { //定制版
implementation project(':Designer')
}
上面引用了定义带properties.gradle中的一些属性:
use_mat_official=
use_compat_official=
libs_compat=androidx.appcompat:appcompat:1.1.0-rc01
libs_mat=com.google.android.material:material:1.1.0-alpha09
这样写,比分开包名和版本号好得多,不必单为版本号新建脚本和变量。
二、改进下拉菜单(Toolbar Overflow Menu)
2.1 支持长按
传统的处理方式是多级菜单,抑或将多而杂的功能选项放在设置里面,或者根本就没有。
类似于MXPlayer的多级菜单用起来很麻烦,尤其是两级都需要滚动的时候,你脑海里的“字符串定位匹配API”要被调用两次,中间还要等待菜单动画结束。
放设置里就更不用说了,而且很多还都是多级设置,比如via浏览器开关JS(“启用Javascript”)的功能,处于设置->高级设置的页面下。手指点快了还会打开两个高级设置。
支持长按,其实要加这一功能还算挺方便的,关键以下两点:
-
控制Toolbar Overflow Menu显示的MenuPopup类会有两种实现被调用:CascadingMenuPopup、StandardMenuPopup。
-
菜单项MenuItem的实现是MenuItemImpl类。
§ 2.1.1 修改支持库中的类(androidx.appcompat.view.menu)
- 让抽象类 MenuPopup 实现列表项长按接口:
abstract class MenuPopup implements ShowableListMenu, MenuPresenter,
AdapterView.OnItemClickListener
,/*and....*/ AdapterView.OnItemLongClickListener {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
ListAdapter outerAdapter = (ListAdapter) parent.getAdapter();
MenuAdapter wrappedAdapter = toMenuAdapter(outerAdapter);
boolean ret = wrappedAdapter.mAdapterMenu.dispatchMenuItemLongClicked(wrappedAdapter.mAdapterMenu, (MenuItem) outerAdapter.getItem(position));
return ret;
}
- 在CascadingMenuPopup、StandardMenuPopup两个实现创建MenuPopupWindow之时记得调用
popupWindow.setOnItemLongClickListener(this)
,设置列表的长按监听器。 - 菜单总类,SupportMenu 的实现为 MenuBuilder,在其中参考并改写单击事件的分发——
dispatchMenuItemSelected
,然后为其添加长按事件的分发处理:
boolean dispatchMenuItemSelected(MenuBuilder menu, MenuItem item) {
((MenuItemImpl)item).isLongClicked=false;
return mCallback != null && mCallback.onMenuItemSelected(menu, item);
}
public boolean dispatchMenuItemLongClicked(MenuBuilder menu, MenuItem item) {
((MenuItemImpl)item).isLongClicked=true;
return mCallback != null && mCallback.onMenuItemSelected(menu, item);
}
- 为菜单项实现类MenuItemImpl添加
isLongClicked
布尔变量。每次调用onMenuItemSelected前设置好是否是经过长按。
§ 2.1.2 Activity 中使用,复用onMenuItemClick回调以处理长按事件,可以精细控制是否关闭菜单。
toolbar.inflateMenu(R.menu.menu); //照常使用
toolbar.setOnMenuItemClickListener(this);
@Override
public boolean onMenuItemClick(MenuItem item) {
int id = item.getItemId();
MenuItemImpl mmi = item instanceof MenuItemImpl?(MenuItemImpl)item:null;
boolean isLongClicked= mmi!=null && mmi.isLongClicked;
/* 长按事件默认不处理,因此长按时默认返回false,且不关闭menu。 */
boolean ret = !isLongClicked;
boolean closeMenu=ret;
switch(id){
……
}
if(closeMenu)
closeIfNoActionView(mmi);
return ret;
}
void closeIfNoActionView(MenuItemImpl mi) {
if(mi!=null && !mi.isActionButton()) toolbar.getMenu().close();
}
2.2 高效率处理动态情景模式的变化
怎么让工具菜单中的项目动态显示和隐藏,按照情景模式显示不同的菜单项组合呢?
或许有人想到setGroupVisible
,但交叉的菜单项组合怎么处理?而且这个方法是遍历所有项目的,效率并不高。
可以直接设置内部的mItems数组,然后通知更新。
§ 2.2.1 改写 MenuBuilder.java
- 添加:
public void setItems(List<MenuItemImpl> newItems) {
if(mItems!=newItems) {
mItems=newItems;
onItemsChanged(true);
}
}
§ 2.2.2 Activity 中使用
- 首先,规则化 menu.xml 的格式:
<!--0--><item android:id="@+id/toolbar_action1" android:icon="@drawable/ic_btn_siglemode" android:title="" app:showAsAction="always"/>
<!--1--><item android:id="@+id/toolbar_action0" android:icon="@drawable/ic_btn_siglemode" android:title="@string/fold_all" app:showAsAction="never"/>
<!--2--><item android:id="@+id/toolbar_action12" android:icon="@drawable/ic_btn_siglemode" android:title="@string/peruse_mode" app:showAsAction="never"/>
- 初始化:
toolbar.inflateMenu(R.menu.menu);
Menu AllMenus = toolbar.getMenu();
List<MenuItemImpl> MenuInContext1 = MapNumberToMenu(0, 2, 3, 9, 11, 12);
List<MenuItemImpl> MenuInContext2 = MapNumberToMenu(0, 1, 2, 3, 9, 10, 12);
public List<MenuItemImpl> MapNumberToMenu(int...numbers) {
MenuItemImpl[] items = new MenuItemImpl[numbers.length];
for (int i = 0; i < numbers.length; i++) {
items[i] = (MenuItemImpl) AllMenus.getItem(numbers[i]);
}
return Arrays.asList(items);
}
- 使用:
情景模式1:
AllMenus.setItems(MenuInContext1);
情景模式2:
AllMenus.setItems(MenuInContext2);
网友评论