心水纯纯写作很久的底部对话框样式,同时在Google Play Store 也见到过此样式,不过经过多次问询,没得到想要的结果。只好自己动手实现。
使用BottomSheet,支持滚动布局,同时底部布局不会因BottomSheet未显示全部内容而隐藏。
![]() | ![]() | ![]() |
|---|
高度自定义
支持自定义头部布局(Toolbar...)、内容布局(列表、文字)和底部布局(按钮、BottomAppBar)
底部布局自适应导航栏
只在类原生机器测试过,不保证支持国产定制系统
支持Activity形式的Dialog(BottomDialogActivity)
有Context即可显示的对话框
列表可操作list进行更新View
支持监听List
BottomDialog.builder(this) {
title("Hello")
message(
buildString {
for (i in0..30) {
for (j in0..i *5) append(j)
appendln()
}
}, true
)
oneButton("OK", autoDismiss =true) {
//长按,更新内容布局
onLongClick { dialog ->
dialog.updateContent<MessageContentBuilder> {
text =Random().nextDouble().toString()
}
}
}
}val list =ObservableList.build<String?> {
for (i in0..50) add("item $i")
add("到底了")
}
BottomDialog.builder(this) {
this.title("Hello")
mutableList(list) { _, position, s, l ->
toast("clicked $s at $position longClick: $l")
}
buttons {
negativeButton()
neutralButton("removeAt(0)") {
if (list.isNotEmpty()) list.removeAt(0)
}
positiveButton("add(0, '...')") {
list.add(0, "...")
}
}
}加载应用列表,AppListBuilder 见下文自定义布局构造器
BottomDialog.builder(this) {
title("应用列表")
content(AppListBuilder(this@MainActivity) { _, p, i, l ->
toast("$p\n$i\n$l")
})
oneButton("取消")
}实现的更多的ContentBuilder
- 仿一加系统分享对话框效果 [AwesomeHeader]
- 在工程
build.gradle添加
allprojects {
repositories {
//...
maven { url 'https://jitpack.io' }
}
}- 添加依赖
- BottomDialog
dependencies {
implementation 'com.github.Vove7.BottomDialog:bottomdialog:2.2.5'
}- 扩展包(可选)
dependencies {
implementation 'com.github.Vove7.BottomDialog:extension:2.2.5'
}三层布局均可继承ContentBuilder
如 ToolbarHeader,其中title属性被listenToUpdate委托,在修改时,会通知updateContent进行更新布局。
classToolbarHeader(title:CharSequence?) : ContentBuilder() {
/** * 指定更新type = 1*/var title by listenToUpdate(title, this, type =1)
/** * 导航栏图标 type = 2*/var navIconId:Int? by listenToUpdate(null, this, type =2)
/** * 导航图标点击事件 type = 3*/var onIconClick:OnClick? by listenToUpdate(null, this, type =3)
overrideval layoutRes:Int=R.layout.header_toolbar
lateinitvar toolBar:Toolbar/** * 初始化View * @param view View*/overridefuninit(view:View) {
toolBar = view.tool_bar
}
/** * 进行视图更新 * @param type Int listenToUpdate中指定的type,初始化时type值为-1 * 可根据type值来选择更新视图,而不是全部更新 * @param data Any? 传递值*/overridefunupdateContent(type:Int, data:Any?) {
//type 为1 时,属性 title 被修改if (type ==-1|| type ==1) toolBar.title = title
if (type ==-1|| type ==2)
navIconId?.also {
toolBar.setNavigationIcon(it)
} ?: toolBar.setNavigationIcon(null)
if (type ==-1|| type ==3) {
toolBar.setNavigationOnClickListener {
onIconClick?.invoke(dialog)
}
}
}
}此操作可选,目的是为了方便在builder函数中调用。
已扩展的函数有:
//设置标题fun BottomDialogBuilder.title(title:CharSequence?): BottomDialogBuilder//设置内容fun BottomDialogBuilder.message(
text:String, selectable:Boolean = false
): BottomDialogBuilder//简单列表fun BottomDialogBuilder.simpleList(
items:List<String?>, autoDismiss:Boolean = true, onItemClick:OnItemClick<String?>
): BottomDialogBuilder/** * 三个按钮布局 * buttonPositive * buttonNegative * buttonNeutral*/fun BottomDialogBuilder.buttons(block:ButtonsBuilder.() ->Unit): BottomDialogBuilder//........ 更多参考Class: [BottomDialogBuilder]如扩展BottomDialogBuilder一个toolbar函数:
/** * 头部使用Toolbar*/fun BottomDialogBuilder.toolbar(action:ToolbarHeader.() ->Unit): BottomDialogBuilder {
headerBuilder =ToolbarHeader().apply(action)
returnthis
}使用:
BottomDialog.builder(this, show =true) {
toolbar {
title ="Hello"
navIconId =R.mipmap.ic_launcher
onIconClick = {
dialog.dismiss()
}
}
}除了设置扩展函数,还可直接指定header(其他两种布局亦可,content, footer):
BottomDialog.builder(this) {
header(ToolbarHeader()) {
//...
}
}可继承ListAdapterBuilder快速实现。
可指定
layoutManager
泛型
T可实现Typeable区分元素类别,以构建不同样式
如 应用列表内容构造器 AppListBuilder
classAppListBuilder(
context:Context,
autoDismiss:Boolean = true,
privatevalappList:ObservableList<AppInfo> = ObservableList(),
onItemClick:OnItemClick<AppInfo>
) : ListAdapterBuilder<AppInfo>(applist, autoDismiss, onItemClick) {
init {
loading =true//加载视图
thread {
sleep(1500)
loadAppList(context)
}
}
//type 为元素类型,若items 未继承 Typeable: type = 0overrideval itemView: (type:Int) ->Int= { R.layout.item_app_list }
//item 绑定到视图overrideval bindView:BindView<AppInfo> = { view, item ->
view.text_1.text = item.name
view.text_2.text = item.pkg
}
privatefunloadAppList(context:Context) {
val pm = context.packageManager
appList.addAll(ObservableList.build {
pm.getInstalledPackages(0)?.forEach {
add(AppInfo(it.packageName, it.applicationInfo.loadLabel(pm)))
}
})
//停止加载
loading =false
}
}
data classAppInfo(
valpkg:String,
valname:CharSequence
)
使用:
BottomDialog.builder(this) {
title("应用列表")
//指定内容布局Builder
content(AppListBuilder(this@MainActivity) { _, p, i, l ->
toast("$p\n$i\n$l")
})
oneButton("取消")
}目前有两个自定义属性:
?attr/bd_bg_color 背景色
?android:attr/textColorPrimary 文字颜色
在使用自定义主题时,需要指定上面两个属性:
<stylename="BottomDialog.Dark"parent="BottomDialog">
<itemname="bd_bg_color">#212121</item>
<itemname="android:textColorPrimary">#fff</item>
</style>使用主题:
BottomDialog.builder(this) {
themeId =R.style.BottomDialog_Dark//...
}注意自定义的 ContentBuilder 也需使用动态属性:
<androidx.appcompat.widget.Toolbar
android:background="?attr/bd_bg_color"app:titleTextColor="?android:attr/textColorPrimary" />此时比如更新message内容(内容布局类型为MessageContentBuilder)
dialog.updateContent<MessageContentBuilder> {
//text 属性被委托,才可通知布局刷新,见[MessageContentBuilder]
text ="new message"
}当属性被委托后,改变值即可通知ContentBuilder的updateContent(type: Int, data: Any?)
如:
var title by listenToUpdate(title, this, 2)当title值被修改后,会执行updateContent(2)
列表类型为
ObservableList,可监听内容变化,来通知Adapter更新布局对话框底部布局,能够悬浮,由于:其中BottomSheet布局
bs_root与footer_lay同级。
布局文件dialog_content.xml。(已去除不重要属性)
<CoordinatorLayout>
<!--BottomSheet-->
<LinearLayoutandroid:id="@+id/bs_root"app:layout_behavior="android.support.design.widget.BottomSheetBehavior">
<!--头部布局-->
<FrameLayoutandroid:id="@+id/header_container" />
<NestedScrollViewandroid:id="@+id/container">
<!--内容布局-->
<FrameLayoutandroid:id="@+id/content"/>
</NestedScrollView>
</LinearLayout>
<LinearLayoutandroid:id="@+id/footer_lay"android:layout_alignParentBottom="true"android:layout_gravity="bottom" >
<!--底部布局-->
<FrameLayoutandroid:id="@+id/footer_contains" />
<!--用于撑起底部布局于导航栏之上-->
<Viewandroid:id="@+id/fill_nav" />
</LinearLayout>
</CoordinatorLayout>详细内容请参考源码






