DialogX 的一些骚包的高阶使用技巧

2,888 阅读9分钟

DialogX 的一些骚包的高阶使用技巧

DialogX 是一款轻松易用的对话框组件,具备高扩展性和易上手的特点,包含各种自定义主题样式,可以快速实现各式各样的对话框效果,也避免了 AlertDialog 的诸多蛋疼的问题,详情可以参阅这篇文章:使用 DialogX 快速构建 Android App 对话框 - 掘金 (juejin.cn)

本篇文章将介绍一些 DialogX 的使用技巧,也欢迎大家集思广益在评论区留下宝贵的建议,DialogX 自始至终的目标都是尽量让开发变得更加简单,基于此目的,DialogX 首先想做的就是避免重复性劳动,一般我们开发产品总会有一些各式各样的需要,比如关于对话框启动和关闭的动画。

局部>组件内>全局生效的属性

局部设置

DialogX 的很多属性都可以自定义调整,最简单的就是通过实例的 set 方法对属性进行调整,例如对于动画,你可以使用这些 set 方法进行调整:

Snipaste_2023-02-08_16-00-00.png

但是,当我们的程序中有大量的对话框,但每个 MessageDialog 都需要调整,又不能影响其他对话框的动画,该怎么设置呢?

组件生效

此时就可以使用该对话框的静态方法直接进行设置,例如:

MessageDialog.overrideEnterDuration = 100;    //入场动画时长为100毫秒
MessageDialog.overrideExitDuration = 100;     //出场动画时长为100毫秒
MessageDialog.overrideEnterAnimRes = R.anim.anim_dialogx_top_enter;  //入场动画资源
MessageDialog.overrideExitAnimRes = R.anim.anim_dialogx_top_exit;    //出场动画资源

如果要设置的属性想针对全局,也就是所有对话框都生效,此时可以使用全局设置进行调整:

全局设置

你可以随时召唤神龙 DialogX,直接修改静态属性,这里的设置都是针对全局的,可以快速完成需要的调整。

DialogX.enterAnimDuration = 100;
DialogX.exitAnimDuration = 100;

上边演示的是动画相关设置,除此之外,你还可以对对话框的标题文字样式、对话框OK按钮的样式、取消按钮的样式、正文内容的文字样式等等进行全局的调整,只需要知道属性生效的优先级是:

优先级为:实例使用set方法设置 > 组件override设置 > 全局设置。

额外的,如果需要对部分组件的行为进行调整,例如 PopTip 的默认显示位置位于屏幕底部,但产品或设计要求想显示到屏幕中央,但这个设置又取决于主题的限制,此时你可以通过重写主题的设置来实现调整:

覆盖主题设置

想要将 PopTip 吐司提示不按照主题的设定(例如屏幕底部)显示,而是以自己的要求显示(例如屏幕中央),但对于 PopTip 的 align 属性属于主题控制的,此时可以通过重写主题来调整对话框的部分行为,例如:

DialogX.globalStyle = new MaterialStyle(){
    @Override
    public PopTipSettings popTipSettings() {
        return new PopTipSettings() {
            @Override
            public ALIGN align() {
                return ALIGN.CENTER;
            }
        };
    }
};

DialogX 强大的扩展性允许你发挥更多想象空间!如果你的产品经理或者设计师依然不满足于简简单单的动画,想要定制更为丰富的入场/出场效果,此时可以利用 DialogX 预留的对话框动画控制接口对每一个对话框内的组件动画细节进行定制。

完全的动画细节定制

例如,我们可以针对一个对话框的背景遮罩进行透明度动画效果处理,但对于对话框内容部分进行一个从屏幕顶部进入的动画效果,其他的,请发挥你的想象进行设计吧!

使用 DialogXAnimInterface 接口可以完全自定义开启、关闭动画。

由于 DialogX 对话框组件的内部元素都是暴露的,你可以轻松获取并访问内部实例,利用这一点,再加上 DialogXAnimInterface 会负责对话框启动和关闭的动画行为,你可以充分利用它实现你想要的效果。

例如对于一个 CustomDialog,你可以这样控制其启动和关闭动画:

