仿餓了麼加入購物車旋轉控件 - 自帶閃轉騰挪動畫 的按鈕

本篇文章已受權微信公衆號 guolin_blog (郭霖)獨家發佈 javascript

想經濟上支持我 or 想經過視頻看我是怎麼實現的:java

edu.csdn.net/course/deta…android

概述

上文,酷炫Path動畫已經預告了,今天給你們帶來的是利用 純自定義View,實現的仿餓了麼加入購物車控件,自帶閃轉騰挪動畫的按鈕。
效果圖以下:git

圖1 項目中使用的效果,考慮到了View回收複用
而且能夠看到在RecyclerView中使用,切換LayoutManager也是沒有問題的,
github

項目中使用的效果

圖2 Demo效果,測試各類屬性值
數據庫

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

注意,本控件非繼承自ViewGroup,而是純自定義View實現。理由以下:canvas

  • 1 減小布局層級,從而提升性能
  • 2 文字和圖形純draw,用到什麼draw什麼,沒有其餘的額外工做,也間接提升性能。
  • 3 純自定義View難度更高,更有實(裝)踐(B)的意義

1 減小布局層次,很好理解,ViewGroup內嵌套幾個TextView、ImageV這裏寫代碼片iew也能夠實現這個效果,然而這會使佈局層次多了一級,而且內部要嵌套多個控件,層級越多,控件越多,繪製的就越慢,在列表中對性能的影響更大。微信

2 別小看了「小小」的TextView和的ImageView,其實它們有不少的屬性和特性在本例中是沒必要要的,舉個例子,查看源碼,TextView一萬多行ondraw()方法有一百多行, ImageView1588行,這麼多行代碼都是咱們須要的嗎?直接使用這些現成的控件嵌套實現,其實性能不如咱們用到什麼draw什麼。惟一的好處可能就是比較簡單了。(其實TextView的性能是不高的)app

3 純自定義Viewdraw出這些須要的元素,而且還要考慮動畫,以及點擊各區域的監聽,實現起來仍是有一些難度的,但咱們多寫一些有難度的代碼才能提升水平。ide

轉載請標明出處:
gold.xitu.io/post/587220…
本文出自:【張旭童的稀土掘金】(gold.xitu.io/user/56de21…)
代碼傳送門:喜歡的話,隨手點個star。多謝
github.com/mcxtzhang/A…

如何使用

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

使用

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"/>複製代碼

注意:
加減點擊後,具體的操做,要根據業務的不一樣來編寫了,設計到實際的購物車可能還有寫數據庫操做,或者請求接口等,要操做成功後才執行動畫、或者修改count,這一塊代碼每一個人寫法可能不一樣。
使用時,能夠重寫onDelClick()onAddClick()方法,並在合適的時機回調onCountAddSuccess()onCountDelSuccess()以執行動畫。

效果圖如圖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 繪製數量的textSize
maxCount integer max count 最大數量
count integer current count 當前數量
hintText string The hint text when number is 0 數量爲0時,hint文字
hintBgColor color The hint background when number is 0 數量爲0時,hint背景色
hintFgColor color The hint foreground when number is 0 數量爲0時,hint前景色
hingTextSize dimension The hint text size when number is 0 數量爲0時,hint文字大小
hintBgRoundValue dimension The background fillet value when number is 0 數量爲0時,hint背景圓角值

這麼多屬性夠你用了吧。

下面看重點的實現吧,Let's Go!.

實現解剖

關於自定義View的基礎,這裏再也不贅述。
若是閱讀時有不明白的,建議下載源碼邊看邊讀,或者學習自定義View基礎知識後再閱讀本文。
代碼傳送門:喜歡的話,隨手點個star。多謝
github.com/mcxtzhang/A…


咱們撿重點說,無非是繪製。
繪製的重點,這裏分三塊:

  • 靜態繪製。(分兩塊:加減按鈕和數量、hint提示文字和背景)
  • 第一層。(加減按鈕和數量)以及它的旋轉、位移、透明度動畫
  • 第二層。(hint區域)以及它的伸展收縮動畫

