仿餓了么加入購物車控件

轉(zhuǎn)載之
張旭童
本文部分內(nèi)容出自之前的博文(本文提及的上一節(jié)即指這篇)《酷炫Path動畫》:
http://blog.csdn.net/zxt0601/article/details/54018970

今天給大家?guī)淼氖抢?純自定義View,實現(xiàn)的仿餓了么加入購物車控件,自帶閃轉(zhuǎn)騰挪動畫的按鈕。效果圖如下:
**圖1 **項目中使用的效果,考慮到了 View 的回收復用,并且可以看到在 RecyclerView 中使用,切換 LayoutManager 也是沒有問題的:


圖2 Demo效果,測試各種屬性值:

注意,本控件 **非繼承自ViewGroup,而是純自定義View **實現(xiàn)。理由如下:
1 減少布局層級,從而提高性能

**2 **文字和圖形純draw,用到什么draw什么,沒有其他的額外工作,也間接提高性能。

**3 **純自定義View難度更高,更有實(裝)踐(B)的意義

**1. **減少布局層次,很好理解,ViewGroup 內(nèi)嵌套幾個 TextView、ImageView 這里寫代碼也可以實現(xiàn)這個效果,然而這會使布局層次多了一級,并且內(nèi)部要嵌套多個控件,層級越多,控件越多,繪制的就越慢,在列表中對性能的影響更大。
**2. **別小看了“小小”的 TextView 和 ImageView,其實它們有很多的屬性和特性在本例中是不必要的,舉個例子,查看源碼,TextView 有一萬多行,ondraw() 方法有一百多行, ImageView 有1588行,這么多行代碼都是我們需要的嗎?直接使用這些現(xiàn)成的控件嵌套實現(xiàn),其實性能不如我們用到什么draw什么。唯一的好處可能就是比較簡單了。(其實 TextView 的性能是不高的)
**3. **純自定義View,draw 出這些需要的元素,并且還要考慮動畫,以及點擊各區(qū)域的監(jiān)聽,實現(xiàn)起來還是有一些難度的,但我們多寫一些有難度的代碼才能提高水平。

如何使用

伸手黨福利:講解實現(xiàn)前,先看一下 如何使用 以及 支持的屬性 等。
使用
xml:


注意:加減點擊后,具體的操作,要根據(jù)業(yè)務的不同來編寫了,設計到實際的購物車可能還有寫數(shù)據(jù)庫操作,或者請求接口等,要操作成功后才執(zhí)行動畫、或者修改count,這一塊代碼每個人寫法可能不同。
使用時,可以重寫 onDelClick() 和 onAddClick() 方法,并在合適的時機回調(diào) onCountAddSuccess() 和 onCountDelSuccess() 以執(zhí)行動畫。效果圖如 圖2.
支持的屬性

這么多屬性夠你用了吧。下面看重點的實現(xiàn)吧,Let’s Go!

實現(xiàn)解剖

關于自定義View的基礎,這里不再贅述。如果閱讀時有不明白的,建議下載源碼邊看邊讀,或者學習自定義View基礎知識后再閱讀本文。
我們撿重點說,無非是繪制。繪制的重點,這里分三塊:
靜態(tài)繪制。(分兩塊:加減按鈕和數(shù)量、hint提示文字和背景)

第一層。(加減按鈕和數(shù)量)以及它的旋轉(zhuǎn)、位移、透明度動畫

第二層。(hint區(qū)域)以及它的伸展收縮動畫

除了繪制以外的重點是:
由于采用了完全的自定義View去實現(xiàn)這么一個“組合控件效果”,則點擊事件的監(jiān)聽需要自己處理。

回收復用的列表中使用時,列表滑動,如何正確顯示UI。

靜態(tài)繪制
靜態(tài)繪制就是最基本的自定義View知識,繪制 圓圈(Circle)、線段(Line)、數(shù)字(Text) 以及 圓角矩形(RoundRect),值得注意的是,要考慮到 避免overDraw 和 動畫的需求,我們要繪制的兩層應該是互斥關系。
剝離掉動畫代碼,大致如下(基本都是draw代碼,可以快速閱讀):


根據(jù) isHintMode 布爾值變量,區(qū)分是繪制 第二層(Hint層) 或者 第一層(加減按鈕層)。
繪制 第二層 時沒啥好說的,就是利用 canvas.drawRoundRect,繪制圓角矩形,然后 canvas.drawText 繪制 hint。(如果圓角的值足夠大,矩形的寬度足夠小,就變成了圓形。)
繪制 第一層 時,要根據(jù)當前的數(shù)量選擇不同的顏色,注意在繪制加減按鈕的圓圈時,我們是用 Path 繪制的,這是因為我們還需要用 Path 構(gòu)建 Region類,這個類就是我們監(jiān)聽點擊區(qū)域的重點。

點擊事件的監(jiān)聽

