红点系统的设计

项目中红点是自己手动添加的,并且手动管理该红点的显示和隐藏,会不利于修改和调整。 目前的想法是:在GroupButton组件中新增一个方法和属性,用于控制红点的显示和隐藏。 一、红点的显示和隐藏

private redDotImg: eui.Image;
/** 红点的显示和隐藏 */
public get redDot(): boolean {
    return !!this.redDotImg;
}
public set redDot(visible: boolean) {
    if (visible && !this.redDot) {
        let img = this.redDotImg = new eui.Image(RES.getRes("common_ui_json.unread"));
        this.addChild(img);
        img.right = img.top = 0;// 位置还需在调整
    }
    this.redDotImg && (this.redDotImg.visible = visible)
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14

后续只需设置redDot的属性,即可展现和隐藏红点。 例:

btn.width=100;
btn.height=100;
this.addChild(btn);
// 设置红点
btn.redDot=true;

1
2
3
4
5
6

也可以直接在egret.DisplayObject直接添加该属性,来满足所有的DisplayObject都可以添加红点。

",{ line-numbers-mode">
    get: function () {
        return !!this.redDotImg;
    },
    set: function (value) {
        let visible = value && this.visible;
        if (value && !this.redDot) {
            let img = this.redDotImg = new eui.Image(RES.getRes("common_ui_json.unread"));
            this.addChild(img);
            img.right = img.top = 0;// 位置还需在调整
        }
        this.redDotImg && (this.redDotImg.visible = visible)
    },
})
declare namespace egret{
    interface DisplayObject{
        redDot:boolean;
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

但项目中,红点基本全在GroupButton里,所以只用第一种就可以了。 二、红点的管理 1、红点类别 目前项目中的红点,是每个页面单独写自己的,就会造成代码无法复用,在大厅用到的红点,在捕鱼界面中还得写一份,除非写成静态的或者存起来。且父级与子级之间管理不方便。 在参考了网上一些资料后,如下思路: 红点首先是这样的,例:首页福利入口=>新手任务=>累计登录 ,累计登录如果有红点,则对应的父级也需要有红点。 也可以一对多,首页活动界面=>累计登录。 红点分两种,一种为数量红点,一种为纯红点。 a.数量红点 顾名思义,要在红点上标识有几个红点构成,例:邮件中的红点可以用有几封来表示。

b.纯红点 在有可领取或其他情况时,只需展现红点即可。

2.初步想法: 建一个结构,每个node里面应有父节点列表,子节点列表,对应的groupButton列表。及红点数量。

     /** 该红点关联的btn */
    private btns: GroupButton[];
    /** 子节点名字集合 */
    private ChildDict: string[];
    /** 父节点名字集合 */
    private parentDict: string[];
    /** 在红点数量变为0或整数时 更改btns里红点的展现*/
    public dotNum: number = 0;
    /**
     * 修改父节点的红点数量
     * @param diff 自身红点变幻数量
     */
    updateParent(diff:number):void;
    /** 添加节点名字至父节点*/
    setParent(parentName: string):void;
    /** 添加节点名字至子节点*/
    setChild(childName: string):void;
    /**  添加btn至btns*/
    setBtn(btn: GroupButton):void;
    /**  从btns移除btn*/
    setBtn(btn: GroupButton):void;
    /** 设置该节点 所有btn红点的展现和隐藏 */
    showBtnRed(bool: boolean):void;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

然后各个node应形成这样的结构 不支持在 Docs 外粘贴 block 3.代码实现 建立node结构:

