android自定义长按菜单,安卓自定义AppCompat支持库:让菜单项支持长按、按情景动态变化。...

本文介绍了如何在Android开发中自定义AppCompat支持库,通过Gradle排除其他模块并引入本地子模块。同时,文章探讨了如何改进下拉菜单,包括实现长按事件和高效处理动态情景模式下菜单的显示与隐藏,提供了具体的技术实现和代码示例。
摘要由CSDN通过智能技术生成

一、简述定制方法

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 newItems) {

if(mItems!=newItems) {

mItems=newItems;

onItemsChanged(true);

}

}

§ 2.2.2 Activity 中使用

首先,规则化 menu.xml 的格式:

初始化:

toolbar.inflateMenu(R.menu.menu);

Menu AllMenus = toolbar.getMenu();

List MenuInContext1 = MapNumberToMenu(0, 2, 3, 9, 11, 12);

List MenuInContext2 = MapNumberToMenu(0, 1, 2, 3, 9, 10, 12);

public List 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);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值