简介
游戏中经常会需要红点来提醒玩家有新内容,每个部分单独控制红点显然不是个好办法,因此需要红点系统来统一管理红点。
原理
所有红点根据层级关系组织成红点树,每次有数据变化就从红点树的叶子结点开始向上更新。
红点树
属性
红点数有_children 来保存所有下属节点,有_parent 来回溯上游节点,并且每个节点都有_path 来记录红点在红点树中的路径。
_dotNum 表示当前红点节点的数据,_isShow 用来控制当前红点节点是否需要显示红点。
红点_type 分为两类,一类是 Eternal,只有红点数量归零的时候才不显示;一类是 Once,一旦红点数量变少就不再显示。
当红点数量发生变化的时候会调用_onRedDotChangedCallback 方法执行对应的操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| export enum RedDotType { Eternal, Once, }
export default class RedDot { private _parent: RedDot | null; private _children; public _path: string; private _dotNum; private _isShow; private _type: RedDotType;
private _onRedDotChangedCallback: Function | null;
}
|
方法
添加红点子节点
先获取需要添加的红点节点的路径,判断是否已添加。未添加的情况下我们设置当前红点节点的子节点为需要添加的节点,设置需要添加节点的父节点为当前节点,并且走一遍红点变化的逻辑,防止出现 bug。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
public addChild(child: RedDot) { let path = child._path; if (!this._children[path]) { this._children[path] = child; child._parent = this; if (child._isShow) { this._isShow = true; } this.changeDotNum(child.getDotNum()); } else { console.warn("红点节点重复添加:", path); } }
|
改变红点数量
首先我们保存当前节点的红点数量,然后根据传入的变化数量对当前节点红点数量进行改变。
改变之后会发生两种情况:
- 红点数量小于等于 0,这时候我们置红点为不显示。
- 红点数量大于 0,如果传入的变化量大于 0,则红点显示;否则根据红点类型判断,如果是 Once,则红点数量归零,并且置为不显示。
处理完当前节点的红点数量后,我们回溯父节点,对父节点进行同样的操作。不过传入的变化量需要根据当前节点是否显示红点来判断,如果是不显示红点,则我们传入负的之前保存的节点红点数量;显示红点,则传入该方法传入的参数。
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
|
public changeDotNum(num: number) { let defDotNum = this._dotNum; this._dotNum += num; if (this._dotNum <= 0) { this._dotNum = 0; this._isShow = false; } else { if(num > 0){ this._isShow = true; }else{ if(this._type == RedDotType.Once){ this._dotNum = 0; this._isShow = false; } } } this._onRedDotChangedCallback && this._onRedDotChangedCallback(this._isShow); this._parent && this._parent.changeDotNum(this._isShow ? num : -defDotNum); }
|
其他
基础功能,根据函数内容即可得知作用。
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
|
public getChildren(){ return this._children; }
public checkShow() { this._onRedDotChangedCallback && this._onRedDotChangedCallback(this._isShow); }
public getDotNum() { return this._dotNum; }
public onRedDotChanged(callback) { this._onRedDotChangedCallback = callback; }
public removeRedDotChange(){ this._onRedDotChangedCallback = null; }
|
红点管理类
管理类维护一个红点字典_redDotDic,用于快速查找对应路径的红点。
初始化
初始化一个 root 红点,该红点是所有红点的根节点。
1 2 3 4 5 6 7 8
|
public init() { let root = new RedDot(null, RedDotType.Eternal); root._path = "root"; this._redDotDic["root"] = root; }
|
注册红点
用于注册红点的方法。先判断红点是否已注册,未注册的情况下执行注册方法:
- 裁剪红点路径,根据”/“分开。
- 根据红点路径长度循环判断是否注册过红点,未注册则新建红点并保存到红点字典,红点路径每次也会根据红点层级拼接。如果新建的红点是最终路径,则返回该红点。
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
|
public register(path: string, type: RedDotType = RedDotType.Eternal): RedDot { let leaf = this.getRedDot(path);
if (!leaf) { let redDotPath = path.split("/"); let index = 0; let curDic = this._redDotDic["root"] as RedDot; let subPath = "";
while (index < redDotPath.length) { let child = curDic.getChildren()[redDotPath[index]]; subPath += (index == 0 ? "" : "/") + redDotPath[index]; if (!child) { let newRedDot = new RedDot(null, type); newRedDot._path = subPath; curDic.addChild(newRedDot); if (index == redDotPath.length - 1) { leaf = newRedDot; } child = newRedDot; this._redDotDic[subPath] = child; }
curDic = child; index++; } } return leaf; }
|
其他
基础功能,根据函数内容即可得知作用。
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
|
public changeDotNum(path, num) { let redDot = this.getRedDot(path); if (redDot) { redDot.changeDotNum(num); } else { console.warn("不存在该红点", path); } }
public setRedDotCallback(path,callback){ let redDot = this.getRedDot(path); if(redDot){ redDot.onRedDotChanged(callback); } }
public removeRedDotCallback(path){ let redDot = this.getRedDot(path); if(redDot){ redDot.removeRedDotChange(); } }
private getRedDot(path: string): RedDot { return this._redDotDic[path]; }
public getRoot() { return this._redDotDic["root"]; }
|
代码
RedDot1 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
| import { RedDotType } from "./RedDotManager";
export default class RedDot { private _parent: RedDot | null; private _children; public _path: string; private _dotNum; private _isShow; private _type: RedDotType;
private _onRedDotChangedCallback: Function | null;
public constructor(parent = null, type = RedDotType.Eternal) { this._parent = parent; this._children = {}; this._dotNum = 0; this._isShow = false; this._type = type; }
public addChild(child: RedDot) { let path = child._path; if (!this._children[path]) { this._children[path] = child; child._parent = this; if (child._isShow) { this._isShow = true; } this.changeDotNum(child.getDotNum()); } else { console.warn("红点节点重复添加:", path); } }
public getChildren() { return this._children; }
public checkShow() { this._onRedDotChangedCallback && this._onRedDotChangedCallback(this._isShow); }
public changeDotNum(num: number) { let defDotNum = this._dotNum; this._dotNum += num; if (this._dotNum <= 0) { this._dotNum = 0; this._isShow = false; } else { if (num > 0) { this._isShow = true; } else { if (this._type == RedDotType.Once) { this._dotNum = 0; this._isShow = false; } } } this._onRedDotChangedCallback && this._onRedDotChangedCallback(this._isShow); this._parent && this._parent.changeDotNum(this._isShow ? num : -defDotNum); }
public getDotNum() { return this._dotNum; }
public onRedDotChanged(callback) { this._onRedDotChangedCallback = callback; }
public removeRedDotChange() { this._onRedDotChangedCallback = null; } }
|
RedDotManager1 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
| import RedDot from "./RedDot"; export enum RedDotType { Eternal, Once, }
export default class RedDotManager { private static _instance: RedDotManager | null = null; private _redDotDic = {};
public static getInst(): RedDotManager { if (!this._instance) { this._instance = new RedDotManager(); } return this._instance; }
public init() { let root = new RedDot(null, RedDotType.Eternal); root._path = "root"; this._redDotDic["root"] = root; }
public register(path: string, type: RedDotType = RedDotType.Eternal): RedDot { let leaf = this.getRedDot(path);
if (!leaf) { let redDotPath = path.split("/"); let index = 0; let curDic = this._redDotDic["root"] as RedDot; let subPath = "";
while (index < redDotPath.length) { let child = curDic.getChildren()[redDotPath[index]]; subPath += (index == 0 ? "" : "/") + redDotPath[index]; if (!child) { let newRedDot = new RedDot(null, type); newRedDot._path = subPath; curDic.addChild(newRedDot); if (index == redDotPath.length - 1) { leaf = newRedDot; } child = newRedDot; this._redDotDic[subPath] = child; }
curDic = child; index++; } } return leaf; }
public changeDotNum(path, num) { let redDot = this.getRedDot(path); if (redDot) { redDot.changeDotNum(num); } else { console.warn("不存在该红点", path); } }
public setRedDotCallback(path, callback) { let redDot = this.getRedDot(path); if (redDot) { redDot.onRedDotChanged(callback); } }
public removeRedDotCallback(path) { let redDot = this.getRedDot(path); if (redDot) { redDot.removeRedDotChange(); } }
private getRedDot(path: string): RedDot { return this._redDotDic[path]; }
public getRoot() { return this._redDotDic["root"]; } }
|