仿饿了么加入购物车旋转控件 - 自带闪转腾挪动画 的按钮

תÔØÇë±êÃ÷³ö´¦£º 
http://blog.csdn.net/zxt0601/article/details/54235736 
±¾Îijö×Ô:¡¾ÕÅÐñͯµÄ²©¿Í¡¿(http://blog.csdn.net/zxt0601
´úÂë´«ËÍÃÅ£ºÏ²»¶µÄ»°£¬ËæÊÖµã¸östar¡£¶àл 
https://github.com/mcxtzhang/AnimShopButton

Ïë¾­¼ÃÉÏÖ§³ÖÎÒ or Ïëͨ¹ýÊÓƵ¿´ÎÒÊÇÔõôʵÏÖµÄ:

http://edu.csdn.net/course/detail/3898

¸ÅÊö

ÔÚÉÏÎÄ£¬¿áìÅPath¶¯»­ÒѾ­Ô¤¸æÁË£¬½ñÌì¸ø´ó¼Ò´øÀ´µÄÊÇÀûÓà´¿×Ô¶¨ÒåView£¬ÊµÏֵķ¶öÁËô¼ÓÈ빺Îï³µ¿Ø¼þ£¬×Ô´øÉÁתÌÚŲ¶¯»­µÄ°´Å¥¡£ 
Ч¹ûͼÈçÏ£º

ͼ1 ÏîÄ¿ÖÐʹÓõÄЧ¹û£¬¿¼Âǵ½ÁËViewµÄ**»ØÊÕ¸´ÓÃ**£¬ 
²¢ÇÒ¿ÉÒÔ¿´µ½ÔÚRecyclerViewÖÐʹÓã¬Çл»LayoutManagerÒ²ÊÇûÓÐÎÊÌâµÄ£¬ 
ÏîÄ¿ÖÐʹÓõÄЧ¹û

ͼ2 DemoЧ¹û£¬²âÊÔ¸÷ÖÖÊôÐÔÖµ 
ͼ2 DemoЧ¹û£¬²âÊÔ¸÷ÖÖÊôÐÔÖµ

×¢Ò⣬±¾¿Ø¼þ·Ç¼Ì³Ð×ÔViewGroup,¶øÊÇ´¿×Ô¶¨ÒåViewʵÏÖ¡£ÀíÓÉÈçÏ£º

  • **1 ¼õÉÙ²¼¾Ö²ã¼¶**£¬´Ó¶øÌá¸ßÐÔÄÜ

  • 2 ÎÄ×ÖºÍͼÐδ¿draw£¬Óõ½Ê²Ã´drawʲô£¬Ã»ÓÐÆäËûµÄ¶îÍ⹤×÷£¬Ò²¼ä½ÓÌá¸ßÐÔÄÜ¡£

  • 3 ´¿×Ô¶¨ÒåViewÄѶȸü¸ß£¬¸üÓÐʵ(×°)¼ù(B)µÄÒâÒå

1 ¼õÉÙ²¼¾Ö²ã´Î£¬ºÜºÃÀí½â£¬ViewGroupÄÚǶÌ×¼¸¸öTextView¡¢ImageVÕâÀïд´úÂëƬiewÒ²¿ÉÒÔʵÏÖÕâ¸öЧ¹û£¬È»¶øÕâ»áʹ²¼¾Ö²ã´Î¶àÁËÒ»¼¶£¬²¢ÇÒÄÚ²¿ÒªÇ¶Ì׶à¸ö¿Ø¼þ£¬²ã¼¶Ô½¶à£¬¿Ø¼þÔ½¶à£¬»æÖƵľÍÔ½Âý£¬ÔÚÁбíÖжÔÐÔÄܵÄÓ°Ïì¸ü´ó¡£

2 ±ðС¿´ÁË¡°Ð¡Ð¡¡±µÄTextViewºÍµÄImageView£¬ÆäʵËüÃÇÓкܶàµÄÊôÐÔºÍÌØÐÔÔÚ±¾ÀýÖÐÊDz»±ØÒªµÄ£¬¾Ù¸öÀý×Ó£¬²é¿´Ô´Â룬TextViewÓÐÒ»Íò¶àÐУ¬ondraw()·½·¨ÓÐÒ»°Ù¶àÐУ¬ ImageViewÓÐ1588ÐУ¬Õâô¶àÐдúÂ붼ÊÇÎÒÃÇÐèÒªµÄÂð£¿Ö±½ÓʹÓÃÕâЩÏֳɵĿؼþǶÌ×ʵÏÖ£¬ÆäʵÐÔÄܲ»ÈçÎÒÃÇÓõ½Ê²Ã´drawʲô¡£Î¨Ò»µÄºÃ´¦¿ÉÄܾÍÊDZȽϼòµ¥ÁË¡££¨ÆäʵTextViewµÄÐÔÄÜÊDz»¸ßµÄ£©

3 ´¿×Ô¶¨ÒåView£¬draw³öÕâЩÐèÒªµÄÔªËØ£¬²¢ÇÒ»¹Òª¿¼ÂǶ¯»­£¬ÒÔ¼°µã»÷¸÷ÇøÓòµÄ¼àÌý£¬ÊµÏÖÆðÀ´»¹ÊÇÓÐһЩÄѶȵģ¬µ«ÎÒÃǶàдһЩÓÐÄѶȵĴúÂë²ÅÄÜÌá¸ßˮƽ¡£

ÈçºÎʹÓÃ

ÉìÊÖµ³¸£Àû£º½²½âʵÏÖÇ°£¬ÏÈ¿´Ò»ÏÂÈçºÎʹÓàÒÔ¼°Ö§³ÖµÄÊôÐԵȡ£

ʹÓÃ

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"/>1234567891011121314151617181920212223242512345678910111213141516171819202122232425

×¢Ò⣺ 
¼Ó¼õµã»÷ºó£¬¾ßÌåµÄ²Ù×÷£¬Òª¸ù¾ÝÒµÎñµÄ²»Í¬À´±àдÁË£¬Éè¼Æµ½Êµ¼ÊµÄ¹ºÎï³µ¿ÉÄÜ»¹ÓÐдÊý¾Ý¿â²Ù×÷£¬»òÕßÇëÇó½Ó¿ÚµÈ£¬Òª²Ù×÷³É¹¦ºó²ÅÖ´Ðж¯»­¡¢»òÕßÐÞ¸Äcount£¬ÕâÒ»¿é´úÂëÿ¸öÈËд·¨¿ÉÄܲ»Í¬¡£ 
ʹÓÃʱ£¬¿ÉÒÔÖØдonDelClick()ºÍonAddClick()·½·¨£¬²¢ÔÚºÏÊʵÄʱ»ú»Øµ÷onCountAddSuccess()ºÍonCountDelSuccess()ÒÔÖ´Ðж¯»­¡£

Ч¹ûͼÈçͼ2.

Ö§³ÖµÄÊôÐÔ

nameformatdescriptionÖÐÎĽâÊÍ
isAddFillModebooleanPlus button is opened Fill mode default is stroke (false)¼Ó°´Å¥ÊÇ·ñ¿ªÆôfillģʽ ĬÈÏÊÇstroke(false)
addEnableBgColorcolorThe background color of the plus button¼Ó°´Å¥µÄ±³¾°É«
addEnableFgColorcolorThe foreground color of the plus button¼Ó°´Å¥µÄÇ°¾°É«
addDisableBgColorcolorThe background color when the button is not available¼Ó°´Å¥²»¿ÉÓÃʱµÄ±³¾°É«
addDisableFgColorcolorThe foreground color when the button is not available¼Ó°´Å¥²»¿ÉÓÃʱµÄÇ°¾°É«
isDelFillModebooleanPlus button is opened Fill mode default is stroke (false)¼õ°´Å¥ÊÇ·ñ¿ªÆôfillģʽ ĬÈÏÊÇstroke(false)
delEnableBgColorcolorThe background color of the minus button¼õ°´Å¥µÄ±³¾°É«
delEnableFgColorcolorThe foreground color of the minus button¼õ°´Å¥µÄÇ°¾°É«
delDisableBgColorcolorThe background color when the button is not available¼õ°´Å¥²»¿ÉÓÃʱµÄ±³¾°É«
delDisableFgColorcolorThe foreground color when the button is not available¼õ°´Å¥²»¿ÉÓÃʱµÄÇ°¾°É«
radiusdimensionThe radius of the circleÔ²µÄ°ë¾¶
circleStrokeWidthdimensionThe width of the circleԲȦµÄ¿í¶È
lineWidthdimensionThe width of the line (+ - sign)Ïß(+ - ·ûºÅ)µÄ¿í¶È
gapBetweenCircledimensionThe spacing between two circlesÁ½¸öÔ²Ö®¼äµÄ¼ä¾à
numTextSizedimensionThe textSize of draws the number»æÖÆÊýÁ¿µÄtextSize
maxCountintegermax count×î´óÊýÁ¿
countintegercurrent countµ±Ç°ÊýÁ¿
hintTextstringThe hint text when number is 0ÊýÁ¿Îª0ʱ£¬hintÎÄ×Ö
hintBgColorcolorThe hint background when number is 0ÊýÁ¿Îª0ʱ£¬hint±³¾°É«
hintFgColorcolorThe hint foreground when number is 0ÊýÁ¿Îª0ʱ£¬hintÇ°¾°É«
hingTextSizedimensionThe hint text size when number is 0ÊýÁ¿Îª0ʱ£¬hintÎÄ×Ö´óС
hintBgRoundValuedimensionThe background fillet value when number is 0ÊýÁ¿Îª0ʱ£¬hint±³¾°Ô²½ÇÖµ

Õâô¶àÊôÐÔ¹»ÄãÓÃÁË°É¡£

ÏÂÃæ¿´ÖصãµÄʵÏÖ°É£¬Let¡¯s Go!.

ʵÏÖ½âÆÊ

¹ØÓÚ×Ô¶¨ÒåViewµÄ»ù´¡£¬ÕâÀï²»ÔÙ׸Êö¡£ 
Èç¹ûÔĶÁʱÓв»Ã÷°×µÄ£¬½¨ÒéÏÂÔØÔ´Âë±ß¿´±ß¶Á£¬»òÕßѧϰ×Ô¶¨ÒåView»ù´¡ÖªÊ¶ºóÔÙÔĶÁ±¾ÎÄ¡£ 
´úÂë´«ËÍÃÅ£ºÏ²»¶µÄ»°£¬ËæÊÖµã¸östar¡£¶àл 
https://github.com/mcxtzhang/AnimShopButton


ÎÒÃǼñÖصã˵£¬ÎÞ·ÇÊÇ»æÖÆ¡£ 
»æÖƵÄÖص㣬ÕâÀï·ÖÈý¿é£º

  • ¾²Ì¬»æÖÆ¡££¨·ÖÁ½¿é£º¼Ó¼õ°´Å¥ºÍÊýÁ¿¡¢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);
        }
    }123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869

