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

完整代码点击下载

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

相关推荐
冰帝海岸35 分钟前
01-spring security认证笔记
java·笔记·spring
小二·2 小时前
java基础面试题笔记(基础篇)
java·笔记·python
长亭外的少年4 小时前
Kotlin 编译失败问题及解决方案:从守护进程到 Gradle 配置
android·开发语言·kotlin
JIAY_WX4 小时前
kotlin
开发语言·kotlin
wusong9994 小时前
mongoDB回顾笔记(一)
数据库·笔记·mongodb
猫爪笔记5 小时前
前端:HTML (学习笔记)【1】
前端·笔记·学习·html
Resurgence035 小时前
【计组笔记】习题
笔记
pq113_65 小时前
ftdi_sio应用学习笔记 3 - GPIO
笔记·学习·ftdi_sio
爱米的前端小笔记6 小时前
前端八股自学笔记分享—页面布局(二)
前端·笔记·学习·面试·求职招聘
建群新人小猿6 小时前
会员等级经验问题
android·开发语言·前端·javascript·php