顺序显示多个弹窗方案-责任链模式

在这里插入图片描述

在应用中,用户进入应用首页以后经常会遇到顺序弹出多个弹窗的业务。例如进入首页以后,首先弹出运营活动弹窗,然后弹出应用评分或反馈弹窗,最后弹出应用更新弹窗等等。于是想到封装一套代码可以控制弹窗流程执行。

DialogChainOwner

弹窗链持有者。负责弹窗链节点的加入和展示入口。

/**
 * 弹窗链拥有者,用于控制链节点的添加和展示以及取消,销毁
 */
public class DialogChainOwner implements LifecycleEventObserver {

    private List<ChainNode> chainNodes = new ArrayList<>();  //链节点列表

    private Set<Integer> cancelledIds;  //取消执行的链节点集合

    private boolean isDestroyed = false;  //是否销毁

    public DialogChainOwner(@NonNull LifecycleOwner owner) {
        owner.getLifecycle().addObserver(this);
    }

    @Override
    public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
        if (event == Lifecycle.Event.ON_DESTROY) {
            destroy();
        }
    }

    /**
     * 加入链节点
     *
     * @param chainNode
     * @return
     */
    public DialogChainOwner join(ChainNode chainNode) {
        chainNodes.add(chainNode);
        return this;
    }

    //开始展示
    public void show() {
        DialogChain chain = new DialogChain(this, chainNodes, 0);
        chain.proceed();
    }

    //销毁
    public void destroy() {
        isDestroyed = true;
        chainNodes.clear();
        if (cancelledIds != null) {
            cancelledIds.clear();
        }
    }

    /**
     * 取消链节点
     *
     * @param id
     */
    public void cancel(int id) {
        if (cancelledIds == null) {
            cancelledIds = new HashSet<>();
        }

        cancelledIds.add(id);
    }

    boolean isCancelled(int id) {
        return cancelledIds != null && cancelledIds.contains(id);
    }

    boolean isDestroyed() {
        return isDestroyed;
    }
}

DialogChain

负责取对应的节点执行操作。

final class DialogChain {

    private DialogChainOwner chainOwner;
    private List<ChainNode> chainNodes;

    private final int index;

    DialogChain(DialogChainOwner owner, List<ChainNode> chainNodes, int index) {
        this.chainOwner = owner;
        this.chainNodes = chainNodes;
        this.index = index;
    }

    final void proceed() {
        if (chainOwner != null && chainOwner.isDestroyed()) {
            destroy();
            return;
        }

        if (chainNodes == null || index >= chainNodes.size()) return;

        DialogChain next = new DialogChain(chainOwner, chainNodes, index + 1);
        ChainNode chainNode = chainNodes.get(index);
        if (chainOwner != null && chainOwner.isCancelled(chainNode.getId())) {
            next.proceed();
        } else {
            chainNode.proceed(next);
        }
    }

    void destroy() {
        chainOwner = null;
        chainNodes.clear();
    }
}

ChainNode

链节点接口。弹窗实现该接口执行操作。

public interface ChainNode {
    /**
     * 返回链节点对于的Id
     *
     * @return
     */
    int getId();

    /**
     * 处理链节点
     *
     * @param next 下一个弹窗链节点
     */
    void proceed(DialogChain next);
}

示例

首先需要弹出的弹窗实现对应的链节点接口。

public class CustomDialog extends Dialog implements ChainNode {

    private String content;

    public CustomDialog(@NonNull Context context) {
        super(context, R.style.CustomDialog);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.dialog_custom);

        TextView textView = findViewById(R.id.content);
        textView.setText(content);
    }

    public void setContent(String content) {
        this.content = content;
    }

    @Override
    public int getId() {
        return 0;
    }

    @Override
    public void proceed(DialogChain chain) {
        show();
        setOnDismissListener(dialog -> chain.proceed());
    }
}

