一行代碼無侵入實現(xiàn)RadioGroup無限嵌套互斥

最近有一個需求,需要實現(xiàn)單選的功能,類似這個

Paste_Image.png

像這種結(jié)構(gòu)谓着,通過RecyclerView,FlexBoxLayout之類的都可以實現(xiàn)。然而相對比較麻煩坛掠,我想通過RadioGroup來實現(xiàn)赊锚,但是通過RadioGroup來實現(xiàn)的話也存在一些問題,RadioGroup本身是繼承于LinearLayout屉栓,并不能實現(xiàn)以上布局舷蒲。進(jìn)行嵌套的話,則會失去單選的功能友多,這是為什么呢牲平?通過查看源碼的話可以發(fā)現(xiàn),RadioGroup設(shè)置了OnHierarchyChangeListener監(jiān)聽(可以檢測到直接子
View的添加和移除
)夷陋。

Paste_Image.png
Paste_Image.png

在監(jiān)聽到添加子View的時候欠拾,(上圖375行代碼處可見)如果子ViewRadioButton的話,則添加setONCheckedChangeWidgetListener()監(jiān)聽骗绕,如果RadioButton不存在id的話藐窄,則通過View.generateViewId()為其設(shè)置一個idRadioGroup內(nèi)部是通過id來判斷當(dāng)前選中哪個RadioButton),不是則不進(jìn)行處理酬土。setONCheckedChangeWidgetListener的作用就是當(dāng)RadioButton被選中的時候進(jìn)行通知RadioGroup荆忍,收到通知后,(下圖346行代碼處) RadioGroup就會進(jìn)行處理撤缴,會調(diào)用setCheckedStateForView()方法,這個方法會把上一個選中的RadioButton取消選中狀態(tài),然后調(diào)用351行的 setCheckedId()方法刹枉,設(shè)置當(dāng)前選中 RadioGroupid,并回調(diào)我們平時設(shè)置的setOnCheckedChangeListener中的OnCheckedChangeListener

Paste_Image.png

這樣我們就明白了RadioGroup如何實現(xiàn)選中的屈呕,也明白了為什么嵌套后會失去單選效果微宝。

這個時候,我肯定會按照慣例去Google一下有沒有輪子虎眨,然而發(fā)現(xiàn)了很多都是自定義View的蟋软,基本上都是繼承LinearLayout實現(xiàn)一套類似RadioGroup的實現(xiàn),這樣會失去原有的RadioGroup的特性嗽桩,而且也存在一個明顯的問題岳守,就是只能夠在xml布局中實現(xiàn)互斥,如果在代碼中進(jìn)行動態(tài)添加的話碌冶,則會失去單選的效果湿痢。

于是我想有沒有一個辦法,可以無侵入式的實現(xiàn)RadioGroup的無限嵌套扑庞,事實證明可以譬重。
我們先來看一下使用方法:
new RadioGroupUtils(rg).supportNest();
對的,就是這么一句話就可以了嫩挤。下面我們對RadioGroupUtils的部分實現(xiàn)進(jìn)行分析 :

在調(diào)用supportNest()方法的時候害幅,我們先為RadioGroup設(shè)置了OnHierarchyChangeListener監(jiān)聽,在監(jiān)聽中岂昭,如果子ViewRadioButton則執(zhí)行dispatchChildViewAdded()方法進(jìn)行對應(yīng)的添加和移除以现,最終也是調(diào)用進(jìn)行traversalSetOnCheckedChangeWidgetListener()方法。然后調(diào)用traversalSetOnCheckedChangeWidgetListener()约啊,這里調(diào)用一次進(jìn)行綁定是因為RadioGroup在設(shè)置OnHierarchyChangeListener的時候邑遏,view已經(jīng)通過xml Inflate加入了RadioGroup中,在xml設(shè)置的RadioGroupView不會調(diào)用我們設(shè)置的OnHierarchyChangeListener中的onChildViewAddedonChildViewAdded方法

Paste_Image.png
Paste_Image.png

traversalSetOnCheckedChangeWidgetListener()方法做的事情比較簡單恰矩,就是對傳入的view進(jìn)行遞歸记盒,如果是View的話,我們執(zhí)行setOnCheckedChangeWidgetListener()方法外傅。如果是ViewGroupRadioGroup不進(jìn)行處理纪吮,這樣可以防止對RadioGroup嵌套RadioGroup造成干擾)則為其添加或者移除OnHierarchyChangeListener監(jiān)聽俩檬。

Paste_Image.png

setOnCheckedChangeWidgetListener()做了什么事情呢?通過上面的分析可以知道碾盟,其實RadioGroup是通過給RadioButton添加監(jiān)聽來實現(xiàn)單選棚辽,那我們只需要為RadioButton都加上監(jiān)聽(這里是通過反射的方式實現(xiàn)的,因為這些方法和變量都是私有的)冰肴,并且當(dāng)RadioButton不存在id的時候屈藐,為其設(shè)置一個id,上面已經(jīng)提到RadioGroup是通過id來判斷當(dāng)前選中哪個RadioButton熙尉。

Paste_Image.png