    /** 红点数量 */
    private _dotNum: number = 0;
    /** 该红点关联的btn */
    private btns: GroupButton[] = [];
    /** 子节点集合 */
    private ChildDict: string[]=[];
    /** 父节点名字集合 */
    private parentDict: string[]=[];
    public get dotNum(): number {
        return this._dotNum;
    }
    /** 设置红点数量 */
    public set dotNum(value: number) {
        if (value === this._dotNum) {
            return;
        }
        // 如果原红点数量为正,现在为0 或原红点数量为0,现在为正
        // 需进行红点的展现和隐藏
        if (value * this._dotNum == 0) {
            this.showBtnRed(value > 0);
        }
        // 计算出该节点红点数量的差
        let diff = value - this._dotNum;
        this._dotNum = value;
        // 更改上级红点数量
        if (this.parentDict.length > 0) {
            this.updateParent(diff)
        }
    }
    constructor(/** type名称为不含. 和| 的实际名称 */public nodeName: string) {
    }
    /**
     * 修改父节点的红点数量
     * @param diff 自身红点变幻数量
     */
    updateParent(diff: number) {
        for (let item of this.parentDict) {
            let parent = RedDotSystem.getIns().getTypeNode(item)
            if(parent) parent.dotNum += diff;
        }
    }
    /**
     * 添加节点名字至父节点
     * @param parentName 节点名字
     * @returns 
     */
    setParent(parentName: string) {
        if (this.parentDict.indexOf(parentName) >= 0) return;
        this.parentDict.push(parentName);
    }
    /**
     * 添加节点名字至子节点
     * @param childName节点名字
     * @returns 
     */
    setChild(childName: string) {
        if (this.ChildDict.indexOf(childName) >= 0) return;
        this.ChildDict.push(childName);
    }
    /**  添加监听btn*/
    setBtn(btn: GroupButton) {
        if (this.btns.indexOf(btn) >= 0) return;
        this.btns.push(btn);
        btn.redDot = this.dotNum > 0;
        // 获取btn所在的baseui;
        let Baseui = this.getBtnParentBaseUI(btn);
        // 等待baseui被移除显示舞台时  将btn移除引用
        if (Baseui) Baseui.waitRemove().then(() => { this.offBtn(btn) })
    }
    /** 移除btn引用 */
    offBtn(btn: GroupButton) {
        for (let i = 0; i < this.btns.length; i++) {
            let redBtn: GroupButton = this.btns[i];
            if (redBtn == btn) {
                this.btns.splice(i, 1);
                return;
            }
        }
    }
    /** 设置该节点 所有btn红点的展现和隐藏 */
    showBtnRed(bool: boolean) {
        for (let item of this.btns) {
            item.redDot = bool;
        }
    }
    getChildLength() {
        return this.ChildDict.length;
    }
    /**
     * 获取该node一个baseUi的父级
     * @param node 
     * @returns 
     */
    getBtnParentBaseUI(node: egret.DisplayObject) {
        while (node) {
            node = node.parent;
            if (!node) return;
            if (node instanceof BaseUI) {
                return node;
            }
        }
        return null;
    }
}

涉及到的BaseUI的waitRemove方法
/** 页面被移除 promise */
private awaitPromiseFun: Promise < BaseUI > = null;
/** 异步 等待界面将要被移除显示舞台时返回自身 */
public waitRemove() {
    if (!this.awaitPromiseFun) {
        this.awaitPromiseFun = new Promise(resolve => {
            this.addEvent(this, egret.Event.REMOVED_FROM_STAGE, () => {
                resolve(this);
            }, this);
        });
    };
    return this.awaitPromiseFun;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120

建立枚举类:

 *   没父节点直接写,有父节点需中以.为分隔符,例:Activity.SevenDay 多个父级中用|代替
 *   tab1="tab1",
 *   tab2="tab2",
 *   text2="tab1.text2|tab2.text2",
 *   注意:禁止套娃   text="tab.text.tab.text"
 */
export enum RedDotType {
    Activity = "Activity",
    FireGrow = "Activity.FireGrow|GameActivity.FireGrow",
    SevenDay = "Activity.SevenDay",
    FirstDay = "Activity.SevenDay.FirstDay",
    GameActivity = "GameActivity"
}

RedDotSystem红点系统管理类;
export class RedDotSystem {
    private btnNodes: Dictionary<string, RedDotNode> = new Dictionary();
    public getTypeNode(type: string): RedDotNode {
        return this.btnNodes.getValue(type)
    }
    /**
     * 检查该type是否存在,不存在则进行添加
     * 如果有父级添加父级,并让父级添加子级;
     * @param type 
     * @param node 
     * @returns 
     */
    private checkValue(type: string, node: RedDotNode) {
        let value: RedDotNode;
        if (this.btnNodes.containsKey(type)) {
            value = this.getTypeNode(type);
        } else {
            value = new RedDotNode(type)
            this.btnNodes.put(type, value);
        }
        if (node) {
            value.setParent(node.nodeName)
            node.setChild(type);
        }
        return value;
    }
    /**
     * 获取该type对应的node,没有则进行创建
     * @param types 
     * @returns 
     */
    private getNodeByType(types: RedDotType): RedDotNode {
        let list = types.split("|");
        for (let type of list) {
            let typeList = type.split(".");
            let node: RedDotNode;
            for (let item of typeList) {
                node = this.checkValue(item, node);
            }
        }
        let type = this.getTypeSelf(types)
        return this.getTypeNode(type);
    }
    /** 获取RedDotType中自己的type */
    private getTypeSelf(type: RedDotType) {
        let all = type.split("|");
        let types = all[0].split(".");
        return types[types.length - 1];
    }

    private static _ins: RedDotSystem = null
    public static getIns(): RedDotSystem {
        if (!this._ins) {
            this._ins = new RedDotSystem();
            this._ins.initRedDot();
        }
        return this._ins;
    }
    /**
     * 设置红点的数量 只允许设置叶节点红点数量
     * @param type 红点类型
     * @param num 红点
     * @returns 
     */
    setTypeOn(type: RedDotType, num: number): void {
        let node: RedDotNode;
        let ty = this.getTypeSelf(type);
        if (this.btnNodes.containsKey(ty)) {
            node = this.getTypeNode(ty);
        } else {
            node = this.getNodeByType(type);
        }
        if (node.getChildLength() > 0) {
            /** 禁止拥有子节点的红点在外部设置红点数量 */
            return
        }
        node.dotNum = num;
    }
    /**
     * 添加type和btn的关联
     * 注意 不要将一个btn添加至多个type中
     * @param type 
     * @param btn 
     */
    on(type: RedDotType, btn: GroupButton) {
        let node: RedDotNode;
        let ty = this.getTypeSelf(type);
        if (this.btnNodes.containsKey(ty)) {
            node = this.getTypeNode(ty);
        } else {
            node = this.getNodeByType(type);
        }
        node.setBtn(btn);
    }
    /**
     * 取消type和btn的关联
     * @param type 
     * @param btn 
     * @returns 
     */
    off(type: RedDotType, btn: GroupButton) {
        let node = this.getTypeNode(this.getTypeSelf(type))
        if (node) node.offBtn(btn);
    }
    /** 获取type红点数量 */
    getTypeRedNum(types: RedDotType) {
        let type = this.getTypeSelf(types);
        let node = this.getTypeNode(type);
        return node ? node.dotNum : 0;
    }
    constructor() {}
    /** 初始化时红点刷新方法 */
    initRedDot() {

    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132

4.用法 下方是设置红点的例子: a.简单用法:

Activity = "Activity",
SevenDay = "Activity.SevenDay",

// 在RedDotSystem中加入方法,initRedDot调用
updateSevenDay(){
    let redNum=0;
    //伪代码
    //通过遍历可领取的奖励等一些参数 算出可操作的数量。
    //纯红点无数量要求的情况下 在算出一个可领取的奖励时,即可停止计算,
    // 设置红点
    RedDotSystem.setTypeOn(RedDotType.SevenDay, redNum);
}
// 在有红点需求的页面打开时 将btn与type绑定
RedDotSystem.getIns().on(RedDotType.Activity, this.btnActivity);
RedDotSystem.getIns().on(RedDotType.SevenDay, this.btnSevenDay);

// 在后续玩家升级或领取奖励后,收到涉及到成长基金服务器的消息。我们可以更新成长基金红点
RedDotSystem.getIns().updateSevenDay();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

b.复杂用法:

Activity = "Activity",
FireGrow = "Activity.FireGrow|GameActivity.FireGrow",
SevenDay = "Activity.SevenDay",
/** 捕鱼界面的活动*/
GameActivity = "GameActivity"

// 分别对两个type设置红点数量为1
RedDotSystem.setTypeOn(RedDotType.SevenDay,1)
RedDotSystem.setTypeOn(RedDotType.FireGrow,1);
 
// 将btn与type绑定
RedDotSystem.getIns().on(RedDotType.Activity, this.btnActivity);
RedDotSystem.getIns().on(RedDotType.FireGrow, this.btnFireGrow);
RedDotSystem.getIns().on(RedDotType.SevenDay, this.btnSevenDay);
RedDotSystem.getIns().on(RedDotType.GameActivity , this.GameActivity);

// 此时玩家升级或领取奖励后,收到涉及到成长基金服务器的消息。我们可以更新成长基金红点
RedDotSystem.setTypeOn(RedDotType.FireGrow,0);
// 此时btnFireGrow和GameActivity的红点会消失,因为对应的子级FireGrow红点为0;


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
上次更新: 2021/10/07, 09:53:27
最近更新
01
根据exml文件Id自动生成定义及接口
10-07
02
搜索Excel表里sheet名称
11-03
03
1447题-最简分数
10-28
更多文章>