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()
    }
}

完整代码点击下载

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

相关推荐
踢球的打工仔19 小时前
PHP面向对象(7)
android·开发语言·php
安卓理事人19 小时前
安卓socket
android
Yawesh_best1 天前
告别系统壁垒!WSL+cpolar 让跨平台开发效率翻倍
运维·服务器·数据库·笔记·web安全
Ccjf酷儿1 天前
操作系统 蒋炎岩 3.硬件视角的操作系统
笔记
习习.y1 天前
python笔记梳理以及一些题目整理
开发语言·笔记·python
安卓理事人1 天前
安卓LinkedBlockingQueue消息队列
android
在逃热干面1 天前
(笔记)自定义 systemd 服务
笔记
万能的小裴同学1 天前
Android M3U8视频播放器
android·音视频
DKPT1 天前
ZGC和G1收集器相比哪个更好?
java·jvm·笔记·学习·spring
q***57741 天前
MySql的慢查询(慢日志)
android·mysql·adb