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

背景

项目需要在首页弹一系列弹窗,每个弹窗是否弹出都有自己的策略,以及哪个优先弹出,哪个在上一个关闭后再弹出,为了更好管理,于是封装了一个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@NonNullpublic String toString() {return "INIT";}}public static final class PROGRESS extends State {private PROGRESS() {super();}@Override@NonNullpublic String toString() {return "PROGRESS";}}public static final class COMPLETE extends State {private COMPLETE() {super();}@Override@NonNullpublic String toString() {return "COMPLETE";}}public static final class ERROR extends State {private final Throwable mThrowable;public ERROR(@NonNull Throwable exception) {super();mThrowable = exception;}@NonNullpublic Throwable getThrowable() {return mThrowable;}@Override@NonNullpublic 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);}@Overridepublic void complete() {setState(Operation.COMPLETE);if (callBack != null) {callBack.onComplete();}}@Overridepublic 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;}@Overridepublic int getId() {return id;}public void setId(int id) {this.id = id;}@Overridepublic 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;}@Overridepublic 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? = nullfun 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() {@Overridepublic void onComplete() {nextNode(index);}@Overridepublic 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] = chainProcessorreturn 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.rhkb.cn/news/467675.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

掌握软件组件/单元测试中的这些术语,你就算正式入门了

上篇干货&#xff0c;和大家分享了软件测试的几个级别&#xff0c;在【组件/单元测试】当中&#xff0c;涉及不少名词术语。从之前的学员学习过程来看&#xff0c;这里比较容易出现概念混乱&#xff0c;进而导致面试过程中频频翻车&#xff0c;所以有必要在这里单独拎出来和大家…

html的week控件 获取周(星期)的第一天(周一)和最后一天(周日)

