Android 优雅地管理Dialog弹框

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

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

接下来讲解下小球项目里Dialog,我们项目中Dialog居中显示,属性有标题、内容、按钮(1或2个),如我们的布局文件dialog_common_view.xml
<?xml version="1.0" encoding="utf-8"?>
<com.flyco.roundview.RoundLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_height="wrap_content"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:paddingBottom="10dp"
    app:rv_backgroundColor="@color/white"
    app:rv_cornerRadius="6dp">

    <com.flyco.roundview.RoundTextView
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        android:gravity="center"
        android:id="@+id/tv_dialog_title"
        android:minHeight="50dp"
        android:paddingBottom="4dp"
        android:paddingEnd="15dp"
        android:paddingStart="15dp"
        android:paddingTop="4dp"
        android:text="标题"
        android:textColor="@color/dialog_title_text"
        android:textSize="17sp"
        android:textStyle="bold"
        android:visibility="visible"
        app:rv_backgroundColor="@color/dialog_title_bg"
        app:rv_cornerRadius_TL="6dp"
        app:rv_cornerRadius_TR="6dp" />

    <TextView
        android:layout_height="wrap_content"
        android:layout_marginBottom="25dp"
        android:layout_marginLeft="15dp"
        android:layout_marginRight="15dp"
        android:layout_marginTop="25dp"
        android:layout_width="match_parent"
        android:gravity="center"
        android:id="@+id/tv_dialog_content"
        android:lineSpacingExtra="2dp"
        android:maxHeight="400dp"
        android:scrollbars="vertical"
        android:text="弹框内容"
        android:textColor="@color/dialog_content_text"
        android:textSize="14sp" />


    <LinearLayout
        android:layout_height="wrap_content"
        android:layout_marginLeft="15dp"
        android:layout_marginRight="15dp"
        android:layout_width="match_parent"
        android:id="@+id/ll_bottom_operation"
        android:orientation="horizontal">

        <com.flyco.roundview.RoundTextView
            android:layout_height="40dp"
            android:layout_marginEnd="4dp"
            android:layout_weight="1"
            android:layout_width="0dp"
            android:gravity="center"
            android:id="@+id/rtv_left"
            android:text="@string/dialog_btn_cancel"
            android:textColor="@color/dialog_btn_cancel_text"
            android:textSize="14sp"
            android:visibility="visible"
            app:rv_backgroundColor="@color/dialog_btn_cancel_bg"
            app:rv_backgroundPressColor="@color/dialog_btn_cancel_bg_checked"
            app:rv_cornerRadius="6dp"
            app:rv_strokeColor="@color/dialog_btn_cancel_stroke"
            app:rv_strokePressColor="@color/dialog_btn_cancel_stroke_checked"
            app:rv_strokeWidth="1dp" />

        <com.flyco.roundview.RoundTextView
            android:layout_height="40dp"
            android:layout_marginStart="4dp"
            android:layout_weight="1"
            android:layout_width="0dp"
            android:gravity="center"
            android:id="@+id/rtv_right"
            android:text="@string/dialog_btn_confirm"
            android:textColor="@color/dialog_btn_confirm_text"
            android:textSize="14sp"
            android:visibility="visible"
            app:rv_backgroundColor="@color/dialog_btn_confirm_bg"
            app:rv_backgroundPressColor="@color/dialog_btn_confirm_bg_checked"
            app:rv_cornerRadius="6dp" />

    </LinearLayout>
</com.flyco.roundview.RoundLinearLayout>

用到的依赖
    implementation 'com.blankj:utilcodex:1.30.6'
    implementation 'com.flyco.roundview:FlycoRoundView_Lib:1.1.4@aar'
颜色值
    <!--弹框相关颜色-->
    <color name="dialog_title_text">#101a3e</color>
    <color name="dialog_title_bg">#F9FAFB</color>
    <color name="dialog_content_text">#221715</color>

    <color name="dialog_btn_cancel_text">#221715</color>
    <color name="dialog_btn_cancel_stroke">#221715</color>
    <color name="dialog_btn_cancel_stroke_checked">#221715</color>
    <color name="dialog_btn_cancel_bg">#FFFFFF</color>
    <color name="dialog_btn_cancel_bg_checked">#F0F0F0</color>

    <color name="dialog_btn_confirm_text">#221715</color>
    <color name="dialog_btn_confirm_stroke">#221715</color>
    <color name="dialog_btn_confirm_stroke_checked">#221715</color>
    <color name="dialog_btn_confirm_bg">#4996F3</color>
    <color name="dialog_btn_confirm_bg_checked">#4996F3</color>

    <color name="dialog_btn_confirm_text_enable">#80221715</color>
    <color name="dialog_btn_confirm_bg_enable">#384996F3</color>
字符值
    <string name="dialog_title">温馨提示</string>
    <string name="dialog_permission_allow">允许</string>
    <string name="dialog_permission_refuse">拒绝</string>
    <string name="dialog_permission_title">申请权限</string>
    <string name="dialog_btn_cancel">取消</string>
    <string name="dialog_btn_confirm">确认</string>
    <string name="dialog_btn_setting">设置</string>
以前项目中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 title                    Dialog 标题
     * @param content                  Dialog 内容
     * @param negative                 左边按钮
     * @param negativeLister           左边按钮的点击事件
     * @param positive                 右边按钮
     * @param positiveLister           右边按钮的点击事件
     * @param isCanceledOnTouchOutside 点击非Dialog内容部分是否允许Dismiss
     * @param isCancelable             点击后退键是否允许Dismiss
     * @param dismissListener          Dialog消失的监听事件
     * @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
dismissListenerdialog消失的监听事件
isMultiDialog是否允许多个dialog同时存在
isClickDismiss点击按钮是否允许 dismiss dialog

可实现的功能

  • 可任意修改按钮文案
  • 设置是否有标题
  • 控制按钮的数量
  • 按钮点击事件回调
  • 任意控制dialog的消失(如强制更新按钮点击不允许dialog消失、back键等)
要想实现这些功能,直接重载上面这个showDialog()方法。

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

APP合规检查系列文章:
Android 组件导出风险及防范
Android Activity防劫持方案
Android 申请权限前简单封装弹框阐述申请理由工具类,应付app合规检查
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黄小梁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值