游戏里的邮件、私聊、成就等功能通常都需要红点提示。MyFramework 使用树形 RedPoint 组织红点状态,子节点变化后会自动向上刷新父节点。
项目地址:
一、本篇目标
创建下面的红点结构:
bash
主菜单红点
├── 邮件红点
├── 私聊红点
└── 成就红点
邮件下面还可以继续挂每一封邮件的红点:
bash
邮件红点
├── 邮件1
├── 邮件2
└── 邮件3
叶节点负责判断具体业务状态,父节点只负责汇总子节点:
bash
任意一封邮件未读
↓
邮件按钮显示红点
↓
主菜单按钮显示红点
二、创建项目红点树
创建 RedPointManager:
bash
using static FrameBaseHotFix;
public class RedPointManager : FrameSystem
{
public static RedPoint mMain;
public static RedPoint mMainMail;
public static RedPoint mMainPrivateChat;
public static RedPoint mMainAchivement;
public override void init()
{
base.init();
mRedPointSystem.createRedPoint(out mMain);
mRedPointSystem.createRedPoint(out mMainMail, mMain);
mRedPointSystem.createRedPoint(out mMainPrivateChat, mMain);
mRedPointSystem.createRedPoint(out mMainAchivement, mMain);
}
}
这里创建的都是普通 RedPoint。
普通节点不判断业务数据,只要任意子节点显示,它就会自动显示。
三、注册 RedPointManager
在 GameHotFix.initFrameSystem() 中注册:
bash
protected override void initFrameSystem()
{
registeFrameSystem<RedPointManager>((com) =>
{
mRedPointManager = com;
});
}
RedPointManager 应尽早创建红点树,确保邮件、私聊等业务数据初始化时能够找到对应父节点。
四、创建邮件叶节点
具体业务红点需要继承 RedPoint:
bash
public class RedPointMail : RedPoint
{
protected Mail mMail;
protected override void initEventType()
{
base.initEventType();
addEvent<EventUnreadMailChanged>();
}
public override void resetProperty()
{
base.resetProperty();
mMail = null;
}
public void setMail(Mail mail)
{
mMail = mail;
}
public override void refresh()
{
setEnable(!mMail.isRead());
}
}
这个红点只关心一件事:
bash
邮件未读 显示红点
邮件已读 隐藏红点
initEventType() 中注册了 EventUnreadMailChanged。收到事件以后,红点会被标记为需要刷新。
五、给每封邮件创建红点
邮件数据创建时,同时创建对应的红点:
bash
using static FrameBaseHotFix;
using static RedPointManager;
public class Mail : ClassObject
{
protected RedPointMail mRedPoint;
protected bool mHasRead;
public void init()
{
mRedPointSystem
.createRedPoint<RedPointMail>(mMainMail)
.setMail(this);
}
public override void destroy()
{
mRedPointSystem?.destroyRedPoint(mRedPoint);
mRedPoint = null;
base.destroy();
}
public void setRead(bool read)
{
mHasRead = read;
}
public bool isRead()
{
return mHasRead;
}
public RedPointMail getRedPoint()
{
return mRedPoint;
}
}
为了保存创建出来的红点,代码应写成:
bash
public void init()
{
mRedPointSystem
.createRedPoint(out mRedPoint, mMainMail)
.setMail(this);
}
邮件销毁时,红点也要一起销毁。销毁叶节点后,框架会自动重新计算父节点状态。
六、邮件状态变化时发送事件
读取邮件后发送事件:
bash
using static FrameBaseHotFix;
public class CmdGlobalReadMail
{
public static void execute(Mail mail)
{
if (mail.isRead())
{
return;
}
mail.setRead(true);
mEventSystem.pushEvent<EventUnreadMailChanged>();
CSMailRead.send(mail.getInstanceID());
}
}
事件本身不需要携带具体邮件:
bash
public class EventUnreadMailChanged : GameEvent
{
}
所有 RedPointMail 收到事件后都会标记为脏状态。RedPointSystem 会在更新时统一刷新,并把变化向父节点传递。
bash
EventUnreadMailChanged
↓
刷新每封邮件红点
↓
刷新邮件入口红点
↓
刷新主菜单红点
同一帧内多次触发事件,也只需要统一刷新一次。
七、绑定红点 UI
在 Prefab 中准备红点节点:
bash
Mail
└── MailRedPoint
MailRedPoint 可以添加一个 UGUI Image 显示红点图案,生成类型使用:
bash
myUGUIObject
界面初始化时绑定:
bash
public override void init()
{
base.init();
RedPointManager.mMainMail.bindPointUI(mMailRedPoint);
}
界面销毁时取消绑定:
bash
public override void destroy()
{
RedPointManager.mMainMail.removePointUI(mMailRedPoint);
base.destroy();
}
绑定后不需要手动调用:
bash
mMailRedPoint.setActive(...);
红点状态变化时,RedPoint 会自动控制节点的显示和隐藏。
同一个红点数据也可以绑定多个 UI 节点。例如邮件入口同时出现在主界面和功能菜单中,两个位置可以绑定同一个 mMainMail。
八、列表项中的红点
每一封邮件的列表项,可以绑定自己的叶节点:
bash
public void setMail(Mail mail)
{
mMail = mail;
mMail.getRedPoint().bindPointUI(mUnreadMark);
}
列表项回收时必须取消绑定:
bash
public override void recycle()
{
if (mMail.isValid())
{
mMail.getRedPoint()?.removePointUI(mUnreadMark);
}
mMail = null;
base.recycle();
}
否则 UI 节点已经被回收或销毁后,红点仍然会尝试更新它。
九、显示未读数量
需要显示数字时,可以继承 RedPointCount:
bash
public class RedPointPrivateChat : RedPointCount
{
protected PrivateChatInfo mChat;
protected override void initEventType()
{
base.initEventType();
addEvent<EventPrivateChange>();
}
public void setPrivateChat(PrivateChatInfo chat)
{
mChat = chat;
}
public override void refresh()
{
setCount(mChat.getUnreadCount());
}
}
绑定红点节点和数量文本:
bash
mPrivateChat
.getRedPoint()
.setPointUI(mMessagePoint, mUnreadMessageCount);
当数量大于 0 时显示红点,为 0 时自动隐藏。
回收时同样要取消绑定:
bash
mPrivateChat
?.getRedPoint()
?.removePointUI(mMessagePoint, mUnreadMessageCount);
十、使用规则
使用红点系统时主要注意四件事:
父节点使用普通 RedPoint,叶节点才继承并重写 refresh()。
不要在多个界面里分别判断"是否有未读邮件"。业务状态只由 RedPointMail 判断,界面只负责绑定显示节点。
十一、总结
树形红点系统把业务判断和 UI 表现分开:
bash
业务数据
↓
叶节点判断状态
↓
父节点自动汇总
↓
所有绑定的 UI 自动刷新
新增一种红点时,只需要:
bash
创建 RedPoint 子类
注册关注的事件
实现 refresh()
挂到对应父节点
绑定 UI 节点
这样邮件、私聊、成就等功能可以共用同一套红点更新流程,不需要在每个界面中重复查询业务状态。