Android|Android 优雅地管理Dialog弹框

我们应该都知道任何一个app的UI都会遵循一个统一的样式,比如我们的Dialog、Log、Toast等,统一管理好自己的组件库,对自己或后来人都会有很大地帮助的。

记得自己刚开始接手某个项目时,发现这个项目什么规范都没有,命名啥的也不规范,虽然有一些统一管理的工具类,但是写地真是烂,唉,说多了都是泪。。。。

接下来讲解下小球项目里Dialog,我们项目中Dialog居中显示,属性有标题、内容、按钮(1或2个),如我们的布局文件dialog_common_view.xml


用到的依赖
implementation 'com.blankj:utilcodex:1.30.6' implementation 'com.flyco.roundview:FlycoRoundView_Lib:1.1.4@aar'

颜色值
#101a3e #F9FAFB #221715#221715 #221715 #221715 #FFFFFF #F0F0F0#221715 #221715 #221715 #4996F3 #4996F3#80221715 #384996F3

字符值
="dialog_title">温馨提示 ="dialog_permission_allow">允许 ="dialog_permission_refuse">拒绝 ="dialog_permission_title">申请权限 ="dialog_btn_cancel">取消 ="dialog_btn_confirm">确认 ="dialog_btn_setting">设置

以前项目中Dialog这里写一个,那边又写一个,所以自己就重构了整个项目的Dialog,先是定义了个简单基类BaseDialog
package com.littlejerk.permissiondemo; import android.app.Activity; import android.app.Dialog; import android.content.Context; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.view.Window; import android.view.WindowManager; import com.blankj.utilcode.util.ActivityUtils; import androidx.annotation.NonNull; /** * @author : HHotHeart * @date : 2021/9/17 12:46 * @desc : 描述 */ public class BaseDialog extends Dialog {private Context context; public BaseDialog(@NonNull Context context) {super(context, R.style.DialogTheme); this.context = context; }/** * Dialog显示前处理逻辑 * * @param view Dialog内容布局 */ public void show(View view) {Window window = getWindow(); if (window == null) return; window.setContentView(view); WindowManager.LayoutParams pl = window.getAttributes(); pl.gravity = Gravity.CENTER; //位置 pl.height = WindowManager.LayoutParams.WRAP_CONTENT; int width = window.getDecorView().getResources().getDisplayMetrics().widthPixels; pl.width = (int) (width * 0.9); window.setAttributes(pl); show(); }@Override public void show() {Activity activity = ActivityUtils.getActivityByContext(context); if (activity != null && !activity.isFinishing()) {super.show(); } }@Override public void dismiss() {Activity activity = ActivityUtils.getActivityByContext(context); if (activity != null && !activity.isFinishing()) {super.dismiss(); } }/** * 是否是外部区域 * * @param context * @param event * @return */ public boolean isOutOfBounds(Context context, MotionEvent event) {final int x = (int) event.getX(); final int y = (int) event.getY(); final int slop = ViewConfiguration.get(context).getScaledWindowTouchSlop(); final Window window = getWindow(); if (window == null) return false; final View decorView = getWindow().getDecorView(); return (x < -slop) || (y < -slop) || (x > (decorView.getWidth() + slop)) || (y > (decorView.getHeight() + slop)); } }

然后使用单例模式AppDialogManager来管理Dialog,如下是完整的代码
package com.littlejerk.permissiondemo; import android.app.Activity; import android.app.Dialog; import android.content.DialogInterface; import android.text.method.ScrollingMovementMethod; import android.view.LayoutInflater; import android.view.View; import android.widget.LinearLayout; import android.widget.TextView; import com.blankj.utilcode.util.SizeUtils; import com.blankj.utilcode.util.StringUtils; import com.flyco.roundview.RoundTextView; /** * @author : HHotHeart * @date : 2021/8/21 01:03 * @desc : Dialog公共管理类 */ public class AppDialogManager {private AppDialogManager() {}public static AppDialogManager getInstance() {return Holder.instance; }private static class Holder {private static AppDialogManager instance = new AppDialogManager(); }/** * 普通弹框-只有一个实例的弹框 */ private BaseDialog mDialog = null; /** * 扩展的Dialog回调 */ public interface DialogClickCallback {void onClick(int index); }/** * 权限申请弹框 * * @param activity * @param content * @param positiveLister */ public void showPermissionRemindDialog(Activity activity, String content, DialogInterface.OnClickListener negativeLister, DialogInterface.OnClickListener positiveLister) {showCommonDialog(activity, activity.getString(R.string.dialog_permission_title), content, activity.getString(R.string.dialog_permission_refuse), negativeLister, activity.getString(R.string.dialog_permission_allow), positiveLister); }/** * 权限设置弹框提示 * * @param activity * @param content * @param positiveLister */ public void showPermissionSettingRemind(Activity activity, String content, DialogInterface.OnClickListener negativeLister, DialogInterface.OnClickListener positiveLister) {showCommonDialog(activity, activity.getString(R.string.dialog_permission_title), content, activity.getString(R.string.dialog_btn_cancel), negativeLister, activity.getString(R.string.dialog_btn_setting), positiveLister); }/** * 有Positive按钮的Dialog * * @param activity * @param title * @param content * @param positive * @param positiveLister */ public void showPositiveDialog(Activity activity, String title, String content, String positive, DialogInterface.OnClickListener positiveLister) {showCommonDialog(activity, title, content, null, null, positive, positiveLister); }/** * 有确认和取消按钮(没有title) * * @param activity * @param content * @param positiveLister */ public void showCommonDialog(Activity activity, String content, DialogInterface.OnClickListener positiveLister) {showCommonDialog(activity, null, content, activity.getString(R.string.dialog_btn_cancel), null, activity.getString(R.string.dialog_btn_confirm), positiveLister); }/** * 公共方法 * * @param activity * @param title * @param content * @param negative * @param negativeLister * @param positive * @param positiveLister */ public void showCommonDialog(Activity activity, String title, String content, String negative, DialogInterface.OnClickListener negativeLister, String positive, DialogInterface.OnClickListener positiveLister) {showCommonDialog(activity, title, content, negative, negativeLister, positive, positiveLister, true); }/** * 公共方法 * * @param activity * @param title * @param content * @param negative * @param negativeLister * @param positive * @param positiveLister * @param isClickDismiss */ public void showCommonDialog(Activity activity, String title, String content, String negative, DialogInterface.OnClickListener negativeLister, String positive, DialogInterface.OnClickListener positiveLister, boolean isClickDismiss) {showDialog(activity, title, content, negative, negativeLister, positive, positiveLister, false, false, null, true, isClickDismiss); }/** * Dialog基本方法 * * @param activity显示Dialog的Activity * @param titleDialog 标题 * @param contentDialog 内容 * @param negative左边按钮 * @param negativeLister左边按钮的点击事件 * @param positive右边按钮 * @param positiveLister右边按钮的点击事件 * @param isCanceledOnTouchOutside 点击非Dialog内容部分是否允许Dismiss * @param isCancelable点击后退键是否允许Dismiss * @param dismissListenerDialog消失的监听事件 * @param isMultiDialog是否允许多个Dialog同时存在 * @param isClickDismiss点击按钮是否允许dismiss Dialog */ private void showDialog(Activity activity, String title, String content, String negative, final DialogInterface.OnClickListener negativeLister, String positive, final DialogInterface.OnClickListener positiveLister, boolean isCanceledOnTouchOutside, boolean isCancelable, final DialogInterface.OnDismissListener dismissListener, boolean isMultiDialog, final boolean isClickDismiss) {View view = LayoutInflater.from(activity).inflate(R.layout.dialog_common_view, null); TextView tvTitle = view.findViewById(R.id.tv_dialog_title); TextView tvContent = view.findViewById(R.id.tv_dialog_content); tvContent.setMovementMethod(ScrollingMovementMethod.getInstance()); RoundTextView rtvLeft = view.findViewById(R.id.rtv_left); RoundTextView rtvRight = view.findViewById(R.id.rtv_right); //显示title逻辑 if (!StringUtils.isEmpty(title)) {tvTitle.setText(title); tvTitle.setVisibility(View.VISIBLE); } else {tvTitle.setVisibility(View.GONE); } //内容为空时,直接return,不显示dialog if (StringUtils.isEmpty(content)) {return; } //显示content逻辑 tvContent.setText(content); tvContent.setVisibility(View.VISIBLE); //没有标题时,content的上下距离30dp、文字大小14sp if (StringUtils.isEmpty(title)) {LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) tvContent.getLayoutParams(); params.setMargins( SizeUtils.dp2px(15), SizeUtils.dp2px(30), SizeUtils.dp2px(15), SizeUtils.dp2px(30)); tvContent.setLayoutParams(params); }//沒有title,只有右边按钮时,content文字大小为12sp if (StringUtils.isEmpty(title) && StringUtils.isEmpty(negative) && !StringUtils.isEmpty(positive)) {tvContent.setTextSize(12); }if (!StringUtils.isEmpty(negative)) {rtvLeft.setText(negative); rtvLeft.setVisibility(View.VISIBLE); } else {rtvLeft.setVisibility(View.GONE); } if (!StringUtils.isEmpty(positive)) {rtvRight.setText(positive); rtvRight.setVisibility(View.VISIBLE); } else {rtvRight.setVisibility(View.GONE); }if (isMultiDialog) {//允许多个Dialog存在 final BaseDialog dialog = new BaseDialog(activity); //外部是否可以取消 dialog.setCanceledOnTouchOutside(isCanceledOnTouchOutside); //返回是否可以取消 dialog.setCancelable(isCancelable); dialog.setOnDismissListener(dialog1 -> {if (dismissListener != null) dismissListener.onDismiss(dialog1); }); rtvLeft.setOnClickListener(v -> {if (isClickDismiss) {dialog.dismiss(); } if (negativeLister != null) {negativeLister.onClick(dialog, DialogInterface.BUTTON_NEGATIVE); }}); rtvRight.setOnClickListener(v -> {if (isClickDismiss) {dialog.dismiss(); } if (positiveLister != null) {positiveLister.onClick(dialog, DialogInterface.BUTTON_POSITIVE); }}); dialog.show(view); } else {//只允许一个Dialog存在 resetLatestDialog(mDialog); mDialog = new BaseDialog(activity); //外部是否可以取消 mDialog.setCanceledOnTouchOutside(isCanceledOnTouchOutside); //返回是否可以取消 mDialog.setCancelable(isCancelable); mDialog.setOnDismissListener(dialog -> {if (dismissListener != null) dismissListener.onDismiss(dialog); }); rtvLeft.setOnClickListener(v -> {if (isClickDismiss) {mDialog.dismiss(); } if (negativeLister != null) {negativeLister.onClick(mDialog, DialogInterface.BUTTON_NEGATIVE); }}); rtvRight.setOnClickListener(v -> {if (isClickDismiss) {mDialog.dismiss(); } if (positiveLister != null) {positiveLister.onClick(mDialog, DialogInterface.BUTTON_POSITIVE); }}); mDialog.show(view); }}/** * 释放掉最近显示的Dialog * * @param dialog */ private void resetLatestDialog(Dialog dialog) {if (dialog != null && dialog.isShowing()) {dialog.dismiss(); dialog = null; } }}

这里我们提供了一个最终调用方法,不同样式可通过传入不同参数控制
private void showDialog(Activity activity, String title, String content, String negative, final DialogInterface.OnClickListener negativeLister, String positive, final DialogInterface.OnClickListener positiveLister, boolean isCanceledOnTouchOutside, boolean isCancelable, final DialogInterface.OnDismissListener dismissListener, boolean isMultiDialog, final boolean isClickDismiss);

方法参数和说明如下
activity 上下文
title 标题
content 内容
negative 左边按钮(取消)
negativeLister 左边按钮的回调
positive 右边按钮(确认)
positiveLister 右边按钮的回调
isCanceledOnTouchOutside 点击非Dialog内容部分是否允许dismiss
isCancelable 点击后退键是否允许dismiss
dismissListener dialog消失的监听事件
isMultiDialog 是否允许多个dialog同时存在
isClickDismiss 点击按钮是否允许 dismiss dialog
【Android|Android 优雅地管理Dialog弹框】可实现的功能
  • 可任意修改按钮文案
  • 设置是否有标题
  • 控制按钮的数量
  • 按钮点击事件回调
  • 任意控制dialog的消失(如强制更新按钮点击不允许dialog消失、back键等)
要想实现这些功能,直接重载上面这个showDialog()方法。

文章主要讲解了小球项目中dialog的样式管理,我们可以根据代码修改符合自己项目的dialog,如dialog的位置显示、圆角控制、左右边距、按钮的显示样式等等,这里只是提供一个思路,以后应该会整合一些常用的dialog,欢迎大家来支持关注一波。最后,文章代码在 PermissionDemo里,有需要可以去看看!

APP合规检查系列文章:
Android 组件导出风险及防范
Android Activity防劫持方案
Android 申请权限前简单封装弹框阐述申请理由工具类,应付app合规检查

    推荐阅读