Android笔记(三十五):用责任链模式封装一个App首页Dialog管理工具

news2024/11/25 4:40:35

背景

项目需要在首页弹一系列弹窗,每个弹窗是否弹出都有自己的策略,以及哪个优先弹出,哪个在上一个关闭后再弹出,为了更好管理,于是封装了一个Dialog管理工具

效果

在这里插入图片描述

  • 整体采用责任链模块设计,控制优先级及弹出策略

原理分析

  1. 每个弹窗都当做一个节点Node,抽象出一些公共接口
public interface Node {
    int getId();

    String getTag();

    void complete();

    void error(ChainException e);
}
  1. 定义弹窗的状态
    INIT:初始创建
    PROGRESS:开始弹出
    COMPLETE:完成弹出到关闭的流程
    ERROR:弹出错误
public interface Operation {
    State.INIT INIT = new State.INIT();
    State.PROGRESS PROGRESS = new State.PROGRESS();
    State.COMPLETE COMPLETE = new State.COMPLETE();

    abstract class State {
        State() {
        }

        public static final class INIT extends State {
            private INIT() {
                super();
            }

            @Override
            @NonNull
            public String toString() {
                return "INIT";
            }
        }

        public static final class PROGRESS extends State {
            private PROGRESS() {
                super();
            }

            @Override
            @NonNull
            public String toString() {
                return "PROGRESS";
            }
        }

        public static final class COMPLETE extends State {
            private COMPLETE() {
                super();
            }

            @Override
            @NonNull
            public String toString() {
                return "COMPLETE";
            }
        }

        public static final class ERROR extends State {

            private final Throwable mThrowable;

            public ERROR(@NonNull Throwable exception) {
                super();
                mThrowable = exception;
            }

            @NonNull
            public Throwable getThrowable() {
                return mThrowable;
            }

            @Override
            @NonNull
            public String toString() {
                return String.format("ERROR (%s)", mThrowable.getMessage());
            }
        }
    }
}
  1. 为Dialog节点定义具体的状态切换
public class DialogNode implements Node {
    private static final String TAG = "ChainNode";
    private int id;
    private String tag;
    private Operation.State state = Operation.INIT;
    private Executor executor;
    private CallBack callBack;

    private DialogNode(int id, String tag, Executor executor) {
        this.id = id;
        this.tag = tag;
        this.executor = executor;
    }

    public static DialogNode create(int id, Executor executor) {
        return create(id, TAG + id, executor);
    }

    public static DialogNode create(int id, String tag, Executor executor) {
        return new DialogNode(id, tag, executor);
    }

    @Override
    public void complete() {
        setState(Operation.COMPLETE);
        if (callBack != null) {
            callBack.onComplete();
        }
    }

    @Override
    public void error(ChainException e) {
        setState(Operation.COMPLETE);
        if (callBack != null) {
            callBack.onError(e);
        }
    }

    public void process(CallBack callBack) {
        this.callBack = callBack;
        if (executor != null) {
            executor.execute(this);
        }
    }

    public static String getTAG() {
        return TAG;
    }

    @Override
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Override
    public String getTag() {
        return tag;
    }

    public void setTag(String tag) {
        this.tag = tag;
    }

    public Executor getExecutor() {
        return executor;
    }

    public void setExecutor(Executor executor) {
        this.executor = executor;
    }

    public CallBack getCallBack() {
        return callBack;
    }

    public void setCallBack(CallBack callBack) {
        this.callBack = callBack;
    }

    public Operation.State getState() {
        return state;
    }

    public void setState(Operation.State state) {
        this.state = state;
    }

    @Override
    public String toString() {
        return "ChainNode{" +
                "id=" + id +
                ", tag='" + tag + '\'' +
                ", state=" + state +
                ", executor=" + executor +
                ", callBack=" + callBack +
                '}';
    }

    public interface CallBack {
        void onComplete();

        void onError(ChainException e);
    }
}
  1. 抽象DialogNode的构建工厂类,弹窗链上每个Dialog必须继承该类,并实现createDialog方法返回具体的业务Dialog;实现execute方法控制弹窗是否要弹出,以及通知工具什么时候完成弹出到关闭的流程
abstract class MDialogNodeCreator {
    protected var nodeDialog: Dialog? = null

    fun build(context: Context, dialogId: Int): DialogNode? {
        nodeDialog = createDialog(context)
        val node = DialogNode.create(dialogId) { node ->
            execute(node)
        }
        return node
    }

    /**
     * 构造一个对话框
     */
    abstract fun createDialog(context: Context): Dialog

    /**
     * 在此执行业务弹窗逻辑
     */
    abstract fun execute(node: Node)
}
  1. ChainProcessor核心类,保存了每一个DialogNode,在调用start后开始从队列里面去头节点,当DialogNode回调onComplete后递归取下一个节点,直到队列尾部