html的week控件 获取周(星期)的第一天(周一)和最后一天(周日) <input type"week" id"week" class"my-css" value"ViewBag.DefaultWeek" /><script> function PageList() { var dateStrin…

【主机游戏】艾尔登法环游戏攻略

艾尔登法环&#xff0c;作为一款备受好评但优化问题频发的游戏&#xff0c;就连马斯克都夸过 今天介绍一下这款游戏 https://pan.quark.cn/s/24760186ac0b 角色升级 在《艾尔登法环》中&#xff0c;角色升级需要找到梅琳娜。你可以在关卡前废墟的营地附近&#xff0c;风暴关…

CSS 中三角形的绘制方法详解

在网页设计领域&#xff0c;特殊形状常常能为页面增添独特的视觉效果&#xff0c;三角形便是其中之一。本文将详细介绍如何利用 CSS 绘制三角形。 一、原理阐述 CSS 中一个元素的边框分为上边框、右边框、下边框和左边框。当把一个元素的宽度和高度设为 0&#xff0c;且只让其…

虚拟机linux7.9下安装mysql

1.MySQL官网下载安装包&#xff1a; MySQL :: Download MySQL Community Server https://cdn.mysql.com/archives/mysql-5.7/mysql-5.7.39-linux-glibc2.12-x86_64.tar.gz 2.解压文件&#xff1a; #tar xvzf mysql-5.7.39-linux-glibc2.12-x86_64.tar.gz 3.移动文件&#…

负载均衡式在线oj项目开发文档(个人项目)

项目目标 需要使用的技术栈&#xff1a; 这个项目共分成三个模块第一个模块为公共的模块&#xff0c;用于解决字符串处理&#xff0c;文件操作&#xff0c;网络连接等等的问题。 第二个模块是一个编译运行的模块&#xff0c;这个模块的主要功能就是将用户的代码收集上来之后要…

MySQL数据库专栏(五)连接MySQL数据库C API篇

摘要 本篇文章主要介绍通过C语言API接口链接MySQL数据库&#xff0c;各接口功能及使用方式&#xff0c;辅助类的封装及调用实例&#xff0c;可以直接移植到项目里面使用。 目录 1、环境配置 1.1、添加头文件 1.2、添加库目录 2、接口介绍 2.1、MySql初始化及数据清理 2.1.…

PH热榜 | 2024-11-08

DevNow 是一个精简的开源技术博客项目模版&#xff0c;支持 Vercel 一键部署&#xff0c;支持评论、搜索等功能&#xff0c;欢迎大家体验。 在线预览 1. Quorini 标语&#xff1a;几分钟内设计并运行无服务器云 API 介绍&#xff1a;Quorini 提供了一套可视化的工具&#xff…

QML:Menu详细使用方法

目录 一.性质 二.作用 三.方法 四.使用 1.改变标签 2.打开本地文件 3.退出程序 4.打开Dialog 五.效果 六.代码 在 QML 中&#xff0c;Menu 是一个用于创建下拉菜单或上下文菜单的控件。它通常由多个 MenuItem 组成&#xff0c;每个 MenuItem 可以包含文本、图标和快捷…

k8s 处理namespace删除一直处于Terminating —— 筑梦之路

问题现象 k8s集群要清理某个名空间&#xff0c;把该名空间下的资源全部删除后&#xff0c;删除名空间&#xff0c;一直处于Terminating状态&#xff0c;无法完全清理掉。 如何处理 为什么要记录下这个处理的步骤&#xff0c;经过查询资料&#xff0c;网上也有各种各样的方法&…

>>,<<,~,,|,∧

‌监视器中的数值在十六进制显示时没有负数&#xff0c;主要是因为十六进制本身不直接表示负数&#xff0c;而是通过补码的形式来表示。

【韩老师零基础30天学会Java 】03章 变量

第三章 变量 1. 变量介绍 为什么需要变量&#xff1f; 变量是程序的基本组成单位 变量有三个基本单位&#xff1a;类型名称值 //1.定义变量int age 20;double score88.6;char gender男;String namejack;变量使用注意事项 变量表示内存中的一个存储区域[不同的变量,类型不同&am…

扭蛋机小程序开发,潮玩扭蛋机市场下新机遇

随着大众对潮玩文化的需求不断增长&#xff0c;市场进行了创新升级&#xff0c;不再局限于传统的销售营销模式&#xff0c;进一步推动行业的发展。目前&#xff0c;扭蛋机的种类越来越丰富&#xff0c;从手办、玩具到各种IP周边等&#xff0c;为市场带来更多新颖的扭蛋商品。销…

Unity 实现数字垂直滚动效果

Unity 实现数字垂直滚动效果 前言项目场景布置Shader代码编写材质球设置代码编写数字图片 前言 遇到一个需要数字垂直滚动模拟老虎机的效果&#xff0c;记录一下。 项目 场景布置 3个Image换上带有RollNumberShader的材质 在RollNumberScript脚本中引用即可 Shader代码编…

记录解决vscode 登录leetcode中遇到的问题

1. 安装完 leetcode 点击sign in to leetcode 点击打开网站登录leetcode&#xff0c;发现网页无法打开。 解决办法&#xff1a;将leetcode.cn.js文件中的leetcode-cn.com路径都改成leetcode.cn 2. 继续点击 sign in to leetcode &#xff0c;选择使用账号登录&#xff0c;始…

设计模式之适配器模式(从多个MQ消息体中,抽取指定字段值场景)

前言 工作到3年左右很大一部分程序员都想提升自己的技术栈&#xff0c;开始尝试去阅读一些源码&#xff0c;例如Spring、Mybaits、Dubbo等&#xff0c;但读着读着发现越来越难懂&#xff0c;一会从这过来一会跑到那去。甚至怀疑自己技术太差&#xff0c;慢慢也就不愿意再触碰这…

万字长文解读深度学习——循环神经网络RNN、LSTM、GRU、Bi-RNN

推荐阅读&#xff1a; 深度学习知识点全面总结 如何从RNN起步&#xff0c;一步一步通俗理解LSTM 深度学习之RNN(循环神经网络) 循环神经网络&#xff08;RNN与LSTM&#xff09; 文章目录 &#x1f33a;深度学习面试八股汇总&#x1f33a;文本特征提取的方法1. 基础方法1.1 词袋…

Qt 使用QTreeView显示并动态的增删改查JSON文件数据

文章目录 效果图概述部分代码总结 效果图 概述 本案例在此开源项目QJsonModel的基础上实现&#xff0c;动态的生成并操作JSON数据&#xff0c;QJsonModel是一个基于QAbstractItemModel的JSON数据模型&#xff0c;它提供了一种简单的方式来将JSON数据可视化&#xff0c;功能简单…

基于Springboot+Vue的游乐园管理系统 (含源码数据库)

1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 该系统…

漫谈MCU优化:从硬件设计优化到可靠性挑战

1.关于MCU 微控制器&#xff08;Microcontroller Unit, MCU&#xff09;&#xff0c;是以微处理器为基础&#xff0c;加上存储器以及计数器、I2C、UART等外设模块与接口电路整合的单芯片微型计算机。 ▲MCU实物图 MCU拥有性能好、可编程、灵活度高、功耗低等优点&#xff0c;…