我们先分析一下,这样一种UI应该由哪些布局组成?首先在原界面上以一小块区域显示界面的这种形式,很明显就是对话框Dialog做的事情吧!最底部是一个取消菜单,上面的功能菜单可以是一个,也可以是两个、三个甚至更多。所以,我们可以使用RecyclerView实现。需要注意一点的是,最上面那个菜单的样式稍微有点不一样,因为它上面是圆滑的,有圆角,这样的界面显示更加和谐。我们主要考虑的就是弹出对话框的动画样式,另外注意一点就是可以多支持几个语种,让框架更加专业,这里只需要翻译“取消”文字。
package dora.widget import android.app.Activity import android.app.Dialog import android.view.Gravity import android.view.LayoutInflater import android.view.View import android.view.WindowManager import android.widget.TextView import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.RecyclerView import com.chad.library.adapter.base.BaseQuickAdapter import com.chad.library.adapter.base.listener.OnItemChildClickListener import dora.widget.bean.BottomMenu import dora.widget.bottomdialog.R class DoraBottomMenuDialog : View.OnClickListener, OnItemChildClickListener { private var bottomDialog: Dialog? = null private var listener: OnMenuClickListener? = null // 堆代码 duidaima.com interface OnMenuClickListener { fun onMenuClick(position: Int, menu: String) } fun setOnMenuClickListener(listener: OnMenuClickListener) : DoraBottomMenuDialog { this.listener = listener return this } fun show(activity: Activity, menus: Array<String>): DoraBottomMenuDialog { if (bottomDialog == null && !activity.isFinishing) { bottomDialog = Dialog(activity, R.style.DoraView_AlertDialog) val contentView = LayoutInflater.from(activity).inflate(R.layout.dview_dialog_content, null) initView(contentView, menus) bottomDialog!!.setContentView(contentView) bottomDialog!!.setCanceledOnTouchOutside(true) bottomDialog!!.setCancelable(true) bottomDialog!!.window!!.setGravity(Gravity.BOTTOM) bottomDialog!!.window!!.setWindowAnimations(R.style.DoraView_BottomDialog_Animation) bottomDialog!!.show() val window = bottomDialog!!.window window!!.decorView.setPadding(0, 0, 0, 0) val lp = window.attributes lp.width = WindowManager.LayoutParams.MATCH_PARENT lp.height = WindowManager.LayoutParams.WRAP_CONTENT window.attributes = lp } else { bottomDialog!!.show() } return this } private fun initView(contentView: View, menus: Array<String>) { val recyclerView = contentView.findViewById<RecyclerView>(R.id.dview_recycler_view) val adapter = MenuAdapter() val list = mutableListOf<BottomMenu>() menus.forEachIndexed { index, s -> when (index) { 0 -> { list.add(BottomMenu(s, BottomMenu.TOP_MENU)) } else -> { list.add(BottomMenu(s, BottomMenu.NORMAL_MENU)) } } } adapter.setList(list) recyclerView.adapter = adapter val decoration = DividerItemDecoration(contentView.context, DividerItemDecoration.VERTICAL) recyclerView.addItemDecoration(decoration) adapter.addChildClickViewIds(R.id.tv_menu) adapter.setOnItemChildClickListener(this) val tvCancel = contentView.findViewById<TextView>(R.id.tv_cancel) tvCancel.setOnClickListener(this) } private fun dismiss() { bottomDialog?.dismiss() bottomDialog = null } override fun onClick(v: View) { when (v.id) { R.id.tv_cancel -> dismiss() } } override fun onItemChildClick(adapter: BaseQuickAdapter<*, *>, view: View, position: Int) { listener?.onMenuClick(position, adapter.getItem(position) as String) dismiss() } }类的结构不仅可以继承,还可以使用聚合和组合的方式,我们这里就不直接继承Dialog了,使用一种更接近代理的一种方式。条条大路通罗马,能抓到老鼠的就是好猫。这里的设计是通过调用show方法,传入一个菜单列表的数组来显示菜单,调用dismiss方法来关闭菜单。最后添加一个菜单点击的事件,把点击item的内容和位置暴露给调用方。
package dora.widget import com.chad.library.adapter.base.BaseMultiItemQuickAdapter import com.chad.library.adapter.base.viewholder.BaseViewHolder import dora.widget.bean.BottomMenu import dora.widget.bottomdialog.R class MenuAdapter : BaseMultiItemQuickAdapter<BottomMenu, BaseViewHolder>() { init { addItemType(BottomMenu.NORMAL_MENU, R.layout.dview_item_menu) addItemType(BottomMenu.TOP_MENU, R.layout.dview_item_menu_top) } override fun convert(holder: BaseViewHolder, item: BottomMenu) { holder.setText(R.id.tv_menu, item.menu) } }多类型的列表布局我们采用BRVAH,
implementation("io.github.cymchad:BaseRecyclerViewAdapterHelper:3.0.10")来区分有圆角和没圆角的item条目。
<?xml version="1.0" encoding="utf-8"?> <resources> <style name="DoraView.AlertDialog" parent="@android:style/Theme.Dialog"> <!-- 是否启用标题栏 --> <item name="android:windowIsFloating">true</item> <item name="android:windowIsTranslucent">true</item> <item name="android:windowNoTitle">true</item> <!-- 是否使用背景半透明 --> <item name="android:windowBackground">@android:color/transparent</item> <item name="android:background">@android:color/transparent</item> <item name="android:backgroundDimEnabled">true</item> </style> <style name="DoraView.BottomDialog.Animation" parent="Animation.AppCompat.Dialog"> <item name="android:windowEnterAnimation">@anim/translate_dialog_in</item> <item name="android:windowExitAnimation">@anim/translate_dialog_out</item> </style> </resources>以上是对话框的样式。我们再来看一下进入和退出对话框的动画。
<?xml version="1.0" encoding="utf-8"?> <translate xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:duration="300" android:fromXDelta="0" android:fromYDelta="100%" android:toXDelta="0" android:toYDelta="0"> </translate>translate_dialog_out.xml
<?xml version="1.0" encoding="utf-8"?> <translate xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:duration="300" android:fromXDelta="0" android:fromYDelta="0" android:toXDelta="0" android:toYDelta="100%"> </translate>最后给你们证明一下我是做了语言国际化的。
// 打开底部弹窗 val dialog = DoraBottomMenuDialog() dialog.setOnMenuClickListener(object : DoraBottomMenuDialog.OnMenuClickListener { override fun onMenuClick(position: Int, menu: String) { val intent = Intent(Intent.ACTION_VIEW) intent.data = Uri.parse(url) startActivity(intent) } }) dialog.show(this, arrayOf("外部浏览器打开"))