¸ù¾Ý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);12341234

ÖªµÀÁËÕâÒ»µã£¬ÔÙдÕⲿ·Ö´úÂë¾ÍÏ൱¼òµ¥ÁË£º

    @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);
    }123456789101112131415161718192021222324252627123456789101112131415161718192021222324252627

hintģʽʱ£¬ÎÒÃÇ¿ÉÒÔÈÏΪ¿Ø¼þËùÓз¶Î§¶¼ÊÇ¡°+¡±µÄÓÐЧÇøÓò¡£

¶øÔÚ·Çhintģʽʱ£¬¸ù¾ÝÉÏÒ»½Ú¹¹½¨µÄmAddRegionºÍmDelRegionÈ¥Åжϡ£

ÅжÏÈ·Èϵã»÷ºó£¬¾ßÌåµÄ²Ù×÷£¬Òª¸ù¾ÝÒµÎñµÄ²»Í¬À´±àдÁË£¬Éè¼Æµ½Êµ¼ÊµÄ¹ºÎï³µ¿ÉÄÜ»¹ÓÐдÊý¾Ý¿â²Ù×÷£¬»òÕßÇëÇó½Ó¿ÚµÈ£¬Òª²Ù×÷³É¹¦ºó²ÅÖ´Ðж¯»­¡¢»òÕßÐÞ¸Ä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();
        }
    }1234567891011121314151617181920212223242526272829303132333435363738394012345678910111213141516171819202122232425262728293031323334353637383940

