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

背景

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

效果

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

原理分析

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

    String getTag();

    void complete();

    void error(ChainException e);
}
  1. 定义弹窗的状态
    INIT:初始创建
    PROGRESS:开始弹出
    COMPLETE:完成弹出到关闭的流程
    ERROR:弹出错误
kotlin 复制代码
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节点定义具体的状态切换
kotlin 复制代码
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方法控制弹窗是否要弹出,以及通知工具什么时候完成弹出到关闭的流程
kotlin 复制代码
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后递归取下一个节点,直到队列尾部
kotlin 复制代码
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,支持处理多个弹窗链
kotlin 复制代码
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验证

kotlin 复制代码
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"    )
        }
    }
}
kotlin 复制代码
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()
    }
}
kotlin 复制代码
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()
    }
}

完整代码点击下载

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

相关推荐
恋猫de小郭7 分钟前
2026 Flutter VS React Native ,同时在 AI 时代 VS Native 开发,你没见过的版本
android·前端·flutter
冬奇Lab1 小时前
PowerManagerService(上):电源状态与WakeLock管理
android·源码阅读
tingshuo29172 小时前
D006 【模板】并查集
笔记
BoomHe6 小时前
Now in Android 架构模式全面分析
android·android jetpack
二流小码农14 小时前
鸿蒙开发:上传一张参考图片便可实现页面功能
android·ios·harmonyos
鹏程十八少14 小时前
4.Android 30分钟手写一个简单版shadow, 从零理解shadow插件化零反射插件化原理
android·前端·面试
Kapaseker14 小时前
一杯美式搞定 Kotlin 空安全
android·kotlin
三少爷的鞋15 小时前
Android 协程时代,Handler 应该退休了吗?
android
tingshuo29171 天前
S001 【模板】从前缀函数到KMP应用 字符串匹配 字符串周期
笔记
火柴就是我1 天前
让我们实现一个更好看的内部阴影按钮
android·flutter