-
Notifications
You must be signed in to change notification settings - Fork 224
底部对话框 BottomDialog 和底部菜单 BottomMenu
🌐 View English Document | 繁體中文文檔
底部对话框 BottomDialog 提供从底部弹出显示的对话框样式,可设置标题、提示文本和自定义布局,使用 Material 主题时还会提供向下滑动关闭和向上滑动展开的功能。
底部菜单 BottomMenu 则是底部对话框 BottomDialog 的扩展组件,在底部对话框的基础上额外提供了菜单功能,菜单可设置菜单内容/菜单图标/单选功能,在不同的主题下还可以提供“取消”关闭按钮(注:因 Material 直接可以下滑关闭因此 Material 主题不提供额外的“取消”关闭按钮)
使用以下代码显示一个对话框:
BottomDialog.show("标题", "这里是对话框内容。");
请注意,Material 主题的底部对话框默认是通过下滑操作来进行关闭的,不同主题处理逻辑可能不同,例如在iOS主题中,则是提供“取消”按钮来进行关闭。
在其他主题中你可以通过.setCancelButton(...)
方法来设置取消按钮的文字和事件
BottomDialog.show("标题", "这里是对话框内容。")
.setCancelButton("取消", new OnDialogButtonClickListener<BottomDialog>() {
@Override
public boolean onClick(BottomDialog baseDialog, View v) {
//...
return false;
}
});
底部菜单是基于底部对话框扩展实现的一个快速选择菜单对话框,你可以通过传入List<String>
、String[] menuList
、List<CharSequence> menuList
或者CharSequence[] menuList
来快速显示一个菜单:
BottomMenu.show("新标签页中打开", "稍后阅读", "复制链接网址")
.setMessage("这里是标题")
.setOnMenuItemClickListener(new OnMenuItemClickListener<BottomMenu>() {
@Override
public boolean onClick(BottomMenu dialog, CharSequence text, int index) {
toast(text);
return false;
}
});
上述代码中,OnMenuItemClickListener
回调是菜单的点击事件回调,其中 text 为菜单文本,index 为菜单的索引。
单选菜单也可以轻松设置菜单图标,我们尽可能的省去编写 Adapter 适配器的麻烦,要设置菜单图标可以直接指定,对于某一项菜单不需要图标的可以直接传入 0:
.setIconResIds(R.mipmap.img_dialogx_demo_add, R.mipmap.img_dialogx_demo_edit...)
图标是否根据亮暗色主题进行染色处理可通过以下方法调整:
.setAutoTintIconInLightOrDarkMode(boolean)
底部菜单也提供“已选择”功能,你可以设置一个已选择的 index 索引,那么在 BottomMenu 显示时,相应位置的菜单会被选中,不同主题的选择效果略有不同。
private int selectMenuIndex;
BottomMenu.show("拒绝", "询问", "始终允许", "仅在使用中允许")
.setMessage("这里是权限确认的文本说明,这是一个单选的演示菜单")
.setTitle("获得权限标题")
.setOnMenuItemClickListener(new OnMenuItemClickListener<BottomMenu>() {
@Override
public boolean onClick(BottomMenu dialog, CharSequence text, int index) {
//记录已选择值
selectMenuIndex = index;
toast(text);
return false;
}
})
.setSelection(selectMenuIndex); //指定已选择的位置
同样的,当您在OnMenuItemClickListener
中返回值设置return true
时,点击菜单后不会自动关闭。
或者使用实现一个回调setOnIconChangeCallBack(...)
的方式:
BottomMenu.show("添加", "查看", "编辑", "删除")
.setOnIconChangeCallBack(new OnIconChangeCallBack(true) { //参数代表是否根据亮色/暗色模式对图标染色
@Override
public int getIcon(BottomMenu bottomMenu, int index, String menuText) {
switch (menuText) {
case "添加":
return R.mipmap.img_dialogx_demo_add;
case "查看":
return R.mipmap.img_dialogx_demo_view;
case "编辑":
return R.mipmap.img_dialogx_demo_edit;
case "删除":
return R.mipmap.img_dialogx_demo_delete;
}
return 0; //返回0代表不显示图标
}
})
.setOnMenuItemClickListener(new OnMenuItemClickListener<BottomMenu>() {
@Override
public boolean onClick(BottomMenu dialog, CharSequence text, int index) {
toast(text);
return false;
}
});
上述代码中,OnIconChangeCallBack 存在一个参数(boolean)autoTintIconInLightOrDarkMode
用于识别是否需要将图标进行相应的染色,以适应亮色和暗色模式的变化,设置为 true 时代表,处于亮暗色模式下,菜单图标会自动根据文字颜色进行染色,当使用非多彩图标的线性/面性图标时,建议开启此功能。
在 0.0.50.beta27 版本更新后,你还可以通过网络异步加载菜单图标,此功能需要使用全新的 MenuIconAdapter 来实现,具体方法如下:
.setOnIconChangeCallBack(new MenuIconAdapter<BottomMenu>(false) {
String[] urls = {
"http://www.kongzue.com/test/res/dialogx/ic_menu_add.png",
"http://www.kongzue.com/test/res/dialogx/ic_menu_read_later.png",
"http://www.kongzue.com/test/res/dialogx/ic_menu_link.png"
};
@Override
public boolean applyIcon(BottomMenu dialog, int index, String menuText, ImageView iconImageView) {
Glide.with(MainActivity.this).load(urls[index]).into(iconImageView); //演示通过 Glide 加载网络资源到菜单图标中
return true;
}
});
回调方法 applyIcon 中会暴露菜单图标的 ImageView iconImageView,你可以通过任意异步框架完成图标资源的加载,返回值 true 表示该菜单项的图标需要显示,返回 false 该菜单项的图标将隐藏。
如果你需要选中菜单但不立即关闭对话框,而是保持选中状态,等待用户确认或取消操作再进行处理,那么此时可以通过单选状态回调 OnMenuItemSelectListener#onOneItemSelect
来实现对菜单实时选中状态的监听:
.setOnMenuItemClickListener(new OnMenuItemSelectListener<BottomMenu>() {
@Override
public void onOneItemSelect(BottomMenu dialog, CharSequence text, int index, boolean select) {
//index 即用户点击的菜单项索引
}
})
//开启单选模式,以下方式二选一:
.setSelection(selectMenuIndex) //开启单选模式,并指定一个已选项
.setSingleSelection() //开启单选模式,但不指定已选项
此时,用户点击菜单后对话框不会立即消失,而是等待用户可能再次修改选择。
要在对话框关闭时,或之后再获取用户已选择的菜单项,可以通过 .getSelectionIndex()
方法获取用户当前选中的菜单索引,另可通过 .getMenuList()
获取所有的菜单文本。
要开启多选菜单,你可以通过以下代码实现,使用多选菜单状态回调实时获取选中项:
.setOnMenuItemClickListener(new OnMenuItemSelectListener<BottomMenu>() {
@Override
public void onMultiItemSelect(BottomMenu dialog, CharSequence[] text, int[] index) {
//index[] 数组是所有已选中菜单项的索引集合,text[] 是所有已选中菜单项文本的集合
}
})
//开启多选模式,以下方式二选一:
.setSelection(selectMenuIndexArray); //开启多选模式,并指定一个已选中的索引数组
.setMultiSelection() //开启多选模式,但不指定已选项
此时,用户点击菜单后对话框不会立即消失,而是等待用户可能再次修改或继续其他选择。
要在对话框关闭时,或之后再获取用户已选择的菜单项,可以通过 .getSelectionIndexArray()
方法获取用户当前选中的菜单索引集合,通过 .getSelectTextArray()
获取已选中的菜单文本集合,另可通过 .getMenuList()
获取所有的菜单文本。
BottomDialog 和 BottomMenu 和 MessageDialog 一样,也支持设置 OK、Cancel、Other 按钮的显示,但请注意,部分主题可能不支持额外的 Cancel 和 Other 两个按钮。
要设置按钮和回调,与 MessageDialog 类似,使用以下代码进行设置:
.setOkButton("OK", new OnDialogButtonClickListener<BottomDialog>() {
@Override
public boolean onClick(BottomDialog dialog, View v) {
//...
return false;
}
})
.setCancelButton("Cancel", new OnDialogButtonClickListener<BottomDialog>() {
@Override
public boolean onClick(BottomDialog dialog, View v) {
//...
return false;
}
})
.setOtherButton("Other", new OnDialogButtonClickListener<BottomDialog>() {
@Override
public boolean onClick(BottomDialog dialog, View v) {
//...
return false;
}
})
回调中有一个返回值,若return true
则可以点击后不自动关闭对话框。
另外,DialogX 还提供了多种设置回调和按钮文本的方法:
//只设置按钮文本
.setOkButton("确定")
//只设置按钮点击回调
.setOkButton(new OnDialogButtonClickListener<BottomDialog>() {
@Override
public boolean onClick(BottomDialog dialog, View v) {
toast("点击确定按钮");
return false;
}
});
//设置按钮文本并设置回调
.setOkButton("确定", new OnDialogButtonClickListener<BottomDialog>() {
@Override
public boolean onClick(BottomDialog dialog, View v) {
toast("点击确定按钮");
return false;
}
});
//隐藏按钮
.setOkButton(null)
请依据个人喜好随意使用。
如果你的业务流程中,并不需要对用户操作立即进行处理,但需要知道用户选择了哪个按钮,你也可以通过持有对话框句柄,在后续获取选择的状态,例如:
private BottomDialog dialog;
//业务流程模拟,创建并显示了一个对话框,但不立即处理点击事务
dialog = BottomDialog.show(...);
//需要时可根据 getButtonSelectResult() 方法获取用户点击了哪个按钮选项
BUTTON_SELECT_RESULT result = dialog.getButtonSelectResult();
BUTTON_SELECT_RESULT 是一个枚举,它包含以下类型:
NONE, //未做出选择
BUTTON_OK, //选择了确定按钮
BUTTON_CANCEL, //选择了取消按钮
BUTTON_OTHER //选择了其他按钮
你可以根据它的状态判断用户点击了哪个按钮。
基于这个特性,如果业务流程中针对用户的选择,无论选择哪个选项都有相同部分的代码需要执行,那么开发者也可以在用户选择后,例如在 DialogLifecycle#onDismiss
对话框的关闭事件中对用户的选择进行统一处理,减少重复代码量;
想要监控对话框的生命周期,可以实现其 .setDialogLifecycleCallback(...)
接口,建议使用build()
方法构建对话框:
BottomMenu.build()
.setDialogLifecycleCallback(new DialogLifecycleCallback<BottomMenu>() {
@Override
public void onShow(BottomMenu dialog) {
//对话框启动时回调
}
@Override
public void onDismiss(BottomMenu dialog) {
//对话框关闭时回调
}
})
.show();
BottomDialog/BottomMenu 也支持 Lifecycle,你可以使用 .getLifecycle()
获取 Lifecycle 对象。
对于可滑动的 BottomDialog/BottomMenu(受限于主题),可以使用增强型的 DialogLifecycleCallback 实现 BottomDialogSlideEventLifecycleCallback,它可以提供关于滑动过程以及滑动关闭的扩展事件处理和回调:
.setDialogLifecycleCallback(new BottomDialogSlideEventLifecycleCallback<BottomDialog>() {
@Override
public boolean onSlideClose(BottomDialog dialog) {
log("滑动关闭对话框触发");
return false; //默认返回 false,当返回 true 时表示拦截操作,对话框不会继续关闭;
}
@Override
public boolean onSlideTouchEvent(BottomDialog dialog, View v, MotionEvent event) {
log("#滑动过程触发: action="+ event.getAction() + " y="+event.getY());
return false; //默认返回 false,当返回 true 时表示拦截操作,对话框不会处理滑动流程,可实现触摸事件接管;
}
})
你也可以通过使用 new 构建实例时,override 的生命周期事件的方式来处理生命周期事务,例如:
//复写事件演示
new BottomDialog() {
@Override
public void onShow(BottomDialog dialog) {
//...
tip("onShow");
}
@Override
public void onDismiss(BottomDialog dialog) {
//...
tip("onDismiss");
}
}
你也可以使用方法 .onShow(DialogXRunnable)
和 .onDismiss(DialogXRunnable)
,来处理生命周期事务,例如:
BottomDialog.show(...)
.onShow(new DialogXRunnable<BottomDialog>() {
@Override
public void run(BottomDialog dialog) {
//BottomDialog show!
}
})
.onDismiss(new DialogXRunnable<BottomDialog>() {
@Override
public void run(BottomDialog dialog) {
//BottomDialog dismiss!
}
});
BottomDialog 原生参数支持自定义布局,你可以通过如下代码自定义底部对话框布局:
BottomDialog.show("标题", "这里是对话框内容。\n底部对话框也支持自定义布局扩展使用方式。",
new OnBindView<BottomDialog>(R.layout.layout_custom_view) {
@Override
public void onBind(BottomDialog dialog, View v) {
//v.findViewById...
}
});
BottomMenu 菜单需要使用如下代码加入自定义布局:
BottomMenu.show("新标签页中打开", "稍后阅读", "复制链接网址")
.setMessage("菜单标题")
.setOnMenuItemClickListener(new OnMenuItemClickListener<BottomMenu>() {
@Override
public boolean onClick(BottomMenu dialog, CharSequence text, int index) {
toast(text);
return true;
}
})
.setCustomView(new OnBindView<BottomDialog>(R.layout.layout_custom_view) {
@Override
public void onBind(BottomDialog dialog, View v) {
//v.findViewById...
}
});
回调参数中,v
为您给定的布局文件的实例化组件,您可以通过 v.findViewById(resId)
来实例化其他子布局组件,并在 onBind
方法中设置其功能和事件回调。
使用 ViewBinding 的話也可以更換為 OnBindingView 來實現直接通過 binding 獲取佈局實例:
BottomDialog.show("标题", "这里是对话框内容。\n底部对话框也支持自定义布局扩展使用方式。",
new OnBindingView<BottomDialog, LayoutCustomViewBinding>() {
@Override
public void onBind(BottomDialog dialog, View view, LayoutCustomViewBinding binding) {
//View childView = binding.childView
}
});
//设置菜单项目(无级)
.setMenus("添加", "编辑", "删除", "分享"...);
//设置图标(无级)
.setIconResIds(R.mipmap.img_dialogx_demo_add, R.mipmap.img_dialogx_demo_edit...)
//设置菜单项目(列表或集合)
.setMenuList(list);
//强制重新刷新界面
.refreshUI();
//关闭对话框
.dismiss();
//是否允许点击外部区域或返回键关闭
.setCancelable(boolean);
//设置标题文字样式
.setTitleTextInfo(TextInfo);
//设置消息文字样式
.setMessageTextInfo(TextInfo);
//设置按钮文字样式
.setCancelTextInfo(TextInfo);
//设置返回按键回调
.setOnBackPressedListener(OnBackPressedListener);
//获取对话框实例化对象,您可以通过此方法更深度的定制Dialog的功能
.getDialogImpl()
//获取自定义布局实例
.getCustomView()
//设置背景颜色,强行对对话框背景进行染色,请注意参数为int类型的颜色值而非R.color的索引
.setBackgroundColor(ColorInt);
//设置菜单文本样式
.setMenuTextInfo(TextInfo)
//设置对话框圆角(会裁切内容显示,此设置在 BottomDialog 和 BottomMenu 中只影响左上、右上两个角的圆角)
.setRadius(float px)
//隐藏对话框(无动画),恢复显示请执行非静态方法的 .show()
.hide();
//隐藏对话框(模拟关闭对话框的动画),恢复显示请执行非静态方法的 .show()
.hideWithExitAnim();
//是否允许滑动关闭(仅对 Material 主题有效)
.setAllowInterceptTouch(boolean)
//是否处于显示状态
.isShow()
//置顶对话框
.bringToFront()
//指定对话框显示层级
.setThisOrderIndex(int)
BottomDialog 支持修改背景遮罩,这是为了丰富扩展性。如果需要背景遮罩,您可以自行使用如下代码设置:
bottomDialog.setMaskColor(colorInt);
请注意,传入参数为 ColorInt 值,您可以使用 Color.parseColor("#4D000000")
设置一个 HEX 色值,或使用 getResources().getColor(R.color.black30)
设置一个颜色的资源值。
TextInfo 用于存储基础文本样式设置,其包含一系列属性和响应的 get/set 方法,例如方法解释如下:
属性 | 解释 | 默认值 |
---|---|---|
fontSize | 字号大小,值为-1时使用默认样式,单位:dp | -1 |
gravity | 对齐方式,值为-1时使用默认样式,取值可使用Gravity.CENTER 等对齐方式 |
-1 |
fontColor | 文字颜色,值为1时使用默认样式,取值可以用Color.rgb(r,g,b)等方式获取 | 1 |
bold | 是否粗体 | false |
请注意,fontColor 为 ColorInt 值,您可以使用 Color.parseColor("#4D000000")
设置一个 HEX 色值,或使用资源 getResources().getColor(R.color.black30)
设置一个颜色的资源值,请勿直接传入资源 ID,它可能会无效。
在使用 Material 主题风格且底部对话框,默认是可滑动关闭的,且当 cancelable
值为 true 时,会在对话框顶部显示一个 “滑动提示条”,若依据您的需求需要隐藏其显示,可通过以下方式关闭:
禁用向下滑动对话框以关闭 BottomDialog 可以隐藏滑动提示条。
.setAllowInterceptTouch(false)
如果不想禁用向下滑动关闭对话框的功能,可以借助 DialogX 暴露内部元素的特性,删除 tab 布局。
BottomDialog.show("标题", "这里是对话框内容。\n底部对话框也支持自定义布局扩展使用方式。",
new OnBindView<BottomDialog>(R.layout.layout_custom_view) {
@Override
public void onBind(BottomDialog dialog, View v) {
if (dialog.getDialogImpl().imgTab != null) {
((ViewGroup) dialog.getDialogImpl().imgTab.getParent()).removeView(dialog.getDialogImpl().imgTab);
}
//...
}
});
手动创建一个 layout_dialogx_bottom_material
的布局(暗色对应 layout_dialogx_bottom_material_dark
),内容请参考 layout_dialogx_bottom_material.xml(暗色对应 layout_dialogx_bottom_material_dark.xml)
修改删除其中 <ImageView id="@+id/img_tab">
的布局:
<ImageView
android:id="@+id/img_tab"
android:layout_width="30dp"
android:layout_height="4dp"
android:layout_centerHorizontal="true"
android:layout_marginTop="10dp"
android:src="@drawable/rect_dialogx_material_dialogtap" />
此方案利用的是对资源的重写方式完成修改,请无需担心空指针,DialogX 内部有做对应处理。
若你的 App 引入了多种主题,在特定场景下需要使对话框显示为某种非全局的主体样式,可使用 .build()
构建对话框,然后使用 .setStyle(style)
来指定主题样式,在最后执行 .show()
命令显示对话框,例如:
BottomDialog.build()
//或直接使用 .build(IOSStyle.style())
.setStyle(IOSStyle.style())
.setTitle("Title")
.setMessage("Message content.")
.setOkButton("OK")
.show();