Android仿餓了么加入購物車旋轉(zhuǎn)控件自帶閃轉(zhuǎn)騰挪動畫的按鈕效果(實例詳解)
概述
在上文,酷炫Path動畫已經(jīng)預告了,今天給大家?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、ImageV這里寫代碼片iew也可以實現(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:
<!--使用默認UI屬性--> <com.mcxtzhang.lib.AnimShopButton android:id="@+id/btn1" android:layout_width="wrap_content" android:layout_height="wrap_content" app:maxCount="3"/> <!--設置了兩圓間距--> <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"/>
注意:
加減點擊后,具體的操作,要根據(jù)業(yè)務的不同來編寫了,設計到實際的購物車可能還有寫數(shù)據(jù)庫操作,或者請求接口等,要操作成功后才執(zhí)行動畫、或者修改count,這一塊代碼每個人寫法可能不同。
使用時,可以重寫onDelClick()和onAddClick()方法,并在合適的時機回調(diào)onCountAddSuccess()和onCountDelSuccess()以執(zhí)行動畫。
效果圖如圖2.
支持的屬性
| name | format | description | 中文解釋 |
|---|---|---|---|
| isAddFillMode | boolean | Plus button is opened Fill mode default is stroke (false) | 加按鈕是否開啟fill模式 默認是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模式 默認是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 | 當前數(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背景圓角值 |
這么多屬性夠你用了吧。
下面看重點的實現(xiàn)吧,Let's Go!.
實現(xiàn)解剖
關于自定義View的基礎,這里不再贅述。
如果閱讀時有不明白的,建議下載源碼邊看邊讀,或者學習自定義View基礎知識后再閱讀本文。
代碼傳送門:喜歡的話,隨手點個star。多謝
https://github.com/mcxtzhang/AnimShopButton
我們撿重點說,無非是繪制。
繪制的重點,這里分三塊:
- 靜態(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代碼,可以快速閱讀):
@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);
// 計算Baseline繪制的起點X軸坐標
int baseX = (int) (mWidth / 2 - mHintPaint.measureText(mHintText) / 2);
// 計算Baseline繪制的Y坐標
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ù)當前的數(shù)量選擇不同的顏色,注意在繪制加減按鈕的圓圈時,我們是用Path繪制的,這是因為我們還需要用Path構建Region類,這個類就是我們監(jiān)聽點擊區(qū)域的重點。
點擊事件的監(jiān)聽
在講解動畫之前,我們先說說如何監(jiān)聽點擊的區(qū)域,因為本控件的動畫是和加減數(shù)量息息相關的,而數(shù)量的加減是由點擊相應”+ - 按鈕”區(qū)域觸發(fā)的。
所以我們的監(jiān)聽按鈕的點擊事件,其實就是監(jiān)聽相應的”+ - 按鈕”區(qū)域。
上一節(jié)中,我們在繪制”+ - 按鈕”區(qū)域時,通過Path,構建了兩個Region類,Region類有個contains(int x, int y)方法如下,通過傳入對應觸摸的x、y坐標,就可知道知否點擊了相應區(qū)域。
/** * Return true if the region contains the specified point */ public native boolean contains(int x, int y);
知道了這一點,再寫這部分代碼就相當簡單了:
@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模式時,我們可以認為控件所有范圍都是“+”的有效區(qū)域。
而在非hint模式時,根據(jù)上一節(jié)構建的mAddRegion和mDelRegion去判斷。
判斷確認點擊后,具體的操作,要根據(jù)業(yè)務的不同來編寫了,設計到實際的購物車可能還有寫數(shù)據(jù)庫操作,或者請求接口等,要操作成功后才執(zhí)行動畫、或者修改count,這一塊代碼每個人寫法可能不同。
使用時,可以重寫onDelClick()和onAddClick()方法,并在合適的時機回調(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();
}
}
動畫的實現(xiàn)
這里會用到兩個變量:
//動畫的基準值 動畫:減 0~1, 加 1~0 // 普通狀態(tài)下是0 protected float mAnimFraction; //提示語收縮動畫 0-1 展開1-0 //普通模式時,應該是1, 只在 isHintMode true 才有效 protected float mAnimExpandHintFraction;
依次分析有哪些動畫:
Hint動畫
主要是圓角矩形的展開、收縮。
固定right、bottom,當展開時,不斷減少矩形的左起點left坐標值,則整個矩形寬度變大,呈現(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)、位移、透明度。
那么對于背景的圓圈來說,我們只需要位移、透明度。因為它本身是個圓,就不要旋轉(zhuǎn)了。
代碼:
//動畫 mAnimFraction :減 0~1, 加 1~0 , //動畫位移Max, float animOffsetMax = (mRadius * 2 +mGapBetweenCircle); //透明度動畫的基準 int animAlphaMax = 255; int animRotateMax = 360; //左邊 //背景 圓 mDelPaint.setAlpha((int) (animAlphaMax * (1 - mAnimFraction))); mDelPath.reset(); //改變圓心的X坐標,實現(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()恢復畫布的狀態(tài)。(透明度在上面已經(jīng)設置過了。)
//前景 - //旋轉(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)中心點,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í)行減按鈕(第一層)進入的動畫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);
針對復用機制的處理
因為我們的購物車控件肯定會用在列表中,不管你用ListView還是RecyclerView,都會涉及到復用的問題。
復用給我們帶來一個麻煩的地方就是,我們要處理好一些屬性狀態(tài)值,否則UI上會有問題。
可以從兩處下手處理:
onMeasure
列表復用時,依然會回調(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);
//復用時會走這里,所以初始化一些UI顯示的參數(shù)
mAnimFraction = 0;
initHintSettings();
}
/**
* 根據(jù)當前count數(shù)量 初始化 hint提示語相關變量
*/
private void initHintSettings() {
if (mCount == 0) {
isHintMode = true;
isShowHintText = true;
mAnimExpandHintFraction = 0;
} else {
isHintMode = false;
isShowHintText = false;
mAnimExpandHintFraction = 1;
}
}
在改變count時
一般在onBindViewHolder()或者getView()時,都會對本控件重新設置count值,count改變時,當然也是需要根據(jù)count進行屬性值的調(diào)整。
且此時如果View正在做動畫,應該停止這些動畫。
/**
* 設置當前數(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();
}
//復用機制的處理
if (mCount == 0) {
// 0 不顯示 數(shù)字和-號
mAnimFraction = 1;
} else {
mAnimFraction = 0;
}
initHintSettings();
return this;
}
總結(jié)
代碼傳送門:喜歡的話,隨手點個star。多謝
https://github.com/mcxtzhang/AnimShopButton
我在實現(xiàn)這個控件時,覺得難度相對大的地方在于做動畫時,“-”按鈕和數(shù)量的旋轉(zhuǎn)動畫,如何確定正確的坐標值。因為將text繪制的居中本身就有一些注意事項在里面,再涉及到動畫,難免蒙圈。需要多計算,多試驗。
還有就是觀察餓了么的效果,將hint區(qū)域的動畫利用改變RoundRect的寬度去實現(xiàn)。起初沒有想到,也是思考了一會如何去做。這是屬于分析、拆解動畫遇到的問題。
除了繪制以外的重點是:
- 利用Region監(jiān)聽區(qū)域點擊事件。
- 復用的列表,如何正確顯示UI。
- 動畫次序以及考慮到復用時,在合適的地方取消動畫。
盡情在項目中使用它吧,有問題隨時gayhub給我反饋。
通過sdk工具查看餓了么,它其實是用TextView和ImageView組合實現(xiàn)的。另外我十分懷疑它沒有封裝成控件,因為在列表頁和詳情頁的交互,以及動畫居然略有不同, 在詳情頁,仔細看由0-1時,它右邊的 + 按鈕的動畫居然會閃一下,在列表頁卻沒有,很是不解。
好了,本文所述到此結(jié)束。
- Android Tween動畫之RotateAnimation實現(xiàn)圖片不停旋轉(zhuǎn)效果實例介紹
- Android開發(fā)之圖形圖像與動畫(二)Animation實現(xiàn)圖像的漸變/縮放/位移/旋轉(zhuǎn)
- Android編程實現(xiàn)RotateAnimation設置中心點旋轉(zhuǎn)動畫效果
- Android 3D旋轉(zhuǎn)動畫效果實現(xiàn)分解
- Android動畫之漸變動畫(Tween Animation)詳解 (漸變、縮放、位移、旋轉(zhuǎn))
- Android酷炫動畫效果之3D星體旋轉(zhuǎn)效果
- Android旋轉(zhuǎn)、平移、縮放和透明度漸變的補間動畫
- Android使用Rotate3dAnimation實現(xiàn)3D旋轉(zhuǎn)動畫效果的實例代碼
- Android仿視頻加載旋轉(zhuǎn)小球動畫效果的實例代碼
- Android實現(xiàn)簡單旋轉(zhuǎn)動畫
相關文章
Android 中 onSaveInstanceState()使用方法詳解
這篇文章主要介紹了Android 中 onSaveInstanceState()使用方法詳解的相關資料,希望通過本文大家能夠掌握這部分知識,需要的朋友可以參考下2017-09-09
Android使用BroadcastReceiver實現(xiàn)手機開機之后顯示畫面的功能
這篇文章主要介紹了Android使用BroadcastReceiver實現(xiàn)手機開機之后顯示畫面的功能,結(jié)合實例形式分析了BroadcastReceiver的具體使用技巧及實現(xiàn)開機畫面的相關功能代碼,需要的朋友可以參考下2016-01-01
Android中 TeaScreenPopupWindow多類型篩選彈框功能的實例代碼
這篇文章主要介紹了Android TeaScreenPopupWindow多類型篩選彈框功能,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值 ,需要的朋友可以參考下2019-06-06
Android Studio / IDEA kotlin 顯示 var 真實類型操作
這篇文章主要介紹了Android Studio / IDEA kotlin 顯示 var 真實類型操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-08-08
Android下拉刷新完全解析,教你如何一分鐘實現(xiàn)下拉刷新功能(附源碼)
以下是我自己花功夫編寫了一種非常簡單的下拉刷新實現(xiàn)方案,現(xiàn)在拿出來和大家分享一下。相信在閱讀完本篇文章之后,大家都可以在自己的項目中一分鐘引入下拉刷新功能2013-07-07
Android開發(fā)之imageView圖片按比例縮放的實現(xiàn)方法
這篇文章主要介紹了Android開發(fā)之imageView圖片按比例縮放的實現(xiàn)方法,較為詳細的分析了Android中ImageView控件的scaleType屬性控制圖片縮放的具體用法,需要的朋友可以參考下2016-01-01
Android自定義View實現(xiàn)等級滑動條的實例
這篇文章主要介紹了 Android自定義View實現(xiàn)等級滑動條的實例的相關資料,需要的朋友可以參考下2017-04-04
Android實現(xiàn)調(diào)用系統(tǒng)分享功能示例的總結(jié)
這篇文章主要介紹了通過Android調(diào)用系統(tǒng)分享文本信息、單張圖片、多個文件和指定分享到微信、QQ,同時分享圖片和文字的功能示例,小編覺得挺不錯,一起跟隨小編過來看看吧2018-05-05