¶¯»­µÄʵÏÖ

ÕâÀï»áÓõ½Á½¸ö±äÁ¿£º

    //¶¯»­µÄ»ù×¼Öµ ¶¯»­£º¼õ 0~1, ¼Ó 1~0 
    // ÆÕͨ״̬ÏÂÊÇ0
    protected float mAnimFraction;    //ÌáʾÓïÊÕËõ¶¯»­ 0-1 Õ¹¿ª1-0
    //ÆÕͨģʽʱ£¬Ó¦¸ÃÊÇ1£¬ Ö»ÔÚ isHintMode true ²ÅÓÐЧ
    protected float mAnimExpandHintFraction;12345671234567

ÒÀ´Î·ÖÎöÓÐÄÄЩ¶¯»­£º

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);1234512345

¼õ°´Å¥¶¯»­

¿´ÆðÀ´ÊÇÐýת¡¢Î»ÒÆ¡¢Í¸Ã÷¶È¡£ 
ÄÇô¶ÔÓÚ±³¾°µÄԲȦÀ´Ëµ£¬ÎÒÃÇÖ»ÐèҪλÒÆ¡¢Í¸Ã÷¶È¡£ÒòΪËü±¾ÉíÊǸöÔ²£¬¾Í²»ÒªÐýתÁË¡£ 
´úÂ룺

            //¶¯»­ 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);12345678910111213141234567891011121314

