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.mzph.cn/bicheng/60108.shtml

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

相关文章

【SpringMVC】——Cookie和Session机制

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01; 希望本文内容能够帮助到你&#xff01;&#xff01; 目录 一&#xff1a;实践 1&#xff1a;获取URL中的参数 &#xff08;1&#xff09;PathVariable 2&…

ROS2humble版本使用colcon构建包

colcon与与catkin相比&#xff0c;没有 devel 目录。 创建工作空间 首先&#xff0c;创建一个目录 ( ros2_example_ws ) 来包含我们的工作区: mkdir -p ~/ros2_example_ws/src cd ~/ros2_example_ws 此时&#xff0c;工作区包含一个空目录 src : . └── src1 directory, …

MySQL查询数据被截断

说明&#xff1a;本文记录一个MySQL查询&#xff0c;返回数据被截断的问题&#xff1b; 场景 假设有个用户查询列表&#xff0c;查询条件中有个用户类型&#xff08;普通用户、大会员、黄金大会员、铂金大会员、至尊大会员&#xff09;&#xff0c;是个下拉列表&#xff0c;可…

华为云计算HCIE-Cloud Computing V3.0试验考试北京考场经验分享

北京试验考场 北京考场位置 1.试验考场地址 北京市海淀区北清路156号中关村环保科技示范园区M地块Q21楼 考试场选择北京&#xff0c;就是上面这个地址&#xff0c;在预约考试的时候会显示地址&#xff0c;另外在临近考试的时候也会给你发邮件&#xff0c;邮件内会提示你考试…

GDPU Android移动应用 Broadcast Receiver

聆听广播&#xff0c;跟着节拍吧。 计时器 新建一个名为PhoneStateMonitor的工程&#xff1b; 实现一个应用运行时长的计时器&#xff0c;并在界面上刷新计数器&#xff0c;要求包括&#xff1a; &#xff08;1&#xff09;在Layout中包含两个TextView控件&#xff0c;横向分…

数据库SQL——什么是实体-联系模型(E-R模型)?

目录 什么是实体-联系模型&#xff1f; 1.实体集 2.联系集 3.映射基数 一对一&#xff08;1:1&#xff09; 一对多&#xff08;1:n&#xff09; 多对一&#xff08;n:1&#xff09; 多对多&#xff08;m:n&#xff09; 全部参与&#xff1a; 4.主码 弱实体集&#xf…

共筑开源技术新篇章 | 2024 CCF中国开源大会盛大开幕

在这个技术革新日新月异的时代&#xff0c;开源精神如同点燃创新火焰的火种&#xff0c;照亮了无数技术探索者的征途。2024年11月9日&#xff0c;备受瞩目的2024 CCF中国开源大会在深圳这座充满活力的创新之城盛大开幕。这场开源领域的顶级盛事&#xff0c;以“湾区聚力 开源启…

[极客大挑战 2019]Secret File 1

[极客大挑战 2019]Secret File 1 审题 看到题目应该是一道简单的按照要求找flag的题目 知识点 跟着题目走 解题 一&#xff0c;查看源码 找到网站进入 点开发现 【注意它说没看清吗】 二&#xff0c;使用BP抓包试试 发现新出现了/action.php 抓到后放到Repeater中响应 得…

初识Electron 进程通信

概述 Electron chromium nodejs native API&#xff0c;也就是将node环境和浏览器环境整合到了一起&#xff0c;这样就构成了桌面端&#xff08;chromium负责渲染、node负责操作系统API等&#xff09; 流程模型 预加载脚本&#xff1a;运行在浏览器环境下&#xff0c;但是…

语义分割实战——基于DeepLabv3+神经网络头发分割系统源码

第一步&#xff1a;准备数据 头发分割数据&#xff0c;总共有1050张图片&#xff0c;里面的像素值为0和1&#xff0c;所以看起来全部是黑的&#xff0c;不影响使用 第二步&#xff1a;搭建模型 DeepLabV3的网络结构如下图所示&#xff0c;主要为Encoder-Decoder结构。其中&am…