除了繪製之外的重點是:

  • 因爲採用了徹底的自定義View去實現這麼一個「組合控件效果」,則點擊事件的監聽須要本身處理。
  • 在回收複用的列表中使用時,列表滑動,如何正確顯示UI。

靜態繪製

靜態繪製就是最基本的自定義View知識,繪製圓圈(Circle)、線段(Line)、數字(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);

            //數量
            //是沒有動畫的普通寫法,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);
        }
    }複製代碼

根據isHintMode 布爾值變量,區分是繪製第二層(Hint層)或者第一層(加減按鈕層)。

繪製第二層時沒啥好說的,就是利用canvas.drawRoundRect,繪製圓角矩形,而後canvas.drawText繪製hint。
(若是圓角的值足夠大,矩形的寬度足夠小,就變成了圓形。)

繪製第一層時,要根據當前的數量選擇不一樣的顏色,注意在繪製加減按鈕的圓圈時,咱們是用Path繪製的,這是由於咱們還須要用Path構建Region類,這個類就是咱們監聽點擊區域的重點

點擊事件的監聽

在講解動畫以前,咱們先說說如何監聽點擊的區域,由於本控件的動畫是和加減數量息息相關的,而數量的加減是由點擊相應"+ - 按鈕"區域觸發的。
因此咱們的監聽按鈕的點擊事件,其實就是監聽相應的"+ - 按鈕"區域

上一節中,咱們在繪製"+ - 按鈕"區域時,經過Path,構建了兩個Region類,Region類有個contains(int x, int y)方法以下,經過傳入對應觸摸的x、y座標,就可知道知否點擊了相應區域

/** * 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模式時,咱們能夠認爲控件全部範圍都是「+」的有效區域

而在非hint模式時,根據上一節構建的mAddRegionmDelRegion去判斷。

判斷確認點擊後,具體的操做,要根據業務的不一樣來編寫了,設計到實際的購物車可能還有寫數據庫操做,或者請求接口等,要操做成功後才執行動畫、或者修改count,這一塊代碼每一個人寫法可能不一樣。
使用時,能夠重寫onDelClick()onAddClick()方法,並在合適的時機回調onCountAddSuccess()onCountDelSuccess()以執行動畫。

本文以下編寫:

protected void onDelClick() {
        if (mCount > 0) {
            mCount--;
            onCountDelSuccess();
        }
    }

    protected void onAddClick() {
        if (mCount < mMaxCount) {
            mCount++;
            onCountAddSuccess();
        } else {
        }
    }

    /** * 數量增長成功後,使用者回調 */
    public void onCountAddSuccess() {
        if (mCount == 1) {
            cancelAllAnim();
            mAnimReduceHint.start();
        } else {
            mAnimFraction = 0;
            invalidate();
        }
    }

    /** * 數量減小成功後,使用者回調 */
    public void onCountDelSuccess() {
        if (mCount == 0) {
            cancelAllAnim();
            mAniDel.start();
        } else {
            mAnimFraction = 0;
            invalidate();
        }
    }複製代碼

動畫的實現

這裏會用到兩個變量:

//動畫的基準值 動畫:減 0~1, 加 1~0 
    // 普通狀態下是0
    protected float mAnimFraction;

    //提示語收縮動畫 0-1 展開1-0
    //普通模式時,應該是1, 只在 isHintMode true 纔有效
    protected float mAnimExpandHintFraction;複製代碼

依次分析有哪些動畫:

Hint動畫

主要是圓角矩形的展開、收縮
固定right、bottom,當展開時,不斷減小矩形的左起點left座標值,則整個矩形寬度變大,呈現展開。收縮時相反。
代碼:

//背景
            mHintPaint.setColor(mHintBgColor);
            RectF rectF = new RectF(mLeft + (mWidth - mRadius * 2) * mAnimExpandHintFraction, mTop
                    , mWidth - mCircleWidth, mHeight - mCircleWidth);
            canvas.drawRoundRect(rectF, mHintBgRoundValue, mHintBgRoundValue, mHintPaint);複製代碼

減按鈕動畫

看起來是旋轉、位移、透明度
那麼對於背景的圓圈來講,咱們只須要位移、透明度。由於它自己是個圓,就不要旋轉了。
代碼:

