背景
项目需要在首页弹一系列弹窗,每个弹窗是否弹出都有自己的策略,以及哪个优先弹出,哪个在上一个关闭后再弹出,为了更好管理,于是封装了一个Dialog管理工具
效果
- 整体采用责任链模块设计,控制优先级及弹出策略
原理分析
- 每个弹窗都当做一个节点Node,抽象出一些公共接口
public interface Node {int getId();String getTag();void complete();void error(ChainException e);
}
- 定义弹窗的状态
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());}}}
}
- 为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);}
}
- 抽象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)
}
- 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;}}
}
- 工具外部接口,主要是构建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()}
}
完整代码点击下载
大家觉得不错的点个赞鼓励下哦,有任何建议欢迎留言,谢谢🌹