c# 开发web服务 webserver

024-11-10<<<<<<<<<<<<<<<<<<<<<<<<<< 开始插件前Cyber_CallWeb acajax_dac_database_viewer 2024-11-10<<<<<<<<<<<<<<<<<<<<…

「C/C++」C/C++ 预处理 之 常用预处理宏

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「C/C」C/C程序设计&#x1f4da;全部专栏「VS」Visual Studio「C/C」C/C程序设计「UG/NX」BlockUI集合「Win」Windows程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「PK」Parasoli…

Javascript中如何实现函数缓存?函数缓存有哪些应用场景?

#一、是什么 函数缓存&#xff0c;就是将函数运算过的结果进行缓存 本质上就是用空间&#xff08;缓存存储&#xff09;换时间&#xff08;计算过程&#xff09; 常用于缓存数据计算结果和缓存对象 解释 const add (a,b) > ab; const calc memoize(add); // 函数缓存…

Maven的依赖管理、传递、冲突、父子工程的继承和聚合

目录 一、基于IDEA 进行Maven依赖管理 (一)依赖管理概念 (二)Maven工程核心信息配置和解读&#xff08;GAVP&#xff09; (三)Maven工程依赖管理配置 1.依赖管理和依赖添加 2.依赖版本统一提取和维护 (四)依赖范围 (五)Maven工程依赖下载失败错误解决&#xff08;重点…

iOS SmartCodable 替换 HandyJSON 适配记录

前言 HandyJSON群里说建议不要再使用HandyJSON&#xff0c;我最终选择了SmartCodable 来替换&#xff0c;原因如下&#xff1a; 首先按照 SmartCodable 官方教程替换 大概要替换的内容如图&#xff1a; 详细的替换教程请前往&#xff1a;使用SmartCodable 平替 HandyJSON …

1.2 图像处理基本操作

在本实战中&#xff0c;我们将学习如何使用OpenCV进行基本的图像处理操作。首先&#xff0c;我们将通过cv2.imread()函数读取图像&#xff0c;并使用cv2.imshow()在窗口中显示它。接着&#xff0c;我们将探索如何通过cv2.imwrite()保存图像&#xff0c;并设置不同的参数以控制图…

使用Python实现音频降噪

在音频处理领域&#xff0c;背景噪声是一个常见的问题。为了提高音频的质量&#xff0c;我们需要对音频进行降噪处理。本文将介绍如何使用 Python 实现音频降噪。 依赖库安装 在开始之前&#xff0c;我们需要安装以下依赖库&#xff1a; pydub&#xff1a;用于音频文件的读取…

与AMD GPU上的对比语言-图像预训练(CLIP)模型交互

Interacting with Contrastive Language-Image Pre-Training (CLIP) model on AMD GPU — ROCm Blogs 2024年4月16日&#xff0c;由Sean Song撰写. 引言 对比语言-图像预训练&#xff08;CLIP&#xff09;是一种多模态深度学习模型&#xff0c;连接视觉和自然语言。它在Open…

2024年第四届“网鼎杯”网络安全比赛---朱雀组Crypto- WriteUp

2024年第四届“网鼎杯”网络安全比赛---朱雀组Crypto-WriteUp Crypto&#xff1a;Crypto-2&#xff1a;Crypto-3&#xff1a; 前言&#xff1a;本次比赛已经结束&#xff0c;用于赛后复现&#xff0c;欢迎大家交流学习&#xff01; Crypto&#xff1a; Crypto-2&#xff1a; …

下载mysql的jar,添加至jmeter中,编写jdbc协议脚本1106

下载jar包&#xff1a; 步骤1&#xff1a;进入maven仓库官网https://mvnrepository.com/ 步骤2&#xff1a;搜索实际的数据库 步骤3&#xff1a;点击 Mysql connnector/J 步骤5、查看数据库的版本号&#xff0c;选择具体版本&#xff0c;我的是mysql 8.0.16,下图&#xff0c;…