//動畫 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座標,實現位移
            mDelPath.addCircle(animOffsetMax * mAnimFraction + mLeft + mRadius, mTop + mRadius, mRadius, Path.Direction.CW);
            canvas.drawPath(mDelPath, mDelPaint);複製代碼

對於前景的「-」號來講,旋轉、位移、透明度都須要作。
這裏咱們利用canvas.translate() canvas.rotate 作旋轉和位移動畫,別忘了 canvas.save()canvas.restore()恢復畫布的狀態。(透明度在上面已經設置過了。)

//前景 -
            //旋轉動畫
            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();複製代碼

數量的動畫

看起來也是旋轉、位移、透明度。一樣是利用canvas.translate() canvas.rotate 作旋轉和位移動畫。

//數量
            canvas.save();
            //平移動畫
            canvas.translate(mAnimFraction * (mGapBetweenCircle / 2 - mTextPaint.measureText(mCount + "") / 2 + mRadius), 0);
            //旋轉動畫,旋轉中心點,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初始化時就定義好的,執行順序:

  • 數量增長,0-1時,先收縮Hint(第二層)mAnimReduceHint執行,完畢後執行減按鈕(第一層)進入的動畫mAnimAdd
  • 數量減小,1-0時,先執行減按鈕退出的動畫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, "如今仍是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, "如今仍是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,都會涉及到複用的問題。
複用給咱們帶來一個麻煩的地方就是,咱們要處理好一些屬性狀態值,不然UI上會有問題。

能夠從兩處下手處理:

onMeasure

列表複用時,依然會回調onMeasure()方法,因此在這裏初始化一些UI顯示的參數。
這裏順帶將適配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:
                //不超過父控件給的範圍內,自由發揮
                int computeSize = (int) (getPaddingLeft() + mRadius * 2 +mGapBetweenCircle + mRadius * 2 + getPaddingRight() + mCircleWidth * 2);
                wSize = computeSize < wSize ? computeSize : wSize;
                break;
            case MeasureSpec.UNSPECIFIED:
                //自由發揮
                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顯示的參數
        mAnimFraction = 0;
        initHintSettings();
    }複製代碼
/** * 根據當前count數量 初始化 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改變時,固然也是須要根據count進行屬性值的調整。
且此時若是View正在作動畫,應該中止這些動畫。

/** * 設置當前數量 * @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 不顯示 數字和-號
            mAnimFraction = 1;
        } else {
            mAnimFraction = 0;
        }
        initHintSettings();
        return this;
    }複製代碼

總結

代碼傳送門:喜歡的話,隨手點個star。多謝
github.com/mcxtzhang/A…

我在實現這個控件時,以爲難度相對大的地方在於作動畫時,「-」按鈕和數量的旋轉動畫,如何肯定正確的座標值。由於將text繪製的居中自己就有一些注意事項在裏面,再涉及到動畫,不免蒙圈。須要多計算,多試驗

還有就是觀察餓了麼的效果,將hint區域的動畫利用改變RoundRect的寬度去實現。起初沒有想到,也是思考了一會如何去作。這是屬於分析、拆解動畫遇到的問題。

除了繪製之外的重點是:

  • 利用Region監聽區域點擊事件
  • 複用的列表,如何正確顯示UI。
  • 動畫次序以及考慮到複用時,在合適的地方取消動畫

盡情在項目中使用它吧,有問題隨時gayhub給我反饋。

經過sdk工具查看餓了麼,它實際上是用TextViewImageView組合實現的。另外我十分懷疑它沒有封裝成控件,由於在列表頁和詳情頁的交互,以及動畫竟然略有不一樣, 在詳情頁,仔細看由0-1時,它右邊的 + 按鈕的動畫竟然會閃一下,在列表頁卻沒有,非常不解。

看大神們都有QQ羣,
向他們靠齊。
我也建了個QQ搞基交流羣:
557266366 。

轉載請標明出處:
gold.xitu.io/post/587220…
本文出自:【張旭童的稀土掘金】(gold.xitu.io/user/56de21…)
代碼傳送門:喜歡的話,隨手點個star。多謝
github.com/mcxtzhang/A…

相關文章
相關標籤/搜索