public class ChainProcessor {
    private final SparseArray<DialogNode> nodeArrays;
    private final Builder builder;

    private ChainProcessor(SparseArray<DialogNode> nodeArrays, Builder builder) {
        this.nodeArrays = nodeArrays;
        this.builder = builder;
    }

    public void start() {
        if (nodeArrays == null || nodeArrays.size() <= 0) {
            Log.e("zbm111", "nodeArrays == null || nodeArrays.size <= 0");
            return;
        }
        startNode(nodeArrays.keyAt(0));
    }

    private void startNode(int nodeId) {
        int index = nodeArrays.indexOfKey(nodeId);
        DialogNode node = nodeArrays.valueAt(index);
        if (node != null && node.getState() == Operation.INIT) {
            node.setState(Operation.PROGRESS);
            node.process(new DialogNode.CallBack() {
                @Override
                public void onComplete() {
                    nextNode(index);
                }

                @Override
                public void onError(ChainException e) {
                    cancel();
                }
            });
        }
    }

    private void nextNode(int index) {
        //移除执行过的第一个
        removeNode(index);
        if (nodeArrays != null && nodeArrays.size() > 0) {
            startNode(nodeArrays.keyAt(0));
        }
    }

    private void removeNode(int index) {
        if (nodeArrays != null && nodeArrays.size() > 0) {
            nodeArrays.removeAt(index);
        }
    }

    private void cancel() {
        if (nodeArrays != null && nodeArrays.size() > 0) {
            nodeArrays.clear();
        }
    }

    public Builder getBuilder() {
        return builder;
    }

    public static class Builder {
        private final SparseArray<DialogNode> nodeArrays;
        private String tag;

        public Builder() {
            this.nodeArrays = new SparseArray<>();
        }

        public Builder addNode(DialogNode node) {
            if (node != null) {
                nodeArrays.append(node.getId(), node);
                if (TextUtils.isEmpty(tag)) {
                    tag = UUID.randomUUID().toString();
                }
                node.setTag(tag);
            }
            return this;
        }

        public Builder addNodes(DialogNode... nodes) {
            if (nodes != null && nodes.length > 0) {
                for (DialogNode node : nodes) {
                    addNode(node);
                }
            }
            return this;
        }

        public Builder addTag(String tag) {
            this.tag = tag;
            return this;
        }

        public ChainProcessor build() {
            checkTag();
            return new ChainProcessor(nodeArrays, this);
        }

        private void checkTag() {
            if (nodeArrays.size() > 0) {
                if (TextUtils.isEmpty(tag)) {
                    tag = UUID.randomUUID().toString();
                }
                for (int i = 0; i < nodeArrays.size(); i++) {
                    nodeArrays.get(nodeArrays.keyAt(i)).setTag(tag);
                }
            }
        }

        public String getTag() {
            return tag;
        }

        public SparseArray<DialogNode> getNodes() {
            return nodeArrays;
        }
    }
}
  1. 工具外部接口,主要是构建ChainProcessor,支持处理多个弹窗链
bject MDialogChainHelper {
    private val chainNodeMap = mutableMapOf<String, ChainProcessor>()

    private fun build(chainProcessor: ChainProcessor) {
        if (chainNodeMap.containsKey(chainProcessor.builder.tag)) {
            chainNodeMap.remove(chainProcessor.builder.tag)
        }
        chainNodeMap[chainProcessor.builder.tag] = chainProcessor
    }

    fun startDialogChain(tag: String) {
        chainNodeMap[tag]?.start()
    }

    private fun clearAllChain() {
        chainNodeMap.clear()
    }

    private fun clearDialogChain(tag: String): ChainProcessor? {
        return chainNodeMap.remove(tag)
    }

    fun addDialogChain(tag: String): ChainProcessor {
        val chainProcessor = ChainProcessor.Builder().addTag(tag).build()
        if (chainNodeMap.containsKey(chainProcessor.builder.tag)) {
            chainNodeMap.remove(chainProcessor.builder.tag)
        }
        chainNodeMap[chainProcessor.builder.tag] = chainProcessor
        return chainProcessor
    }

    fun getDialogChain(tag: String): ChainProcessor? {
        return chainNodeMap[tag]
    }

}

Demo验证

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        MDialogChainHelper.run {
            addDialogChain("test_main")
                .builder
                .addNode(OneDialogNode().build(this@MainActivity, 0))
                .addNode(TwoDialogNode().build(this@MainActivity, 1))
            startDialogChain("test_main"    )
        }
    }
}
class OneDialogNode: MDialogNodeCreator() {