¶ÔÓÚÇ°¾°µÄ¡°-¡±ºÅÀ´Ëµ£¬Ðýת¡¢Î»ÒÆ¡¢Í¸Ã÷¶È¶¼ÐèÒª×ö¡£ 
ÕâÀïÎÒÃÇÀûÓÃ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();123456789123456789

ÊýÁ¿µÄ¶¯»­

¿´ÆðÀ´Ò²ÊÇÐýת¡¢Î»ÒÆ¡¢Í¸Ã÷¶È¡£Í¬ÑùÊÇÀûÓÃ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();1234567891011121312345678910111213

¶¯»­µÄ¶¨Ò壺

¶¯»­ÊÇÔÚ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);12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091921234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192

Õë¶Ô¸´ÓûúÖƵĴ¦Àí

ÒòΪÎÒÃǵĹºÎï³µ¿Ø¼þ¿Ï¶¨»áÓÃÔÚÁбíÖУ¬²»¹ÜÄãÓÃ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();
    }1234567891011121314151617181920212223242526272829303132333435363712345678910111213141516171819202122232425262728293031323334353637
    /**
     * ¸ù¾Ýµ±Ç°countÊýÁ¿ ³õʼ»¯ hintÌáʾÓïÏà¹Ø±äÁ¿
     */
    private void initHintSettings() {        if (mCount == 0) {
            isHintMode = true;
            isShowHintText = true;
            mAnimExpandHintFraction = 0;
        } else {
            isHintMode = false;
            isShowHintText = false;
            mAnimExpandHintFraction = 1;
        }
    }12345678910111213141234567891011121314

