Android仿餓了么加入購物車旋轉(zhuǎn)控件自帶閃轉(zhuǎn)騰挪動畫的按鈕效果(實(shí)例詳解)
概述
在上文,酷炫Path動畫已經(jīng)預(yù)告了,今天給大家?guī)淼氖抢?純自定義View,實(shí)現(xiàn)的仿餓了么加入購物車控件,自帶閃轉(zhuǎn)騰挪動畫的按鈕。
效果圖如下:
圖1 項(xiàng)目中使用的效果,考慮到了View的回收復(fù)用,
并且可以看到在RecyclerView中使用,切換LayoutManager也是沒有問題的,
圖2 Demo效果,測試各種屬性值
注意,本控件非繼承自ViewGroup,而是純自定義View實(shí)現(xiàn)。理由如下:
- 1 減少布局層級,從而提高性能
- 2 文字和圖形純draw,用到什么draw什么,沒有其他的額外工作,也間接提高性能。
- 3 純自定義View難度更高,更有實(shí)(裝)踐(B)的意義
1 減少布局層次,很好理解,ViewGroup內(nèi)嵌套幾個TextView、ImageV這里寫代碼片iew也可以實(shí)現(xiàn)這個效果,然而這會使布局層次多了一級,并且內(nèi)部要嵌套多個控件,層級越多,控件越多,繪制的就越慢,在列表中對性能的影響更大。
2 別小看了“小小”的TextView和的ImageView,其實(shí)它們有很多的屬性和特性在本例中是不必要的,舉個例子,查看源碼,TextView有一萬多行,ondraw()方法有一百多行, ImageView有1588行,這么多行代碼都是我們需要的嗎?直接使用這些現(xiàn)成的控件嵌套實(shí)現(xiàn),其實(shí)性能不如我們用到什么draw什么。唯一的好處可能就是比較簡單了。(其實(shí)TextView的性能是不高的)
3 純自定義View,draw出這些需要的元素,并且還要考慮動畫,以及點(diǎn)擊各區(qū)域的監(jiān)聽,實(shí)現(xiàn)起來還是有一些難度的,但我們多寫一些有難度的代碼才能提高水平。
如何使用
伸手黨福利:講解實(shí)現(xiàn)前,先看一下如何使用 以及支持的屬性等。
使用
xml:
<!--使用默認(rèn)UI屬性--> <com.mcxtzhang.lib.AnimShopButton android:id="@+id/btn1" android:layout_width="wrap_content" android:layout_height="wrap_content" app:maxCount="3"/> <!--設(shè)置了兩圓間距--> <com.mcxtzhang.lib.AnimShopButton android:id="@+id/btn2" android:layout_width="wrap_content" android:layout_height="wrap_content" app:count="3" app:gapBetweenCircle="90dp" app:maxCount="99"/> <!--仿餓了么--> <com.mcxtzhang.lib.AnimShopButton android:id="@+id/btnEle" android:layout_width="wrap_content" android:layout_height="wrap_content" app:addEnableBgColor="#3190E8" app:addEnableFgColor="#ffffff" app:hintBgColor="#3190E8" app:hintBgRoundValue="15dp" app:hintFgColor="#ffffff" app:maxCount="99"/>
注意:
加減點(diǎn)擊后,具體的操作,要根據(jù)業(yè)務(wù)的不同來編寫了,設(shè)計(jì)到實(shí)際的購物車可能還有寫數(shù)據(jù)庫操作,或者請求接口等,要操作成功后才執(zhí)行動畫、或者修改count,這一塊代碼每個人寫法可能不同。
使用時,可以重寫onDelClick()和onAddClick()
方法,并在合適的時機(jī)回調(diào)onCountAddSuccess()和onCountDelSuccess()
以執(zhí)行動畫。
效果圖如圖2.
支持的屬性
name | format | description | 中文解釋 |
---|---|---|---|
isAddFillMode | boolean | Plus button is opened Fill mode default is stroke (false) | 加按鈕是否開啟fill模式 默認(rèn)是stroke(false) |
addEnableBgColor | color | The background color of the plus button | 加按鈕的背景色 |
addEnableFgColor | color | The foreground color of the plus button | 加按鈕的前景色 |
addDisableBgColor | color | The background color when the button is not available | 加按鈕不可用時的背景色 |
addDisableFgColor | color | The foreground color when the button is not available | 加按鈕不可用時的前景色 |
isDelFillMode | boolean | Plus button is opened Fill mode default is stroke (false) | 減按鈕是否開啟fill模式 默認(rèn)是stroke(false) |
delEnableBgColor | color | The background color of the minus button | 減按鈕的背景色 |
delEnableFgColor | color | The foreground color of the minus button | 減按鈕的前景色 |
delDisableBgColor | color | The background color when the button is not available | 減按鈕不可用時的背景色 |
delDisableFgColor | color | The foreground color when the button is not available | 減按鈕不可用時的前景色 |
radius | dimension | The radius of the circle | 圓的半徑 |
circleStrokeWidth | dimension | The width of the circle | 圓圈的寬度 |
lineWidth | dimension | The width of the line (+ - sign) | 線(+ - 符號)的寬度 |
gapBetweenCircle | dimension | The spacing between two circles | 兩個圓之間的間距 |
numTextSize | dimension | The textSize of draws the number | 繪制數(shù)量的textSize |
maxCount | integer | max count | 最大數(shù)量 |
count | integer | current count | 當(dāng)前數(shù)量 |
hintText | string | The hint text when number is 0 | 數(shù)量為0時,hint文字 |
hintBgColor | color | The hint background when number is 0 | 數(shù)量為0時,hint背景色 |
hintFgColor | color | The hint foreground when number is 0 | 數(shù)量為0時,hint前景色 |
hingTextSize | dimension | The hint text size when number is 0 | 數(shù)量為0時,hint文字大小 |
hintBgRoundValue | dimension | The background fillet value when number is 0 | 數(shù)量為0時,hint背景圓角值 |
這么多屬性夠你用了吧。
下面看重點(diǎn)的實(shí)現(xiàn)吧,Let's Go!.
實(shí)現(xiàn)解剖
關(guān)于自定義View的基礎(chǔ),這里不再贅述。
如果閱讀時有不明白的,建議下載源碼邊看邊讀,或者學(xué)習(xí)自定義View基礎(chǔ)知識后再閱讀本文。
代碼傳送門:喜歡的話,隨手點(diǎn)個star。多謝
https://github.com/mcxtzhang/AnimShopButton
我們撿重點(diǎn)說,無非是繪制。
繪制的重點(diǎn),這里分三塊:
- 靜態(tài)繪制。(分兩塊:加減按鈕和數(shù)量、hint提示文字和背景)
- 第一層。(加減按鈕和數(shù)量)以及它的旋轉(zhuǎn)、位移、透明度動畫
- 第二層。(hint區(qū)域)以及它的伸展收縮動畫
除了繪制以外的重點(diǎn)是:
- 由于采用了完全的自定義View去實(shí)現(xiàn)這么一個“組合控件效果”,則點(diǎn)擊事件的監(jiān)聽需要自己處理。
- 在回收復(fù)用的列表中使用時,列表滑動,如何正確顯示UI。
靜態(tài)繪制
靜態(tài)繪制就是最基本的自定義View知識,繪制圓圈(Circle)、線段(Line)、數(shù)字(Text)以及圓角矩形(RoundRect),值得注意的是,
要考慮到 避免overDraw和動畫的需求,
我們要繪制的兩層應(yīng)該是互斥關(guān)系。
剝離掉動畫代碼,大致如下(基本都是draw代碼,可以快速閱讀):
@Override protected void onDraw(Canvas canvas) { if (isHintMode) { //hint 展開 //背景 mHintPaint.setColor(mHintBgColor); RectF rectF = new RectF(mLeft, mTop , mWidth - mCircleWidth, mHeight - mCircleWidth); canvas.drawRoundRect(rectF, mHintBgRoundValue, mHintBgRoundValue, mHintPaint); //前景文字 mHintPaint.setColor(mHintFgColor); // 計(jì)算Baseline繪制的起點(diǎn)X軸坐標(biāo) int baseX = (int) (mWidth / 2 - mHintPaint.measureText(mHintText) / 2); // 計(jì)算Baseline繪制的Y坐標(biāo) int baseY = (int) ((mHeight / 2) - ((mHintPaint.descent() + mHintPaint.ascent()) / 2)); canvas.drawText(mHintText, baseX, baseY, mHintPaint); } else { //左邊 //背景 圓 if (mCount > 0) { mDelPaint.setColor(mDelEnableBgColor); } else { mDelPaint.setColor(mDelDisableBgColor); } mDelPaint.setStrokeWidth(mCircleWidth); mDelPath.reset(); mDelPath.addCircle(mLeft + mRadius, mTop + mRadius, mRadius, Path.Direction.CW); mDelRegion.setPath(mDelPath, new Region(mLeft, mTop, mWidth - getPaddingRight(), mHeight - getPaddingBottom())); canvas.drawPath(mDelPath, mDelPaint); //前景 - if (mCount > 0) { mDelPaint.setColor(mDelEnableFgColor); } else { mDelPaint.setColor(mDelDisableFgColor); } mDelPaint.setStrokeWidth(mLineWidth); canvas.drawLine(-mRadius / 2, 0, +mRadius / 2, 0, mDelPaint); //數(shù)量 //是沒有動畫的普通寫法,x left, y baseLine canvas.drawText(mCount + "", mLeft + mRadius * 2, mTop + mRadius - (mFontMetrics.top + mFontMetrics.bottom) / 2, mTextPaint); //右邊 //背景 圓 if (mCount < mMaxCount) { mAddPaint.setColor(mAddEnableBgColor); } else { mAddPaint.setColor(mAddDisableBgColor); } mAddPaint.setStrokeWidth(mCircleWidth); float left = mLeft + mRadius * 2 + mGapBetweenCircle; mAddPath.reset(); mAddPath.addCircle(left + mRadius, mTop + mRadius, mRadius, Path.Direction.CW); mAddRegion.setPath(mAddPath, new Region(mLeft, mTop, mWidth - getPaddingRight(), mHeight - getPaddingBottom())); canvas.drawPath(mAddPath, mAddPaint); //前景 + if (mCount < mMaxCount) { mAddPaint.setColor(mAddEnableFgColor); } else { mAddPaint.setColor(mAddDisableFgColor); } mAddPaint.setStrokeWidth(mLineWidth); canvas.drawLine(left + mRadius / 2, mTop + mRadius, left + mRadius / 2 + mRadius, mTop + mRadius, mAddPaint); canvas.drawLine(left + mRadius, mTop + mRadius / 2, left + mRadius, mTop + mRadius / 2 + mRadius, mAddPaint); } }
根據(jù)isHintMode 布爾值變量,區(qū)分是繪制第二層(Hint層)或者第一層(加減按鈕層)。
繪制第二層時沒啥好說的,就是利用canvas.drawRoundRect
,繪制圓角矩形,然后canvas.drawText
繪制hint。
(如果圓角的值足夠大,矩形的寬度足夠小,就變成了圓形。)
繪制第一層時,要根據(jù)當(dāng)前的數(shù)量選擇不同的顏色,注意在繪制加減按鈕的圓圈時,我們是用Path繪制的,這是因?yàn)槲覀冞€需要用Path構(gòu)建Region類,這個類就是我們監(jiān)聽點(diǎn)擊區(qū)域的重點(diǎn)。
點(diǎn)擊事件的監(jiān)聽
在講解動畫之前,我們先說說如何監(jiān)聽點(diǎn)擊的區(qū)域,因?yàn)楸究丶膭赢嬍呛图訙p數(shù)量息息相關(guān)的,而數(shù)量的加減是由點(diǎn)擊相應(yīng)”+ - 按鈕”區(qū)域觸發(fā)的。
所以我們的監(jiān)聽按鈕的點(diǎn)擊事件,其實(shí)就是監(jiān)聽相應(yīng)的”+ - 按鈕”區(qū)域。
上一節(jié)中,我們在繪制”+ - 按鈕”區(qū)域時,通過Path,構(gòu)建了兩個Region類,Region類有個contains(int x, int y)方法如下,通過傳入對應(yīng)觸摸的x、y坐標(biāo),就可知道知否點(diǎn)擊了相應(yīng)區(qū)域。
/** * Return true if the region contains the specified point */ public native boolean contains(int x, int y);
知道了這一點(diǎn),再寫這部分代碼就相當(dāng)簡單了:
@Override public boolean onTouchEvent(MotionEvent event) { int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: //hint模式 if (isHintMode) { onAddClick(); return true; } else { if (mAddRegion.contains((int) event.getX(), (int) event.getY())) { onAddClick(); return true; } else if (mDelRegion.contains((int) event.getX(), (int) event.getY())) { onDelClick(); return true; } } break; case MotionEvent.ACTION_MOVE: break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: break; } return super.onTouchEvent(event); }
hint模式時,我們可以認(rèn)為控件所有范圍都是“+”的有效區(qū)域。
而在非hint模式時,根據(jù)上一節(jié)構(gòu)建的mAddRegion和mDelRegion去判斷。
判斷確認(rèn)點(diǎn)擊后,具體的操作,要根據(jù)業(yè)務(wù)的不同來編寫了,設(shè)計(jì)到實(shí)際的購物車可能還有寫數(shù)據(jù)庫操作,或者請求接口等,要操作成功后才執(zhí)行動畫、或者修改count,這一塊代碼每個人寫法可能不同。
使用時,可以重寫onDelClick()和onAddClick()
方法,并在合適的時機(jī)回調(diào)onCountAddSuccess()和onCountDelSuccess()
以執(zhí)行動畫。
本文如下編寫:
protected void onDelClick() { if (mCount > 0) { mCount--; onCountDelSuccess(); } } protected void onAddClick() { if (mCount < mMaxCount) { mCount++; onCountAddSuccess(); } else { } } /** * 數(shù)量增加成功后,使用者回調(diào) */ public void onCountAddSuccess() { if (mCount == 1) { cancelAllAnim(); mAnimReduceHint.start(); } else { mAnimFraction = 0; invalidate(); } } /** * 數(shù)量減少成功后,使用者回調(diào) */ public void onCountDelSuccess() { if (mCount == 0) { cancelAllAnim(); mAniDel.start(); } else { mAnimFraction = 0; invalidate(); } }
動畫的實(shí)現(xiàn)
這里會用到兩個變量:
//動畫的基準(zhǔn)值 動畫:減 0~1, 加 1~0 // 普通狀態(tài)下是0 protected float mAnimFraction; //提示語收縮動畫 0-1 展開1-0 //普通模式時,應(yīng)該是1, 只在 isHintMode true 才有效 protected float mAnimExpandHintFraction;
依次分析有哪些動畫:
Hint動畫
主要是圓角矩形的展開、收縮。
固定right、bottom,當(dāng)展開時,不斷減少矩形的左起點(diǎn)left坐標(biāo)值,則整個矩形寬度變大,呈現(xiàn)展開。收縮時相反。
代碼:
//背景 mHintPaint.setColor(mHintBgColor); RectF rectF = new RectF(mLeft + (mWidth - mRadius * 2) * mAnimExpandHintFraction, mTop , mWidth - mCircleWidth, mHeight - mCircleWidth); canvas.drawRoundRect(rectF, mHintBgRoundValue, mHintBgRoundValue, mHintPaint);
減按鈕動畫
看起來是旋轉(zhuǎn)、位移、透明度。
那么對于背景的圓圈來說,我們只需要位移、透明度。因?yàn)樗旧硎莻€圓,就不要旋轉(zhuǎn)了。
代碼:
//動畫 mAnimFraction :減 0~1, 加 1~0 , //動畫位移Max, float animOffsetMax = (mRadius * 2 +mGapBetweenCircle); //透明度動畫的基準(zhǔn) int animAlphaMax = 255; int animRotateMax = 360; //左邊 //背景 圓 mDelPaint.setAlpha((int) (animAlphaMax * (1 - mAnimFraction))); mDelPath.reset(); //改變圓心的X坐標(biāo),實(shí)現(xiàn)位移 mDelPath.addCircle(animOffsetMax * mAnimFraction + mLeft + mRadius, mTop + mRadius, mRadius, Path.Direction.CW); canvas.drawPath(mDelPath, mDelPaint);
對于前景的“-”號來說,旋轉(zhuǎn)、位移、透明度都需要做。
這里我們利用canvas.translate() canvas.rotate
做旋轉(zhuǎn)和位移動畫,別忘了 canvas.save()
和 canvas.restore()
恢復(fù)畫布的狀態(tài)。(透明度在上面已經(jīng)設(shè)置過了。)
//前景 - //旋轉(zhuǎn)動畫 canvas.save(); canvas.translate(animOffsetMax * mAnimFraction + mLeft + mRadius, mTop + mRadius); canvas.rotate((int) (animRotateMax * (1 - mAnimFraction))); canvas.drawLine(-mRadius / 2, 0, +mRadius / 2, 0, mDelPaint); canvas.restore();
數(shù)量的動畫
看起來也是旋轉(zhuǎn)、位移、透明度。同樣是利用canvas.translate() canvas.rotate
做旋轉(zhuǎn)和位移動畫。
//數(shù)量 canvas.save(); //平移動畫 canvas.translate(mAnimFraction * (mGapBetweenCircle / 2 - mTextPaint.measureText(mCount + "") / 2 + mRadius), 0); //旋轉(zhuǎn)動畫,旋轉(zhuǎn)中心點(diǎn),x 是繪圖中心,y 是控件中心 canvas.rotate(360 * mAnimFraction, mGapBetweenCircle / 2 + mLeft + mRadius * 2 , mTop + mRadius); //透明度動畫 mTextPaint.setAlpha((int) (255 * (1 - mAnimFraction))); //是沒有動畫的普通寫法,x left, y baseLine canvas.drawText(mCount + "", mGapBetweenCircle / 2 - mTextPaint.measureText(mCount + "") / 2 + mLeft + mRadius * 2, mTop + mRadius - (mFontMetrics.top + mFontMetrics.bottom) / 2, mTextPaint); canvas.restore();
動畫的定義:
動畫是在View初始化時就定義好的,執(zhí)行順序:
- 數(shù)量增加,0-1時,先收縮Hint(第二層)mAnimReduceHint執(zhí)行,完畢后執(zhí)行減按鈕(第一層)進(jìn)入的動畫mAnimAdd。
- 數(shù)量減少,1-0時,先執(zhí)行減按鈕退出的動畫mAniDel,再伸展Hint動畫mAnimExpandHint,完畢后,顯示hint文字。
代碼如下:
//動畫 + mAnimAdd = ValueAnimator.ofFloat(1, 0); mAnimAdd.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mAnimFraction = (float) animation.getAnimatedValue(); invalidate(); } }); mAnimAdd.setDuration(350); //提示語收縮動畫 0-1 mAnimReduceHint = ValueAnimator.ofFloat(0, 1); mAnimReduceHint.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mAnimExpandHintFraction = (float) animation.getAnimatedValue(); invalidate(); } }); mAnimReduceHint.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { if (mCount == 1) { //然后底色也不顯示了 isHintMode = false; } if (mCount == 1) { Log.d(TAG, "現(xiàn)在還是1 開始收縮動畫"); if (mAnimAdd != null && !mAnimAdd.isRunning()) { mAnimAdd.start(); } } } @Override public void onAnimationStart(Animator animation) { if (mCount == 1) { //先不顯示文字了 isShowHintText = false; } } }); mAnimReduceHint.setDuration(350); //動畫 - mAniDel = ValueAnimator.ofFloat(0, 1); mAniDel.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mAnimFraction = (float) animation.getAnimatedValue(); invalidate(); } }); //1-0的動畫 mAniDel.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { if (mCount == 0) { Log.d(TAG, "現(xiàn)在還是0onAnimationEnd() called with: animation = [" + animation + "]"); if (mAnimExpandHint != null && !mAnimExpandHint.isRunning()) { mAnimExpandHint.start(); } } } }); mAniDel.setDuration(350); //提示語展開動畫 //分析這個動畫,最初是個圓。 就是left 不斷減小 mAnimExpandHint = ValueAnimator.ofFloat(1, 0); mAnimExpandHint.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mAnimExpandHintFraction = (float) animation.getAnimatedValue(); invalidate(); } }); mAnimExpandHint.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { if (mCount == 0) { isShowHintText = true; } } @Override public void onAnimationStart(Animator animation) { if (mCount == 0) { isHintMode = true; } } }); mAnimExpandHint.setDuration(350);
針對復(fù)用機(jī)制的處理
因?yàn)槲覀兊馁徫镘嚳丶隙〞迷诹斜碇?,不管你用ListView還是RecyclerView,都會涉及到復(fù)用的問題。
復(fù)用給我們帶來一個麻煩的地方就是,我們要處理好一些屬性狀態(tài)值,否則UI上會有問題。
可以從兩處下手處理:
onMeasure
列表復(fù)用時,依然會回調(diào)onMeasure()方法,所以在這里初始化一些UI顯示的參數(shù)。
這里順帶將適配wrap_content 的代碼也一同貼上:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int wMode = MeasureSpec.getMode(widthMeasureSpec); int wSize = MeasureSpec.getSize(widthMeasureSpec); int hMode = MeasureSpec.getMode(heightMeasureSpec); int hSize = MeasureSpec.getSize(heightMeasureSpec); switch (wMode) { case MeasureSpec.EXACTLY: break; case MeasureSpec.AT_MOST: //不超過父控件給的范圍內(nèi),自由發(fā)揮 int computeSize = (int) (getPaddingLeft() + mRadius * 2 +mGapBetweenCircle + mRadius * 2 + getPaddingRight() + mCircleWidth * 2); wSize = computeSize < wSize ? computeSize : wSize; break; case MeasureSpec.UNSPECIFIED: //自由發(fā)揮 computeSize = (int) (getPaddingLeft() + mRadius * 2 + mGapBetweenCircle + mRadius * 2 + getPaddingRight() + mCircleWidth * 2); wSize = computeSize; break; } switch (hMode) { case MeasureSpec.EXACTLY: break; case MeasureSpec.AT_MOST: int computeSize = (int) (getPaddingTop() + mRadius * 2 + getPaddingBottom() + mCircleWidth * 2); hSize = computeSize < hSize ? computeSize : hSize; break; case MeasureSpec.UNSPECIFIED: computeSize = (int) (getPaddingTop() + mRadius * 2 + getPaddingBottom() + mCircleWidth * 2); hSize = computeSize; break; } setMeasuredDimension(wSize, hSize); //復(fù)用時會走這里,所以初始化一些UI顯示的參數(shù) mAnimFraction = 0; initHintSettings(); } /** * 根據(jù)當(dāng)前count數(shù)量 初始化 hint提示語相關(guān)變量 */ private void initHintSettings() { if (mCount == 0) { isHintMode = true; isShowHintText = true; mAnimExpandHintFraction = 0; } else { isHintMode = false; isShowHintText = false; mAnimExpandHintFraction = 1; } }
在改變count時
一般在onBindViewHolder()或者getView()時,都會對本控件重新設(shè)置count值,count改變時,當(dāng)然也是需要根據(jù)count進(jìn)行屬性值的調(diào)整。
且此時如果View正在做動畫,應(yīng)該停止這些動畫。
/** * 設(shè)置當(dāng)前數(shù)量 * @param count * @return */ public AnimShopButton setCount(int count) { mCount = count; //先暫停所有動畫 if (mAnimAdd != null && mAnimAdd.isRunning()) { mAnimAdd.cancel(); } if (mAniDel != null && mAniDel.isRunning()) { mAniDel.cancel(); } //復(fù)用機(jī)制的處理 if (mCount == 0) { // 0 不顯示 數(shù)字和-號 mAnimFraction = 1; } else { mAnimFraction = 0; } initHintSettings(); return this; }
總結(jié)
代碼傳送門:喜歡的話,隨手點(diǎn)個star。多謝
https://github.com/mcxtzhang/AnimShopButton
我在實(shí)現(xiàn)這個控件時,覺得難度相對大的地方在于做動畫時,“-”按鈕和數(shù)量的旋轉(zhuǎn)動畫,如何確定正確的坐標(biāo)值。因?yàn)閷ext繪制的居中本身就有一些注意事項(xiàng)在里面,再涉及到動畫,難免蒙圈。需要多計(jì)算,多試驗(yàn)。
還有就是觀察餓了么的效果,將hint區(qū)域的動畫利用改變RoundRect的寬度去實(shí)現(xiàn)。起初沒有想到,也是思考了一會如何去做。這是屬于分析、拆解動畫遇到的問題。
除了繪制以外的重點(diǎn)是:
- 利用Region監(jiān)聽區(qū)域點(diǎn)擊事件。
- 復(fù)用的列表,如何正確顯示UI。
- 動畫次序以及考慮到復(fù)用時,在合適的地方取消動畫。
盡情在項(xiàng)目中使用它吧,有問題隨時gayhub給我反饋。
通過sdk工具查看餓了么,它其實(shí)是用TextView和ImageView組合實(shí)現(xiàn)的。另外我十分懷疑它沒有封裝成控件,因?yàn)樵诹斜眄摵驮斍轫摰慕换?,以及動畫居然略有不同?在詳情頁,仔細(xì)看由0-1時,它右邊的 + 按鈕的動畫居然會閃一下,在列表頁卻沒有,很是不解。
好了,本文所述到此結(jié)束。
- Android Tween動畫之RotateAnimation實(shí)現(xiàn)圖片不停旋轉(zhuǎn)效果實(shí)例介紹
- Android開發(fā)之圖形圖像與動畫(二)Animation實(shí)現(xiàn)圖像的漸變/縮放/位移/旋轉(zhuǎn)
- Android編程實(shí)現(xiàn)RotateAnimation設(shè)置中心點(diǎn)旋轉(zhuǎn)動畫效果
- Android 3D旋轉(zhuǎn)動畫效果實(shí)現(xiàn)分解
- Android動畫之漸變動畫(Tween Animation)詳解 (漸變、縮放、位移、旋轉(zhuǎn))
- Android酷炫動畫效果之3D星體旋轉(zhuǎn)效果
- Android旋轉(zhuǎn)、平移、縮放和透明度漸變的補(bǔ)間動畫
- Android使用Rotate3dAnimation實(shí)現(xiàn)3D旋轉(zhuǎn)動畫效果的實(shí)例代碼
- Android仿視頻加載旋轉(zhuǎn)小球動畫效果的實(shí)例代碼
- Android實(shí)現(xiàn)簡單旋轉(zhuǎn)動畫
相關(guān)文章
Android 中 onSaveInstanceState()使用方法詳解
這篇文章主要介紹了Android 中 onSaveInstanceState()使用方法詳解的相關(guān)資料,希望通過本文大家能夠掌握這部分知識,需要的朋友可以參考下2017-09-09Android使用BroadcastReceiver實(shí)現(xiàn)手機(jī)開機(jī)之后顯示畫面的功能
這篇文章主要介紹了Android使用BroadcastReceiver實(shí)現(xiàn)手機(jī)開機(jī)之后顯示畫面的功能,結(jié)合實(shí)例形式分析了BroadcastReceiver的具體使用技巧及實(shí)現(xiàn)開機(jī)畫面的相關(guān)功能代碼,需要的朋友可以參考下2016-01-01Android中 TeaScreenPopupWindow多類型篩選彈框功能的實(shí)例代碼
這篇文章主要介紹了Android TeaScreenPopupWindow多類型篩選彈框功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價值 ,需要的朋友可以參考下2019-06-06Android Studio / IDEA kotlin 顯示 var 真實(shí)類型操作
這篇文章主要介紹了Android Studio / IDEA kotlin 顯示 var 真實(shí)類型操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-08-08Android下拉刷新完全解析,教你如何一分鐘實(shí)現(xiàn)下拉刷新功能(附源碼)
以下是我自己花功夫編寫了一種非常簡單的下拉刷新實(shí)現(xiàn)方案,現(xiàn)在拿出來和大家分享一下。相信在閱讀完本篇文章之后,大家都可以在自己的項(xiàng)目中一分鐘引入下拉刷新功能2013-07-07Android開發(fā)之imageView圖片按比例縮放的實(shí)現(xiàn)方法
這篇文章主要介紹了Android開發(fā)之imageView圖片按比例縮放的實(shí)現(xiàn)方法,較為詳細(xì)的分析了Android中ImageView控件的scaleType屬性控制圖片縮放的具體用法,需要的朋友可以參考下2016-01-01Android自定義View實(shí)現(xiàn)等級滑動條的實(shí)例
這篇文章主要介紹了 Android自定義View實(shí)現(xiàn)等級滑動條的實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-04-04Android實(shí)現(xiàn)調(diào)用系統(tǒng)分享功能示例的總結(jié)
這篇文章主要介紹了通過Android調(diào)用系統(tǒng)分享文本信息、單張圖片、多個文件和指定分享到微信、QQ,同時分享圖片和文字的功能示例,小編覺得挺不錯,一起跟隨小編過來看看吧2018-05-05