然后,依次加入对应的链接口并弹出。

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        DialogChainOwner owner = new DialogChainOwner(this);
        CustomDialog dialog1 = new CustomDialog(this);
        dialog1.setContent("第一个弹窗");
        CustomDialog dialog2 = new CustomDialog(this);
        dialog2.setContent("第二个弹窗");
        CustomDialog dialog3 = new CustomDialog(this);
        dialog3.setContent("第三个弹窗");
        CustomDialog dialog4 = new CustomDialog(this);
        dialog4.setContent("第四个弹窗");
        owner.join(dialog1).join(dialog2).join(dialog3).join(dialog4).show();
    }
}

感谢大家的支持,如有错误请指正,如需转载请标明原文出处!

### 关于 Element-UI 中 `el-dialog` 组件闪烁不显示问题的解决方案 在 Vue 项目中使用 Element-UI 的 `el-dialog` 组件时,如果遇到弹窗短暂闪烁后消失的情况,通常是由以下几个原因引起的: #### 原因分析 1. **数据绑定延迟** 如果 `el-dialog` 的可见性属性(如 `v-model` 或 `:visible.sync`)未及时更新,可能会导致组件渲染异常。这可能是因为父组件的数据尚未完全加载完成或者存在异步操作[^1]。 2. **样式冲突** 页面中存在的其他 CSS 样式可能导致 `el-dialog` 渲染位置错误或被隐藏。例如,某些全局样式的覆盖会影响其默认行为[^3]。 3. **遮罩层配置不当** 当多个对话框嵌套时,如果没有正确处理遮罩层逻辑,也可能引发类似的视觉问题[^2]。 4. **动态挂载问题** 默认情况下,`el-dialog` 是挂载到当前 DOM 节点下的子节点上。如果页面结构复杂,可能存在重新计算布局的时间差,从而引起闪烁现象。 --- #### 解决方案 以下是几种常见的解决方法及其适用场景: ##### 方法一:调整 `async` 数据初始化顺序 确保用于控制 `el-dialog` 可见性的变量(如 `dialogVisible`),在其依赖的数据准备好后再切换状态。可以通过监听事件或其他方式延缓触发时间。 ```javascript // 示例代码 data() { return { dialogVisible: false, loadingData: null, // 需要等待加载的数据 }; }, methods: { async showDialog() { this.loadingData = await fetchData(); // 模拟异步请求 this.dialogVisible = true; // 加载完成后才打开窗口 } } ``` ##### 方法二:强制指定挂载目标 通过设置 `append-to-body=true` 参数,让 `el-dialog` 直接附加至 `<body>` 下面而非局部容器内部。这样可以减少由于上下文变化带来的重绘风险。 ```html <el-dialog :visible.sync="dialogVisible" append-to-body> </el-dialog> ``` ##### 方法三:优化动画效果 有时,默认过渡动画会加剧这种瞬态效应。尝试自定义 transition 名字并简化过程来缓解该情况: ```css /* 自定义CSS类名 */ .custom-transition-enter-active, .custom-transition-leave-active { opacity: 1; } .custom-transition-enter, .custom-transition-leave-to { opacity: 0; } ``` 配合 HTML 使用: ```html <el-dialog :visible.sync="dialogVisible" custom-class="custom-transition"> </el-dialog> ``` ##### 方法四:修正潜在样式干扰 检查是否有外部规则影响到了 `.el-dialog` 容器的表现形式。比如高度宽度设定不合理、flexbox 属性错配等问题均需逐一排查。 ##### 方法五:启用调试模式验证生命周期钩子执行流程 利用浏览器开发者工具观察具体时间节点上的 DOM 结构变动轨迹,并结合 Vue DevTools 查看实例的状态迁移路径是否存在异常状况。 --- ### 总结 针对上述提到的各种可能性采取相应措施即可有效改善 `el-dialog` 出现的一闪即逝的现象。实际开发过程中应综合考虑业务需求以及框架特性灵活运用这些技巧。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值