效果圖 本文原創(chuàng),轉(zhuǎn)載請(qǐng)注明本出處! 前言 網(wǎng)上這種類似 PathMenu 的菜單很多,但是基本都不符合我項(xiàng)目的需求,想看他們的源碼實(shí)現(xiàn)然后做出修改,進(jìn)行二次開(kāi)發(fā)來(lái)適應(yīng)我的項(xiàng)目需求,但是發(fā)現(xiàn)——以我現(xiàn)在的能力,如果不是以前做過(guò)類似的功能,看別人的代碼,很難很快地找出主要實(shí)現(xiàn)思路,而且不同的作者的代碼有不同的風(fēng)格(特別是命名),于是就自己按照自己的思路來(lái)實(shí)現(xiàn),然后把實(shí)現(xiàn)思路都寫出來(lái)分享一下,讓大家了解我這個(gè)自定義 View 控件是怎么實(shí)現(xiàn)的,到時(shí)候大家根據(jù)需求修改源碼,進(jìn)行二次開(kāi)發(fā)的時(shí)候也可以參考,也希望和大家一起探討怎樣實(shí)現(xiàn)更好。 需求 做這個(gè)控件的目的是為了實(shí)現(xiàn)一個(gè)平板上的全屏視頻播放器的菜單欄,點(diǎn)擊之后會(huì)彈出一堆按鈕來(lái)讓用戶選擇 ,這樣的話網(wǎng)上很多開(kāi)源控件都能實(shí)現(xiàn),問(wèn)題就是這個(gè)播放器是要支持 Android7.0 的分屏功能,(平板比較坑爹,還打開(kāi)了 Freeform 模式的入口,這個(gè) Freeform 模式可以讓用戶自由調(diào)節(jié) APP 的界面寬高,就像在 Windows 桌面的那些應(yīng)用窗口一樣),要適應(yīng)分屏功能,APP 的寬高可能會(huì)改變,這些按鈕的位置分布情況也要根據(jù)寬高來(lái)改變,想想就蛋疼。而網(wǎng)上很多的這類型 PathMenu是固定分布方式的,所以就做出了這個(gè)可以調(diào)整選項(xiàng)位置的自定義菜單控件——YMenuView(取名技術(shù)不好不知道怎么取,就用這個(gè)挫名字啦(≧▽≦)/)。 具體實(shí)現(xiàn) 具體實(shí)現(xiàn)思路思路大概如下圖: 其中主要難點(diǎn)是第二個(gè)和第三個(gè)。簡(jiǎn)單來(lái)說(shuō),本質(zhì)上 YMenuView 是一個(gè)ViewGroup,然后在里面動(dòng)態(tài)生成一些控件,點(diǎn)擊MenuButton的時(shí)候就會(huì)把一堆OptionButton顯示/消失,這個(gè)過(guò)程加上一些動(dòng)畫,就形成最后的效果。 創(chuàng)建ViewGroup這部分其實(shí)沒(méi)什么好說(shuō)的,就是創(chuàng)建一個(gè)名為 YMenuView 的ViewGroup,然后獲取一些自定義的屬性(自定義屬性的介紹可以自行搜索或者看看我的筆記,這里不多說(shuō)了),為下一步的創(chuàng)建 MenuButton 和 OptionButton 做準(zhǔn)備,獲取的屬性在項(xiàng)目的 Github 地址上有詳細(xì)的說(shuō)明了。篇幅原因,這里就放出部分重要的屬性圖示: 創(chuàng)建 MenuButton 和 OptionButton如上圖所示,MenuButton 就是那個(gè)用于按下彈出菜單的按鈕,OptionButton就是可彈出收回的選項(xiàng)按鈕。 MenuButton下面先來(lái)看看如何創(chuàng)建 MenuButton: private void setMenuButton() { 主要是動(dòng)態(tài)生成一個(gè) Button,利用 LayoutParams 來(lái)控制它的位置,生成ID是為后面要使用 mYMenuButton 的信息做準(zhǔn)備,然后就是設(shè)置點(diǎn)擊事件來(lái)控制菜單開(kāi)關(guān)(開(kāi)關(guān)的操作實(shí)現(xiàn)后面講),最后就是設(shè)置背景和addView() 加入父 ViewGroup 視圖。 OptionButton說(shuō)是 Button,但是實(shí)際上 OptionButton 是繼承 ImageView,因?yàn)槲野l(fā)現(xiàn) ImageView 除了可以通過(guò) private void initBan() { 先不看 Ban 判斷,看下面的位置計(jì)算,OptionButton 的布局是矩形的,每一排中的每個(gè) ImageView 從左到右的,而 optionColumns 是列數(shù)(也就是每排的個(gè)數(shù)),通過(guò)這個(gè)屬性和總個(gè)數(shù)就可以確定所有OptionButton 的布局。如下面就是 optionPositionCount = 8,optionColumns = 3 的效果: 然后再看Ban判斷,這段代碼的目的就是讓序號(hào)為Ban數(shù)組里面的位置跳過(guò)這一輪循環(huán),不放置OptionButton。所以Ban這個(gè)功能可以通過(guò) 前面只是生成了 OptionButton,后面還要為它們?cè)O(shè)置圖片和背景: //設(shè)置選項(xiàng)按鈕的background 實(shí)現(xiàn)動(dòng)畫MenuButton 的動(dòng)畫沒(méi)什么好說(shuō)的,開(kāi)關(guān)動(dòng)畫就是兩個(gè)旋轉(zhuǎn)(一個(gè)逆時(shí)針,一個(gè)順時(shí)針),動(dòng)畫已經(jīng)在 xml 寫好了,太簡(jiǎn)單了就不展示出來(lái),想看的話直接看源碼好了: //初始化MenuButton的點(diǎn)擊動(dòng)畫 還開(kāi)放了方法,可以在外部改變這個(gè)開(kāi)關(guān)動(dòng)畫: //設(shè)置MenuButton彈出菜單選項(xiàng)時(shí)候MenuButton自身的動(dòng)畫,默認(rèn)為順時(shí)針旋轉(zhuǎn)180度,為空則是關(guān)閉動(dòng)畫 然后重點(diǎn)就是OptionButton的動(dòng)畫了,它的動(dòng)畫有四種:
這些動(dòng)畫封裝在 OptionButton 里面,因?yàn)閯?dòng)畫的設(shè)置需要用到自身的位置信息,所以需要注冊(cè) OnGlobalLayoutListener 來(lái)監(jiān)聽(tīng),等自身Layout 完畢之后再設(shè)置,不然 private void init(){ 因?yàn)檫M(jìn)入和退出的動(dòng)畫只是基本只是相反,篇幅原因這里就只展示退出動(dòng)畫的實(shí)現(xiàn)(看的時(shí)候要注意動(dòng)畫的初始坐標(biāo)零點(diǎn)默認(rèn)都是基于View的左上角頂點(diǎn)): private void setShowAndDisappear() { 實(shí)現(xiàn)點(diǎn)擊事件由于 OptionButton 都是在代碼動(dòng)態(tài)生成的,所以它們的ID也是動(dòng)態(tài)生成的,不能作為switch語(yǔ)句的case條件,所以這里自己寫了一個(gè)接口OnOptionsClickListener,來(lái)讓OptionButton的每次點(diǎn)擊都調(diào)用OnOptionsClickListener的帶索引參數(shù)的方法,這樣就實(shí)現(xiàn)讓點(diǎn)擊事件可以在外部實(shí)現(xiàn)并加以區(qū)分OptionButton了: //用于讓用戶在外部實(shí)現(xiàn)點(diǎn)擊事件的接口,index可以區(qū)分OptionButton 這樣做完之后,外部就可以通過(guò)實(shí)現(xiàn) OnOptionsClickListener 接口來(lái)實(shí)現(xiàn)點(diǎn)擊事件了。 結(jié)尾 以上就是 YMenuView 的主要思路了,至于一些細(xì)節(jié)大家有興趣的話可以去代碼的GitHub https://github.com/totond/YMenuView 上Fork下來(lái)或者直接下載下來(lái)看看,有什么意見(jiàn)或者建議的話也可以在issue上提出。
后話 最近剛正式入職,事情比較多,很多天晚上忙完都是懶得開(kāi)電腦,所以沒(méi)怎么寫博客。雖然寫博客耗時(shí)比較長(zhǎng),但是我覺(jué)得這是一件很有意義的事情,不但總結(jié)鞏固了自己的知識(shí),還能幫助他人,我要堅(jiān)持下去?,F(xiàn)在快穩(wěn)定下來(lái)了,后面再忙都會(huì)抽多點(diǎn)時(shí)間來(lái)總結(jié)的,在這里說(shuō)一下,激勵(lì)下自己。 與之相關(guān) 日 更 精 彩 微信號(hào):code-xiaosheng 公眾號(hào) 「code小生」 |
|
來(lái)自: codingSmart > 《待分類》