    override fun createDialog(context: Context): Dialog {
        val dialog = Dialog(context)
        dialog.setContentView(R.layout.dialog_one)
        dialog.findViewById<View>(R.id.tv_title)?.setOnClickListener {
            dialog.dismiss()
        }
        dialog.window?.setLayout(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT)
        return dialog
    }

    override fun execute(node: Node) {
        nodeDialog?.setOnDismissListener {
            node.complete()
            nodeDialog = null
        }
        nodeDialog?.show()
    }
}
class TwoDialogNode: MDialogNodeCreator() {

    override fun createDialog(context: Context): Dialog {
        val dialog = Dialog(context)
        dialog.setContentView(R.layout.dialog_two)
        dialog.findViewById<View>(R.id.tv_title)?.setOnClickListener {
            dialog.dismiss()
        }
        dialog.window?.setLayout(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
        return dialog
    }

    override fun execute(node: Node) {
        nodeDialog?.setOnDismissListener {
            node.complete()
            nodeDialog = null
        }
        nodeDialog?.show()
    }
}

完整代码点击下载

大家觉得不错的点个赞鼓励下哦,有任何建议欢迎留言,谢谢🌹

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2238012.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[CKS] K8S Dockerfile和yaml文件安全检测

最近准备花一周的时间准备CKS考试&#xff0c;在准备考试中发现有一个题目关于Dockerfile和yaml文件安全检测的题目。 ​ 专栏其他文章: [CKS] Create/Read/Mount a Secret in K8S-CSDN博客[CKS] Audit Log Policy-CSDN博客 -[CKS] 利用falco进行容器日志捕捉和安全监控-CSDN博…

Golang | Leetcode Golang题解之第552题学生出勤记录II

题目&#xff1a; 题解&#xff1a; const mod int 1e9 7type matrix [6][6]intfunc (a matrix) mul(b matrix) matrix {c : matrix{}for i, row : range a {for j : range b[0] {for k, v : range row {c[i][j] (c[i][j] v*b[k][j]) % mod}}}return c }func (a matrix) p…

(Go语言)初上手Go?本篇文章帮拿捏Go的数据类型!

1. bool 类型 布尔类型&#xff1a;只有 true 和 false 两种值 在Go中&#xff0c;整数 0 不代表 false 值&#xff0c;1也不代表 true 值 即数字无法代替布尔值进行逻辑判断&#xff0c;两者是完全不同的类型 布尔类型占用 1 字节 2. int 整型 Go中为不同位数的整数分配…

用 Python 从零开始创建神经网络(四):激活函数(Activation Functions)

激活函数&#xff08;Activation Functions&#xff09; 引言1. 激活函数的种类a. 阶跃激活功能b. 线性激活函数c. Sigmoid激活函数d. ReLU 激活函数e. more 2. 为什么使用激活函数3. 隐藏层的线性激活4. 一对神经元的 ReLU 激活5. 在隐蔽层中激活 ReLU6. ReLU 激活函数代码7. …

【C++】—掌握STL string类:string的模拟实现

文章目录 &#x1f49e;1.浅拷贝&#x1f49e;2.深拷贝&#x1f49e;3. string类的模拟实现&#x1f49e;3.1 string的构造函数&#x1f49e;3.2 string的析构函数&#x1f49e;3.3 string的拷贝构造&#x1f49e;3.4 string的size&#x1f49e;3.5 string的operator[]&#x1…

元器件篇——自恢复保险丝(PPTC)

1 定义 保险丝&#xff08;Fuse&#xff09;也被称为电流保险丝。根据IEC127标准&#xff0c;将保险丝定义为熔断体。主要的作用就是起过载保护。电路中正确布置保险丝&#xff0c;当保险丝在电流异常升高到一定时&#xff0c;或者热度升高到一定时&#xff0c;自身熔断&#x…

多媒体信息检索

文章目录 一、绪论二、文本检索 (Text Retrieval)(一) 索引1.倒排索引2.TF-IDF (二) 信息检索模型 (IR模型&#xff0c;Information Retrieval)1.布尔模型 (Boolean模型)(1)扩展的布尔模型 (两个词)(2)P-Norm模型 (多个词) 2.向量空间模型 (Vector Space Model&#xff0c;VSM)…

Node.js——fs模块-文件夹操作

1、借助Node.js的能力&#xff0c;我们可以对文件夹进行创建、读取、删除等操作 2、方法 方法 说明 mkdir/mkdirSync 创建文件夹 readdir/readdirSync 读取文件夹 rmdir/rmdirSync 删除文件夹 3、语法 其余的方法语法类似 本文的分享到此结束&#xff0c;欢迎大家评论区…

ABAP:SET CURSOR FIELD设置鼠标焦点

SET CURSOR FIELD <字段名>&#xff1a;设置鼠标焦点到该字段 SET CURSOR 设置到鼠标焦点列还是行 SET CURSOR LINE 设置鼠标焦点到行 GET CURSOR field <字段名> &#xff1a;这个相对应的获取鼠标焦点得到的字段

Gitlab-执行器为Kubetnetes时的注意事项,解决DNS解析问题

一、Gitlab-Runner 这里对于Runner的理解非常重要。 具体执行ci流水线的叫执行器。执行器可以部署是shell、docker、k8s的pod.执行完任务则生命周期结束。 管理执行器的叫Gitlab-Runner。Runner则是与Gitlab Server的Ci agent.(可以简单这么理解) 二、执行器为Kubetnetes时,DN…

双向链表(带头双向循环链表)巨详解!!!

概念 本文讲述的双向链表&#xff0c;全名叫做带头双向循环链表&#xff0c;我们学习的链表总共有八种 在前文讲述单链表时所讲到的单链表&#xff0c;其实就叫做不带头单向不循环链表&#xff0c;这里的带头、不带头才是真正的头结点&#xff0c;前文中的头结点其实叫做首元素…

时序预测 | Python基于CNN-transformer时间序列预测

时序预测 | Python基于CNN-transformer时间序列预测 目录 时序预测 | Python基于CNN-transformer时间序列预测预测效果基本介绍参考资料 预测效果 基本介绍 时序预测 | Python基于CNN-transformer时间序列预测 Cnn-transformer-自适应稀疏自注意力ASSA-对比归一化contranorm预…

网站架构知识之Ansible进阶(day022)

1.handler触发器 应用场景&#xff1a;一般用于分发配置文件时候&#xff0c;如果配置文件有变化&#xff0c;则重启服务&#xff0c;如果没有变化&#xff0c;则不重启服务 案列01&#xff1a;分发nfs配置文件&#xff0c;若文件发生改变则重启服务 2.when判断 用于给ans运…

Yolo11改进策略:上采样改进|CARAFE,轻量级上采样|即插即用|附改进方法+代码

论文介绍 CARAFE模块概述&#xff1a;本文介绍了一种名为CARAFE&#xff08;Content-Aware ReAssembly of FEatures&#xff09;的模块&#xff0c;它是一种用于特征上采样的新方法。应用场景&#xff1a;CARAFE模块旨在改进图像处理和计算机视觉任务中的上采样过程&#xff0…

常用的c++特性-->day02

c11新特性 可调用对象案例分析 可调用对象包装器语法案例可调用对象包装器作为函数参数补充&#xff1a;类型转换运算符案例 可调用对象绑定器语法格式绑定非类成员函数/变量案例1案例2案例3案例4 绑定类成员函数/变量 lambda表达式捕获列表案例1返回值案例2 --> 包装器绑定…

锐捷技能大赛—L2TP隧道与L2TP Over IPSec嵌套,并在隧道内运行OSPF

目录 总部与分支站点之间建立隧道 基础配置 配置L2TP VPN ​编辑配置L2TP Over IPSec L2TP Over IPSec隧道内运行OSPF协议 总部与分支站点之间建立隧道 拓扑如下 基础配置 R1 int g0/1 ip add 10.1.1.1 30 int g0/0 ip add 192.168.10.254 24 exit ip route 0.0.0.0 0.0…

python可视化将多张图整合到一起(画布)

这周有点事忙着&#xff0c;没时间重温刚结束的Mathurcup数学建模&#xff0c;这两天也是再看了下&#xff0c;论文还是赶紧挺烂的&#xff0c;但比国赛又有进步&#xff08;说起国赛又不得不抱怨了&#xff0c;基本其余省份都发了&#xff0c;但江西......哎&#xff09;。哎&…

网络编程、UDP、TCP、三次握手、四次挥手

一、初识网络编程 网络编程的概念&#xff1a;在网络通信协议下&#xff0c;不同计算机上运行的程序&#xff0c;进行的数据传输。 应用场景&#xff1a;即时通信、网游对战、金融证券、国际贸易、邮件等等。 不管是什么场景&#xff0c;都是计算机和计算机之间通过网络进行…

在 Jupyter Notebook 中使用 Matplotlib 进行交互式可视化的教程

在 Jupyter Notebook 中使用 Matplotlib 进行交互式可视化的教程 引言 数据可视化是数据分析的重要组成部分&#xff0c;能够帮助我们更直观地理解数据。Matplotlib 是 Python 中最流行的绘图库之一&#xff0c;而 Jupyter Notebook 则是进行数据分析和可视化的理想环境。本文…

[单例模式]

[设计模式] 设计模式是软件工程中的一种常见做法, 它可以理解为"模板", 是针对一些常见的特定场景, 给出的一些比较好的固定的解决方案. 不同语言适用的设计模式是不一样的. 这里我们接下来要谈到的是java中典型的设计模式. 而且由于设计模式比较适合有一定编程经…