這樣就可以實現(xiàn)網(wǎng)上大部分自定義RadioGroup的功能联逻,實現(xiàn)xml布局嵌套單選的功能,但是代碼動態(tài)添加就會存在問題检痰,為什么呢包归?認(rèn)真看的小伙伴應(yīng)該已經(jīng)發(fā)現(xiàn)問題了,上面我有說到OnHierarchyChangeListener只能監(jiān)聽到直接子View的添加和移除,也就是說攀细,如下圖所示箫踩,情況1是可以產(chǎn)生互斥效果,情況2是無效的

//情況1:直接添加在RadioGroup上
RadioButton radioButton = new RadioButton(IntentActivity.this);
int height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, getResources().getDisplayMetrics());
radioButton.setLayoutParams(new RadioGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, height, 1));
radioButton.setText("haha");
rg.addView(radioButton);
//情況2:添加在RadioGroup的子View LinearLayout上(rg.getChildAt(2))
RadioButton radioButton2 = new RadioButton(IntentActivity.this);
radioButton2.setLayoutParams(new RadioGroup.LayoutParams(0, height, 1));
radioButton2.setText("sec");
((ViewGroup) rg.getChildAt(2)).addView(radioButton2);

那要怎么辦谭贪?我們通過查看源碼可以發(fā)現(xiàn)setOnHierarchyChangeListener()ViewGroup的方法境钟,所以我們可以在RadioGroup添加子View的時候為所有的ViewGroup都設(shè)置上OnHierarchyChangeListener監(jiān)聽,這樣無論嵌套多少層的子View添加RadioButton俭识,我們都可以為其設(shè)置監(jiān)聽慨削,這樣就可以實現(xiàn)無限嵌套互斥的功能了。 所以我們在setOnCheckedChangeWidgetListener()方法中為子ViewGroup設(shè)置了OnHierarchyChangeListener套媚。這里我進(jìn)行了一些處理缚态,使用了代理的方式,即可以設(shè)置監(jiān)聽 堤瘤,也可以保證子ViewGroup原來的OnHierarchyChangeListener監(jiān)聽玫芦。

Paste_Image.png

這樣,我們的功能就實現(xiàn)了本辐,效果如下圖所示:

a.gif

源碼例子

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末桥帆,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子慎皱,更是在濱河造成了極大的恐慌老虫,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,919評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件茫多,死亡現(xiàn)場離奇詭異祈匙,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評論 3 392
  • 文/潘曉璐 我一進(jìn)店門夺欲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來跪帝,“玉大人,你說我怎么就攤上這事些阅∏干酰” “怎么了?”我有些...
    開封第一講書人閱讀 163,316評論 0 353
  • 文/不壞的土叔 我叫張陵扑眉,是天一觀的道長。 經(jīng)常有香客問我赖钞,道長腰素,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,294評論 1 292
  • 正文 為了忘掉前任雪营,我火速辦了婚禮弓千,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘献起。我一直安慰自己洋访,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,318評論 6 390
  • 文/花漫 我一把揭開白布谴餐。 她就那樣靜靜地躺著姻政,像睡著了一般。 火紅的嫁衣襯著肌膚如雪岂嗓。 梳的紋絲不亂的頭發(fā)上汁展,一...
    開封第一講書人閱讀 51,245評論 1 299
  • 那天,我揣著相機(jī)與錄音厌殉,去河邊找鬼食绿。 笑死,一個胖子當(dāng)著我的面吹牛公罕,可吹牛的內(nèi)容都是我干的器紧。 我是一名探鬼主播,決...
    沈念sama閱讀 40,120評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼楼眷,長吁一口氣:“原來是場噩夢啊……” “哼铲汪!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起摩桶,我...
    開封第一講書人閱讀 38,964評論 0 275
  • 序言:老撾萬榮一對情侶失蹤桥状,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后硝清,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體辅斟,經(jīng)...
    沈念sama閱讀 45,376評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,592評論 2 333
  • 正文 我和宋清朗相戀三年芦拿,在試婚紗的時候發(fā)現(xiàn)自己被綠了士飒。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片查邢。...
    茶點故事閱讀 39,764評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖酵幕,靈堂內(nèi)的尸體忽然破棺而出扰藕,到底是詐尸還是另有隱情,我是刑警寧澤芳撒,帶...
    沈念sama閱讀 35,460評論 5 344
  • 正文 年R本政府宣布邓深,位于F島的核電站,受9級特大地震影響笔刹,放射性物質(zhì)發(fā)生泄漏芥备。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,070評論 3 327
  • 文/蒙蒙 一舌菜、第九天 我趴在偏房一處隱蔽的房頂上張望萌壳。 院中可真熱鬧,春花似錦日月、人聲如沸袱瓮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,697評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽尺借。三九已至,卻和暖如春精拟,著一層夾襖步出監(jiān)牢的瞬間褐望,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,846評論 1 269
  • 我被黑心中介騙來泰國打工串前, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留瘫里,地道東北人。 一個月前我還...
    沈念sama閱讀 47,819評論 2 370
  • 正文 我出身青樓荡碾,卻偏偏與公主長得像谨读,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子坛吁,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,665評論 2 354

推薦閱讀更多精彩內(nèi)容