CustomDialog.show(new OnBindView<CustomDialog>(R.layout.layout_custom_dialog) {
            @Override
            public void onBind(final CustomDialog dialog, View v) {
                //...
            }
        })
        //实现完全自定义动画效果
        .setDialogXAnimImpl(new DialogXAnimInterface<CustomDialog>() {
            //启动对话框动画逻辑
            @Override
            public void doShowAnim(CustomDialog customDialog, ObjectRunnable<Float> animProgress) {
                //创建一个资源动画
                Animation enterAnim;
                int enterAnimResId = com.kongzue.dialogx.R.anim.anim_dialogx_top_enter;
                enterAnim = AnimationUtils.loadAnimation(me, enterAnimResId);
                enterAnim.setInterpolator(new DecelerateInterpolator(2f));
                long enterAnimDurationTemp = enterAnim.getDuration();
                enterAnim.setDuration(enterAnimDurationTemp);
                customDialog.getDialogImpl().boxCustom.startAnimation(enterAnim);	//通过 getDialogImpl() 获取内部暴露的 boxCustom 元素
                //创建一个背景遮罩层的渐变动画
                ValueAnimator bkgAlpha = ValueAnimator.ofFloat(0f, 1f);
                bkgAlpha.setDuration(enterAnimDurationTemp);
                bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        //汇报动画进度,同时 animation.getAnimatedValue() 将改变遮罩层的透明度
                        animProgress.run((Float) animation.getAnimatedValue());
                    }
                });
                bkgAlpha.start();
            }
            
            //关闭对话框动画逻辑
            @Override
            public void doExitAnim(CustomDialog customDialog, ObjectRunnable<Float> animProgress) {
                //创建一个资源动画
                int exitAnimResIdTemp = com.kongzue.dialogx.R.anim.anim_dialogx_default_exit;
                Animation exitAnim = AnimationUtils.loadAnimation(me, exitAnimResIdTemp);
                customDialog.getDialogImpl().boxCustom.startAnimation(exitAnim);	//通过 getDialogImpl() 获取内部暴露的 boxCustom 元素
                //创建一个背景遮罩层的渐变动画
                ValueAnimator bkgAlpha = ValueAnimator.ofFloat(1f, 0f);
                bkgAlpha.setDuration(exitAnim.getDuration());
                bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        //汇报动画进度,同时 animation.getAnimatedValue() 将改变遮罩层的透明度
                        animProgress.run((Float) animation.getAnimatedValue());
                    }
                });
                bkgAlpha.start();
            }
        });

对于 animProgress 它本质上是个反向回调执行器,因为动画时长不定,你需要通知 DialogX 当前你的动画到达哪个阶段了,对话框需要根据这个阶段进行操作处理,例如关闭动画执行过程应当是 1f 至 0f 的过程,完毕后应当销毁对话框,那么当 animProgress.run(0f) 时就会执行销毁流程,而启动动画应当是 0f 至 1f 的过程,当 animProgress.run(1f) 时启动对话框的动画完全执行完毕。

另外,你有没有注意到上述代码中的一个小细节?你可以通过 .getDialogImpl() 访问对话框的所有内部实例,这意味着,DialogX 中的所有实例事实上都是对外开放的,你可以在对话框启动后(DialogLifecycle#onShow)通过 DialogImpl 获取对话框的所有内容组件,对他们进行你想做的调整和设置,这都将极大程度上方便开发者对对话框内容进行定制。

正如我一开始所说,DialogX 将坚持努力打造一款更好用,更高效可定制化的对话框组件。

队列对话框

某些场景下需要有“模态”对话框的需要,即,一次性创建多个对话框,组成队列,逐一显示,当上一个对话框关闭时自动启动下一个对话框,此时可以使用队列对话框来完成。

示例代码如下,在 DialogX.showDialogList(...) 中构建多个对话框,请注意这些对话框必须是没有启动的状态,使用 .build() 方法完成构建,以 “,” 分隔组成队列,即可自动启动。

DialogX.showDialogList(
        MessageDialog.build().setTitle("提示").setMessage("这是一组消息对话框队列").setOkButton("开始").setCancelButton("取消")
                .setCancelButton(new OnDialogButtonClickListener<MessageDialog>() {
                    @Override
                    public boolean onClick(MessageDialog dialog, View v) {
                        dialog.cleanDialogList();
                        return false;
                    }
                }),
        PopTip.build().setMessage("每个对话框会依次显示"),
        PopNotification.build().setTitle("通知提示").setMessage("直到上一个对话框消失"),
        InputDialog.build().setTitle("请注意").setMessage("你必须使用 .build() 方法构建,并保证不要自己执行 .show() 方法").setInputText("输入文字").setOkButton("知道了"),
        TipDialog.build().setMessageContent("准备结束...").setTipType(WaitDialog.TYPE.SUCCESS),
        BottomDialog.build().setTitle("结束").setMessage("下滑以结束旅程,祝你编码愉快!").setCustomView(new OnBindView<BottomDialog>(R.layout.layout_custom_dialog) {
            @Override
            public void onBind(BottomDialog dialog, View v) {
                ImageView btnOk;
                btnOk = v.findViewById(R.id.btn_ok);
                btnOk.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                                        dialog.dismiss();
                                    }
                });
            }
        })
);