Ôڸıä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;
    }123456789101112131415161718192021222324123456789101112131415161718192021222324

×ܽá

´úÂë´«ËÍÃÅ£ºÏ²»¶µÄ»°£¬ËæÊÖµã¸östar¡£¶àл 
https://github.com/mcxtzhang/AnimShopButton

ÎÒÔÚʵÏÖÕâ¸ö¿Ø¼þʱ£¬¾õµÃÄѶÈÏà¶Ô´óµÄµØ·½ÔÚÓÚ×ö¶¯»­Ê±£¬¡°-¡±°´Å¥ºÍÊýÁ¿µÄÐýת¶¯»­£¬ÈçºÎÈ·¶¨ÕýÈ·µÄ×ø±êÖµ¡£ÒòΪ½«text»æÖƵľÓÖб¾Éí¾ÍÓÐһЩעÒâÊÂÏîÔÚÀïÃ棬ÔÙÉæ¼°µ½¶¯»­£¬ÄÑÃâÃÉȦ¡£ÐèÒª**¶à¼ÆË㣬¶àÊÔÑé**¡£

»¹ÓоÍÊǹ۲ì¶öÁËôµÄЧ¹û£¬½«hintÇøÓòµÄ¶¯»­ÀûÓøıäRoundRectµÄ¿í¶ÈȥʵÏÖ¡£Æð³õûÓÐÏëµ½£¬Ò²ÊÇ˼¿¼ÁËÒ»»áÈçºÎÈ¥×ö¡£ÕâÊÇÊôÓÚ·ÖÎö¡¢²ð½â¶¯»­Óöµ½µÄÎÊÌâ¡£

³ýÁË»æÖÆÒÔÍâµÄÖصãÊÇ£º

  • ÀûÓÃRegion¼àÌýÇøÓòµã»÷ʼþ¡£

  • ¸´ÓõÄÁÐ±í£¬ÈçºÎÕýÈ·ÏÔʾUI¡£

  • ¶¯»­´ÎÐòÒÔ¼°¿¼Âǵ½¸´ÓÃʱ£¬ÔÚºÏÊʵĵط½È¡Ïû¶¯»­¡£

¾¡ÇéÔÚÏîÄ¿ÖÐʹÓÃËü°É£¬ÓÐÎÊÌâËæʱgayhub¸øÎÒ·´À¡¡£

ͨ¹ýsdk¹¤¾ß²é¿´¶öÁËô£¬ËüÆäʵÊÇÓÃTextViewºÍImageView×éºÏʵÏֵġ£ÁíÍâÎÒÊ®·Ö»³ÒÉËüûÓзâ×°³É¿Ø¼þ£¬ÒòΪÔÚÁбíÒ³ºÍÏêÇéÒ³µÄ½»»¥£¬ÒÔ¼°¶¯»­¾ÓÈ»ÂÔÓв»Í¬£¬ ÔÚÏêÇéÒ³£¬×Ðϸ¿´ÓÉ0-1ʱ£¬ËüÓÒ±ßµÄ + °´Å¥µÄ¶¯»­¾ÓÈ»»áÉÁһϣ¬ÔÚÁбíҳȴûÓУ¬ºÜÊDz»½â¡£

¿´´óÉñÃǶ¼ÓÐQQȺ£¬ 
ÏòËûÃÇ¿¿Æë¡£ 
ÎÒÒ²½¨Á˸öQQ¸ã»ù½»Á÷Ⱥ£º 
557266366 ¡£

תÔØÇë±êÃ÷³ö´¦£º 
http://blog.csdn.net/zxt0601/article/details/54235736 
±¾Îijö×Ô:¡¾ÕÅÐñͯµÄ²©¿Í¡¿(http://blog.csdn.net/zxt0601
´úÂë´«ËÍÃÅ£ºÏ²»¶µÄ»°£¬ËæÊÖµã¸östar¡£¶àл 
https://github.com/mcxtzhang/AnimShopButton