最近在写安卓APP时需要用到弹出式菜单,系统内置的弹出式菜单有些不和谐,原本的导航菜单都有图标和复选按钮,但是变成弹出式菜单后都没有了,网上找了一个部分解决图标问题,但是这仅能解决图标问题,复选按钮,分割条这些都美没有的,看起来一堆,不爽,后来参考了一些自定义弹出式菜单,自己写了一个弹出式菜单类,个人感觉用起来还不错,这里将源码分享出来。主要用了弹窗类的实现吗,共两个类文件。完整代码如下:
MenuItem.java文件
package com.xmks.Intelligent.lighting.menu; //这里的包名称根据自己的包进行修改
import android.graphics.drawable.Drawable;
/***菜单项目实体*/
public class MenuItem {
private String itemKey;
/**菜单标题*/
private String itemTitle=null;
/**菜单图标*/
private Drawable itemIcon=null;
/**分隔线*/
private boolean splitLine=false;
/**有switch视图*/
private boolean switchItem=false;
/**switch状态*/
private boolean switchChecked=false;
/**项目状态*/
private boolean selected=false;
/**项目可视状态*/
private boolean visibility=true;
/**启用*/
private boolean enabled=true;
public interface OnMenuItemChange{
/**
* 项目属性改变时触发
* @param item 发送改变的菜单项
* @param attributeName 改变的属性名称
* @param value 对应改变的值
*/
void onChange(MenuItem item,String attributeName,Object value);
}
/**菜单项改变事件侦听接口*/
private OnMenuItemChange onMenuItemChange=null;
/**
* 构建一个仅有关键字的实体
* @param key 关键字
*/
public MenuItem(String key){
this.itemKey=key;
}
/**
* 构建一个仅有分隔线的实体
* @param key 关键字
* @param splitLine 是否分割线
*/
public MenuItem(String key,boolean splitLine){
this.itemKey=key;
this.splitLine=splitLine;
this.selected=false;
}
/**
* 构建含标题的实体
* @param key 关键字
* @param title
*/
public MenuItem(String key,String title){
this(key,title,null);
}
/**
* 构建一个包含标题及图标的实体
* @param key 关键字
* @param title 标题
* @param icon 图标
*/
public MenuItem(String key,String title,Drawable icon){
this(key,title,icon,false);
}
/**
* 构建一个包含标题,图标及切换视图的实体
* @param key 关键字
* @param title 标题
* @param icon 图标
* @param switchItem 含有切换视图?
*/
public MenuItem(String key,String title,Drawable icon,boolean switchItem){
this(key,title,icon,switchItem,false);
}
/**
* 构建一个包含标题,图标及切换视图的实体
* @param key 关键字
* @param title 标题
* @param icon 图标
* @param switchItem 含有切换视图?
* @param selected 选择状态
*/
public MenuItem(String key,String title,Drawable icon,boolean switchItem,boolean selected){
this.itemKey=key;
this.itemTitle=title;
this.itemIcon=icon;
this.switchItem=switchItem;
this.selected=selected;
}
/**
* 设置项目标题
* @param itemTitle
*/
public void setItemTitle(String itemTitle) {
if(this.isSplitLine()) {
this.itemTitle = null;
}else{
if(this.itemTitle==null||this.itemTitle.length()==0) {
if(itemTitle!=null && itemTitle.length()>0) {
this.itemTitle = itemTitle;
if (onMenuItemChange != null) {
onMenuItemChange.onChange(this, "itemTitle", itemTitle);
}
}
}else if(this.itemTitle!=null||this.itemTitle.length()>0){
if(!this.itemTitle.equals(itemTitle)){
this.itemTitle = itemTitle;
if (onMenuItemChange != null) {
onMenuItemChange.onChange(this, "itemTitle", itemTitle);
}
}
}
}
}
/**获取项目标题*/
public String getItemTitle() {
return itemTitle;
}
/**
* 设置项目图标
* @param itemIcon
*/
public void setItemIcon(Drawable itemIcon) {
if(this.isSplitLine()){
this.itemIcon = null;
}else {
if(this.itemIcon==null && itemIcon!=null){
this.itemIcon = itemIcon;
if (onMenuItemChange != null) {
onMenuItemChange.onChange(this, "itemIcon", itemIcon);
}
}else if(this.itemIcon!=null && !this.itemIcon.equals(itemIcon)){
this.itemIcon = itemIcon;
if (onMenuItemChange != null) {
onMenuItemChange.onChange(this, "itemIcon", itemIcon);
}
}
}
}
/**
* 获取项目图标
* @return
*/
public Drawable getItemIcon() {
return itemIcon;
}
/**
* 设置项目包含Switch
* @param switchItem
*/
public void setSwitchItem(boolean switchItem) {
if(this.isSplitLine()){
this.switchItem =false; //项目为分割线是禁止拥有Switch
}else {
if( this.switchItem != switchItem) {
this.switchItem = switchItem;
if (onMenuItemChange != null) {
onMenuItemChange.onChange(this, "switchItem", switchItem);
}
}
}
}
/**项目包含witch*/
public boolean isSwitchItem() {
return switchItem;
}
/**
* Switch选择状态
* @param switchChecked
*/
public void setSwitchChecked(boolean switchChecked) {
if(!this.isSwitchItem()){
this.switchChecked =false;
}else {
if(this.switchChecked != switchChecked) {
this.switchChecked = switchChecked;
// if (onMenuItemChange != null) {
// onMenuItemChange.onChange(this, "switchChecked", switchChecked);
// }
}
}
}
/**witch选择状态*/
public boolean isSwitchChecked() {
return switchChecked;
}
/**
* 设置项目选择状态
* @param selected
*/
public void setSelected(boolean selected) {
if(this.isSplitLine()){
this.selected = false; //项目为分割线是禁止选择
}else{
if(this.selected!=selected) {
this.selected = selected;
if (onMenuItemChange != null) {
onMenuItemChange.onChange(this, "selected", selected);
}
}
}
}
/**项目选择状态*/
public boolean isSelected() {
return selected;
}
/**项目是分隔线*/
public boolean isSplitLine() {
return splitLine;
}
public void setItemKey(String key) {
this.itemKey = key;
}
public String getItemKey() {
return itemKey;
}
public void setVisibility(boolean visibility) {
if(this.visibility!=visibility) {
this.visibility = visibility;
if (onMenuItemChange != null) {
onMenuItemChange.onChange(this, "visibility", visibility);
}
}
}
public boolean isVisibility() {
return visibility;
}
public void setEnabled(boolean enabled) {
if(this.isSplitLine()) {
this.enabled=true;
}else{
if (this.enabled != enabled) {
this.enabled = enabled;
if (onMenuItemChange != null) {
onMenuItemChange.onChange(this, "enabled", enabled);
}
}
}
}
public boolean isEnabled() {
return enabled;
}
/**
* 设置菜单改变侦听接口
* @param onMenuItemChange
*/
public void setOnMenuItemChange(OnMenuItemChange onMenuItemChange) {
this.onMenuItemChange = onMenuItemChange;
}
}
DropMenu.java
package com.xmks.Intelligent.lighting.menu;//这里的包名称根据自己的包进行修改
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.text.TextPaint;
import android.text.TextUtils;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.Space;
import android.widget.Switch;
import android.widget.TextView;
import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.annotation.StringRes;
import static android.widget.ListPopupWindow.MATCH_PARENT;
import static android.widget.ListPopupWindow.WRAP_CONTENT;
/**
* 弹出菜单类
*/
public class DropMenu {
private final static String TAG="DropMenu";
/**弹出式窗体对象*/
private PopupWindow mPopupWindow=null;
/**弹出式窗口锚定视图*/
private View mAnchor=null;
/**对齐方式*/
private int mGravity=Gravity.NO_GRAVITY;
/**弹出式串口相对锚定视图偏移量*/
private int xOffset=0,yOffset=0;
/**装载菜单项的容器*/
private LinearLayout mContainerLayout =null;
private Context mContext;
/**菜单宽度*/
private float menuWidth;
/**菜单高度*/
private float menuHeight;
/**菜单项目偏移量*/
private float offset;
private float iconSize;
private float spaceWidth;
private float itemHeight;
public interface OnMenuItemClickListener{
/**
* 菜单项单击事件
* @param item 单击的菜单项
* @param view 单击的视图
*/
void onMenuItemClick(MenuItem item,View view);
}
public interface OnSwitchCheckedChangeListener{
/**
* 当复合按钮的选中状态更改时调用。
* @param item 复选按钮所在的菜单项
* @param buttonView 状态已更改的复合按钮视图。
* @param isChecked 按钮视图的新选中状态。
*/
void onCheckedChanged(MenuItem item,CompoundButton buttonView, boolean isChecked);
}
/**当用户从菜单中选择项目时将收到通知的侦听器。*/
private OnMenuItemClickListener menuItemClickListener=null;
/**当用户改变复选按钮状态时将收到的通知的侦听器*/
private OnSwitchCheckedChangeListener switchCheckedChangeListener=null;
/**
* 构造函数创建带有定位视图的新弹出菜单。
* @param context 弹出菜单运行在上下文中,通过它可以访问当前主题、资源等。
* @param anchor 此弹出窗口的锚定视图。如果有空间,弹出窗口将显示在锚点下方,如果没有空间,弹出窗口将显示在锚点上方。
*/
public DropMenu(@NonNull Context context, @NonNull View anchor){
this(context,anchor,Gravity.NO_GRAVITY);
}
/**
* 造函数创建带有定位视图的新弹出菜单。
* @param context 弹出菜单运行在上下文中,通过它可以访问当前主题、资源等。
* @param anchor 此弹出窗口的锚定视图。
* @param gravity 弹出窗口相对于锚定的对齐
*/
public DropMenu(@NonNull Context context, @NonNull View anchor,int gravity){
this(context,anchor,0,0,gravity);
}
/**
* 造函数创建带有定位视图的新弹出菜单。
* @param context 弹出菜单运行在上下文中,通过它可以访问当前主题、资源等。
* @param anchor 此弹出窗口的锚定视图。
* @param xoff 相对于定位点的水平偏移(以像素为单位)
* @param yoff 相对于定位点的垂直偏移(以像素为单位)
*/
public DropMenu(@NonNull Context context, @NonNull View anchor,int xoff,int yoff){
this(context,anchor,0,0,Gravity.NO_GRAVITY);
}
/**
* 造函数创建带有定位视图的新弹出菜单。
* @param context 弹出菜单运行在上下文中,通过它可以访问当前主题、资源等。
* @param anchor 此弹出窗口的锚定视图。
* @param xoff 相对于定位点的水平偏移(以像素为单位)
* @param yoff 相对于定位点的垂直偏移(以像素为单位)
* @param gravity 弹出窗口相对于锚定的对齐
*/
public DropMenu(@NonNull Context