使用过程中,随时可以使用 .cleanDialogList() 来停止接下来的队列对话框的显示。

多种构建模式

DialogX 的对话框支持多种构建模式,开发者可以随意选择自己最为顺手的方式对对话框进行构建,大大提升开发效率。

例如对于一个消息对话框,你可以使用最基础的 new 指令构建:

new MessageDialog()
    .setTitle("标题")
    .setMessage("内容")
    .setOkButton("确定", new new OnDialogButtonClickListener<MessageDialog>() {
            @Override
            public boolean onClick(MessageDialog baseDialog, View v) {
                toast("点击确定按钮");
                return false;
            }
        })
    .setCancelButton(...)
    .show();

也可以使用 build 构建:

MessageDialog.build()
        .setTitle("标题")
        .setMessage("内容")
        .setOkButton("确定")
        .show();

亦可以使用静态方法快速构建:

MessageDialog.show("标题", "正文内容", "确定").setOkButton(new OnDialogButtonClickListener<MessageDialog>() {
    @Override
    public boolean onClick(MessageDialog baseDialog, View v) {
        toast("点击确定按钮");
        return false;
    }
});

随心所欲,想怎么写就怎么写。

延迟处理用户选择

懒得对每个按钮写回调?业务不需要点击按钮后立即进行处理?或者无论用户点击哪个按钮都有相同的业务代码要处理?完全不需要担心,DialogX 的对话框在关闭后依然会保留用户选择的状态,只要你持有它的实例,依然可以随时获取用户选择的按钮状态:

private MessageDialog dialog;

//业务流程模拟,创建并显示了一个对话框,但不立即处理点击事务
dialog = MessageDialog.show("Title", "Ask Question", "OK", "NO", "OTHER");

//需要时可根据 getButtonSelectResult() 方法获取用户点击了哪个按钮选项
BUTTON_SELECT_RESULT result = dialog.getButtonSelectResult();

BUTTON_SELECT_RESULT 是一个枚举,它包含以下类型:

NONE,           //未做出选择
BUTTON_OK,      //选择了确定按钮
BUTTON_CANCEL,  //选择了取消按钮
BUTTON_OTHER    //选择了其他按钮

你可以根据它的状态判断用户点击了哪个按钮。

基于这个特性,如果业务流程中针对用户的选择,无论选择哪个选项都有相同部分的代码需要执行,那么开发者也可以在用户选择后,例如在对话框的关闭事件中对用户的选择进行统一处理,减少重复代码量:

new MessageDialog() {
    @Override
    public void onDismiss(MessageDialog dialog) {
        BUTTON_SELECT_RESULT result = dialog.getButtonSelectResult();
        //处理 result...
    }
}

对于底部菜单对话框 BottomMenu、弹出悬浮菜单 PopMenu,也有相应的 get 方法可以获取用户选择的菜单索引或菜单文本,你可以在任何时间对用户的选择进行处理。

DialogX 的所有设计均在努力减少开发难度,提升开发效率,使调用和构建一个令人满意的菜单变得更加容易。

尾巴

DialogX 正在努力打造一款对开发者更友好,使用起来更为简单方便的对话框组件,若你有好的想法,也欢迎加入进来一起为 DialogX 添砖加瓦,通过 Github 一起让 DialogX 变得更加强大!

DialogX 路牌:github.com/kongzue/Dia…