最近在寫一個社交APP的時候静暂,在控制消息計數(shù),及界面紅點顯示時總會或多或少有延遲或計數(shù)偏差棚唆,網(wǎng)上大多是對界面繪制的探討,而在處理數(shù)據(jù)處理上相對較少心例,因此宵凌,我琢磨著能寫一個快捷方便,且只需要配置一次止后,之后均自動更新數(shù)字瞎惫,顯示、隱藏的庫译株。于是有了它 —— SuperBadge (全局角標數(shù)據(jù)模型管理庫)
模型思路
通過樹形結(jié)構(gòu)將數(shù)字和紅點進行分層管理瓜喇,只關(guān)聯(lián)父級和子級數(shù)據(jù), 原則上不越級干擾其他控件歉糜,如圖例1所示
- 根節(jié)點 也可理解為頂級節(jié)點乘寒,是所有子級節(jié)點最終的交匯點,其特點為:沒有關(guān)聯(lián)的上級節(jié)點(父節(jié)點)匪补,有關(guān)聯(lián)的下級節(jié)點(節(jié)點A伞辛、節(jié)點B、節(jié)點C)夯缺,不能進行增加數(shù)字和減少數(shù)字操作蚤氏,能進行已讀操作【調(diào)用read()方法】;
- 節(jié)點A踊兜、節(jié)點B竿滨、節(jié)點C 為普通節(jié)點,其特點為:有關(guān)聯(lián)的上級節(jié)點(根節(jié)點),有關(guān)聯(lián)的下級節(jié)點(節(jié)點1.2.3.4.5.6)姐呐,不能進行增加數(shù)字和減少數(shù)字操作,能進行已讀操作
- 節(jié)點1.2.3.4.5.6 最下級節(jié)點典蝌,紅點數(shù)字的直接數(shù)據(jù)源曙砂。其特點為:有關(guān)聯(lián)的上級節(jié)點(節(jié)點A、節(jié)點B骏掀、節(jié)點C)鸠澈,沒有關(guān)聯(lián)的下級節(jié)點,能進行增加數(shù)字和減少數(shù)字操作截驮,能進行已讀操作
舉個栗子
主界面有【消息】【好友】【我的】三個模塊笑陈,那么這三個模塊均為獨立的[根節(jié)點],以消息為例】【消息】節(jié)點下面綁定了好友會話A[節(jié)點A]涵妥、好友會話B[節(jié)點B]、好友會話C[節(jié)點C]坡锡,每個好友會話下面分別綁定 語音消息[節(jié)點1.3.5] 蓬网、文本消息[節(jié)點2.4.6],如圖例2所示
這樣的設(shè)計方式我們能實現(xiàn)那些功能鹉勒?
如果我們要在主界面標記為讀取所有帆锋,那么調(diào)用 【消息】的 read()方法即可,其下所有節(jié)點數(shù)字均會設(shè)置為0(不顯示)禽额;同理锯厢,如果我們要讀取好友會話A,那么調(diào)用好友會話A的 read()脯倒;語音消息和 文本消息數(shù)字均會被設(shè)置為0实辑,而【消息】節(jié)點會減去好友會話A的數(shù)字,通過這種方式藻丢,我們在對任意一個層級做出數(shù)據(jù)更新的時候徙菠,其上下級都會聯(lián)動。
為什么不允許非最下級節(jié)點之外的節(jié)點進行增減操作郁岩?
也可以理解為為什么我只允許在直接關(guān)聯(lián)數(shù)據(jù)的節(jié)點上進行增減操作婿奔,其原因很容易理解,假設(shè)好友會話A發(fā)來一條好友文本消息问慎,如果我們直接對好友會話A節(jié)點進行+1的操作萍摊,因為SuperBadge不干涉業(yè)務(wù)邏輯, 所以我們的下級節(jié)點將無法判斷新+1的消息是語音消息+1還是文本消息+1如叼,從而導(dǎo)致對整個數(shù)據(jù)模型的破壞冰木。同理,我們也不能對【消息】節(jié)點采取添加方法,SuperBadge將無法知道新的會話是來自好友會話A踊沸、B歇终、C其中的哪一個。
同時逼龟,為了保證在配置后能全局自動化评凝,我們希望開發(fā)者在初始化和綁定好控件之后,可以完全忘記數(shù)字的概念腺律,也無需關(guān)心增減的狀態(tài)奕短,而只有讀和未讀的兩種狀態(tài) (但即使如此,為了滿足一些特定需求匀钧,我們依舊提供了增減方法翎碑,但只適用于最下級節(jié)點調(diào)用)
功能分析:自動管理更新全局角標
- 首先我們需要【BadgeManger】 角標繪制管理者在相應(yīng)控件上繪制角標,比如右上角畫一個圓之斯,圓里面有相應(yīng)的數(shù)字日杈;
- 然后需要一個【SuperBadgeHelper】 角標助手記錄這個控件信息的對象,并加入全局數(shù)據(jù)模型隊列佑刷,在每次刷新界面的時候獲取控件角標信息(如有)并顯示达椰。
- 與之相關(guān)聯(lián)的的控件相互綁定,創(chuàng)建樹形數(shù)據(jù)模型项乒。通過【SuperBadgeDater】 角標數(shù)據(jù)管理 - 儲存和獲取數(shù)據(jù)啰劲,在某個控件發(fā)生數(shù)據(jù)變化的時候?qū)εc之相關(guān)聯(lián)的控件做出聯(lián)動。
通過上面的分析檀何,代碼一共由三部分組成
-
BadgeManger 角標繪制管理者 - 用于在View上繪制角標
因為本文重點是討論數(shù)據(jù)模型蝇裤,所以角標繪制代碼借鑒于他人,在此先不多作分析,有興趣可跳轉(zhuǎn)BadgeView查看频鉴。
-
SuperBadgeHelper 角標助手 - 核心類栓辜,處理全局消息計數(shù)模型
-
私有構(gòu)造方法
/** * @param context 當前Avtivity * @param view 綁定角標view * @param tag 全局的唯一標記 * @param num 角標數(shù)字 * @param show 是否顯示數(shù)字 * @return SuperBadgeHelper */ private SuperBadgeHelper(Activity context, View view, String tag, int num, boolean show) { if (SuperBadgeDater.getInstance().getBadge(context,tag) != null) { throw new IllegalArgumentException(tag + "標記已經(jīng)被其他控件注冊"); } if (context == null) { throw new NullPointerException("context not is null "); } if (num < 0) { throw new IllegalArgumentException("初始化角標數(shù)字不能小于0"); } if (tag == null) { throw new IllegalArgumentException("tag 不能為空"); } this.tag = tag; this.view = view; this.num = num; this.context = context; this.show = show; //創(chuàng)建BadgeManger 為控件繪制角標 badge = new BadgeManger(context); badge.setTargetView(view); paterAddNum(num);// 添加數(shù)字
}
-
初始化SuperBadgeHelper
public static SuperBadgeHelper init(Activity context, View view, String tag) { return init(context, view, tag, 0, true); } public static SuperBadgeHelper init(Activity context, View view, String tag, int num) { return init(context, view, tag, num, true); } public static SuperBadgeHelper init(Activity context, View view, String tag, boolean show) { return init(context, view, tag, 0, show); } /** * @param context 當前Avtivity * @param view 綁定角標view * @param tag 用于綁定的唯一標記 * @param num 角標數(shù)字 * @param show 是否顯示數(shù)字 * @return SuperBadgeHelper */ public static SuperBadgeHelper init(Activity context, View view, String tag, int num, boolean show) { //從數(shù)據(jù)模型里查找是否已有標記為 [tag] 的控件 SuperBadgeHelper superBadge = SuperBadgeDater.getInstance().getBadge(context,tag); if (superBadge != null) { //如有,替換view superBadge.setView(view); superBadge.setContext(context); superBadge.setShowBadge(show); superBadge.getBadge().setTargetView(view); if (superBadge.isShow()) { superBadge.getBadge().setBadgeCount(superBadge.getNum()); } return superBadge; } else {//如沒有垛孔,新建SuperBadgeHelper return new SuperBadgeHelper(context, view, tag, num, show); } }
-
綁定關(guān)聯(lián)控件
綁定關(guān)聯(lián)控件是整個數(shù)據(jù)模型里面最關(guān)鍵的一步藕甩,通過下級控件A調(diào)用 bindView()方法來與其他控件B保持關(guān)系,如果A發(fā)生了變化周荐,那么B也會發(fā)生相應(yīng)的變化狭莱。
1.提供了兩種綁定方法
/**
* 根據(jù)父級控件根據(jù)全局唯一標簽將他綁定到本級控件
*
* @param tag 父級控件的Tag
*/
public void bindView(String tag) {
for (SuperBadgeHelper pater : paterBadge) {
if (pater.getTag().equals(tag)) {
// throw new IllegalArgumentException("不能重復(fù)添加相同控件");
return;
}
}
SuperBadgeHelper paterBadgeHelper = SuperBadgeDater.getInstance().getBadge(context,tag);
if (paterBadgeHelper != null) {
paterBadge.add(paterBadgeHelper); //添加本級父控件
paterBadgeHelper.addChild(this);//添加到父級子控件
} else {
throw new NullPointerException("沒有找到標記為[" + tag + "]的控件");
}
}
//第二種是根據(jù)SuperBadgeHelper(實質(zhì)上還是根據(jù)標簽綁定)
public void bindView(SuperBadgeHelper pater) {
bindView(pater.getTag());
}
-
設(shè)置為已讀
/** * 讀取所有消息,清空數(shù)字為0(隱藏角標) */ SuperBadgeHelper.read()
-
增減數(shù)字方法
我們希望開發(fā)者在初始化和綁定好控件之后概作,可以完全忘記數(shù)字的概念腋妙,也無需關(guān)心增減的狀態(tài),而只有讀和未讀的兩種狀態(tài)讯榕。
骤素!只有最下級節(jié)點可以調(diào)用
//減少數(shù)字
SuperBadgeHelper.lessNum(int i)
//增加數(shù)字
SuperBadgeHelper.addNum(int i)
-
其他方法請閱讀源碼
SuperBadgeDater 角標數(shù)據(jù)管理 - 儲存和獲取數(shù)據(jù)
代碼很簡單匙睹,就全部貼上來了,暫時只用Map進行儲存济竹,其他儲存方式正在設(shè)計中
public class SuperBadgeDater implements Serializable {
private static final SuperBadgeDater Instance = new SuperBadgeDater();
Map<String, SuperBadgeHelper> map = new HashMap<>();
private SuperBadgeDater() {
}
public static SuperBadgeDater getInstance() {
return Instance;
}
public void addBadge(SuperBadgeHelper superBadge) {
map.put(superBadge.getTag(), superBadge);
}
public SuperBadgeHelper getBadge(String tag) {
return map.get(tag);
}
}
使用 SuperBadge
添加依賴
添加在到APP的gradle里
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
添加到項目 gradle的dependency里
dependencies {
compile 'com.github.chendongde310:SuperBadge:latest.release'
}
最新版本請?zhí)D(zhuǎn)至我的Github 查看
如何使用
-
step1 初始化控件
/** * * @param context Activity * @param view 綁定角標view * @param tag 用于綁定的唯一標記 * @param num 角標數(shù)字 * @param show 是否顯示數(shù)字 * @return SuperBadgeHelper */ SuperBadgeHelper.init(Activity context, View view, String tag, int num,boolean show)
-
step2 綁定上級控件
/** * 根據(jù)父級控件tag綁定父級控件 * @param tag 父級控件的Tag */ SuperBadgeHelper.bindView(String tag) //or SuperBadgeHelper.bindView(mSuperBadgeHelper)
-
step3 設(shè)置已讀
/** * 讀取所有消息痕檬,清空數(shù)字為0(不顯示) */ SuperBadgeHelper.read()
-
other method
//增加數(shù)字 - 必須為根節(jié)點控件 addNum(int i) //減少數(shù)字 - 必須為根節(jié)點控件 lessNum(int i) //是否顯示角標 setShowBadge(boolean b) //設(shè)置角標半徑 setDipRadius(int dipRadius) //設(shè)置角標顏色 setBadgeColor(int badgeColor)
TODO 即將完成
- 提供自定義繪制方法
- (已完成)小紅點(不顯示具體數(shù)字,半徑小的紅點提示)
- read()方法 異步(網(wǎng)絡(luò) 送浊、數(shù)據(jù)庫讀寫)請求失敗后的處理
- 增加將節(jié)點標記為未讀狀態(tài)功能(+1s)
- 桌面(APP圖標)角標顯示
采用此消息計數(shù)模型梦谜,配置好后能讓開發(fā)者最大化的不去關(guān)注數(shù)字的更新和界面刷新操作,專注于業(yè)務(wù)邏輯開發(fā)罕袋。此庫可以不必擔心界面和控件創(chuàng)建銷毀所造成的內(nèi)存泄漏等問題改淑,如果此庫對你有幫助碍岔,請花幾秒的時間給我一個star浴讯,您的支持是對我最大鼓勵!
文末提供Demo下載.
我的Githu地址 - http://github.com/chendongde310
如果此庫對你有幫助蔼啦,請花幾秒的時間給我一個star榆纽,您的支持是對我最大鼓勵!如有任何關(guān)于此庫的問題和疑問捏肢,請在下方留言評論或提交issues,感謝閱讀