在講解動畫之前,我們先說說如何監(jiān)聽點擊的區(qū)域,因為本控件的動畫是和加減數(shù)量息息相關的,而數(shù)量的加減是由點擊相應”+ - 按鈕”區(qū)域觸發(fā)的。所以我們的監(jiān)聽按鈕的點擊事件,其實就是監(jiān)聽相應的”+ - 按鈕”區(qū)域。
上一節(jié)***** 中,我們在繪制”+ - 按鈕”區(qū)域時,通過 Path,構(gòu)建了兩個 Region類,Region類 有個 contains(int x, int y)方法如下,通過傳入對應觸摸的x、y坐標,就可知道知否點擊了相應區(qū)域。


知道了這一點,再寫這部分代碼就相當簡單了:

hint 模式時,我們可以認為控件所有范圍都是“+”的有效區(qū)域。
而在 非hint 模式時,根據(jù)
上一節(jié)
*構(gòu)建的 mAddRegion 和 mDelRegion 去判斷。
判斷確認點擊后,具體的操作,要根據(jù)業(yè)務的不同來編寫了,設計到實際的購物車可能還有寫數(shù)據(jù)庫操作,或者請求接口等,要操作成功后才執(zhí)行動畫、或者修改count,這一塊代碼每個人寫法可能不同。
使用時,可以重寫 onDelClick() 和 onAddClick() 方法,并在合適的時機回調(diào) onCountAddSuccess() 和 onCountDelSuccess() 以執(zhí)行動畫。
本文如下編寫:

動畫實現(xiàn)

這里會用到兩個變量:


依次分析有哪些動畫:
Hint動畫
主要是圓角矩形的 展開、收縮。固定right、bottom,當展開時,不斷減少矩形的左起點left坐標值,則整個矩形寬度變大,呈現(xiàn)展開。收縮時相反。代碼:

減按鈕動畫
看起來是 旋轉(zhuǎn)、位移、透明度。那么對于背景的圓圈來說,我們只需要位移、透明度。因為它本身是個圓,就不要旋轉(zhuǎn)了。代碼:

對于前景的“-”號來說,旋轉(zhuǎn)、位移、透明度都需要做。這里我們利用 canvas.translate() canvas.rotate 做旋轉(zhuǎn)和位移動畫,別忘了 canvas.save()和 canvas.restore() 恢復畫布的狀態(tài)。(透明度在上面已經(jīng)設置過了。)

數(shù)量的動畫
看起來也是 旋轉(zhuǎn)、位移、透明度。同樣是利用canvas.translate() canvas.rotate 做旋轉(zhuǎn)和位移動畫。

動畫的定義
動畫是在View初始化時就定義好的,執(zhí)行順序:
數(shù)量增加,0-1時,先收縮Hint(第二層)mAnimReduceHint執(zhí)行,完畢后執(zhí)行減按鈕(第一層)進入的動畫mAnimAdd。

數(shù)量減少,1-0時,先執(zhí)行減按鈕退出的動畫mAniDel,再伸展Hint動畫mAnimExpandHint,完畢后,顯示hint文字。

代碼如下:


針對復用機制的處理

因為我們的購物車控件肯定會用在列表中,不管你用 ListView 還是 RecyclerView,都會涉及到復用的問題。
復用給我們帶來一個麻煩的地方就是,我們要處理好一些屬性狀態(tài)值,否則UI上會有問題。可以從兩處下手處理:
onMeasure
列表復用時,依然會回調(diào) onMeasure()方法,所以在這里初始化一些UI顯示的參數(shù)。這里順帶將適配 wrap_content 的代碼也一同貼上:



在改變count時
一般在 onBindViewHolder() 或者 getView() 時,都會對本控件重新設置 count 值,count 改變時,當然也是需要根據(jù) count 進行屬性值的調(diào)整。且此時如果View正在做動畫,應該停止這些動畫。

總結(jié)

我在實現(xiàn)這個控件時,覺得難度相對大的地方在于做動畫時,“-”按鈕 和 數(shù)量的旋轉(zhuǎn)動畫,如何確定正確的坐標值。因為將text繪制的居中本身就有一些注意事項在里面,再涉及到動畫,難免蒙圈。需要多計算,多試驗。
還有就是觀察餓了么的效果,將hint區(qū)域的動畫利用改變RoundRect的寬度去實現(xiàn)。起初沒有想到,也是思考了一會如何去做。這是屬于分析、拆解動畫遇到的問題。
除了繪制以外的重點是:
利用Region監(jiān)聽區(qū)域點擊事件

復用的列表,如何正確顯示UI。

動畫次序以及考慮到復用時,在合適的地方取消動畫。

盡情在項目中使用它吧,有問題隨時gayhub給我反饋。
通過sdk工具查看餓了么,它其實是用 TextView 和 ImageView 組合實現(xiàn)的。另外我十分懷疑它沒有封裝成控件,因為在列表頁和詳情頁的交互,以及動畫居然略有不同, 在詳情頁,仔細看由0-1時,它右邊的 + 按鈕的動畫居然會閃一下,在列表頁卻沒有,很是不解。
代碼傳送門:
https://github.com/mcxtzhang/AnimShopButton

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容