添加购物车控件(增加或减少数字)有动画效果

来源:互联网 发布:网站维护页面源码 编辑:程序博客网 时间:2024/05/29 08:36

最近在做一个关于商城的项目,有一个添加购物车的功能,我们的UI给出来的东西是很好的,效果是很好的,但是完全不考虑我们程序员好不好容易实现,不过在坚持努力下,还是完成了:下面先来看一下效果图片:


话不多说了  下面来看一下实现方式吧:

public class RxShoppingView extends View {    private final static int STATE_NONE = 0;    private final static int STATE_MOVE = 1;    private final static int STATE_MOVE_OVER = 2;    private final static int STATE_ROTATE = 3;    private final static int STATE_ROTATE_OVER = 4;    private final static int DEFAULT_DURATION = 250;    private final static String DEFAULT_SHOPPING_TEXT = "加入购物车";    private Paint mPaintBg, mPaintText, mPaintNum;    private Paint mPaintMinus;    //是否是向前状态(= = 名字不好取,意思就是区分向前和回退状态)    private boolean mIsForward = true;    //动画时长    private int mDuration;    //购买数量    private int mNum = 0;    //展示文案    private String mShoppingText;    //当前状态    private int mState = STATE_NONE;    //属性值    private int mWidth = 0;    private int mAngle = 0;    private int mTextPosition = 0;    private int mMinusBtnPosition = 0;    private int mAlpha = 0;    private int MAX_WIDTH;    private int MAX_HEIGHT;    private ShoppingClickListener mShoppingClickListener;    public RxShoppingView(Context context) {        this(context, null);    }    public RxShoppingView(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public RxShoppingView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init(attrs);    }    private void init(AttributeSet attrs) {        TypedArray typeArray = getContext().obtainStyledAttributes(attrs,                R.styleable.ShoppingView);        mDuration = typeArray.getInt(R.styleable.ShoppingView_sv_duration, DEFAULT_DURATION);        mShoppingText = TextUtils.isEmpty(typeArray.getString(R.styleable.ShoppingView_sv_text)) ? DEFAULT_SHOPPING_TEXT : typeArray.getString(R.styleable.ShoppingView_sv_text);        //展示文案大小        int textSize = (int) typeArray.getDimension(R.styleable.ShoppingView_sv_text_size, sp2px(16));        //背景色        int bgColor = typeArray.getColor(R.styleable.ShoppingView_sv_bg_color, ContextCompat.getColor(getContext(), R.color.slateblue));        typeArray.recycle();        mPaintBg = new Paint();        mPaintBg.setColor(bgColor);        mPaintBg.setStyle(Paint.Style.FILL);        mPaintBg.setAntiAlias(true);        mPaintMinus = new Paint();        mPaintMinus.setColor(bgColor);        mPaintMinus.setStyle(Paint.Style.STROKE);        mPaintMinus.setAntiAlias(true);        mPaintMinus.setStrokeWidth(textSize / 6);        mPaintText = new Paint();        mPaintText.setColor(Color.WHITE);        mPaintText.setStrokeWidth(textSize / 6);        mPaintText.setTextSize(textSize);        mPaintText.setAntiAlias(true);        mPaintNum = new Paint();        mPaintNum.setColor(Color.BLACK);        mPaintNum.setTextSize(textSize / 3 * 4);        mPaintNum.setStrokeWidth(textSize / 6);        mPaintNum.setAntiAlias(true);        MAX_WIDTH = getTextWidth(mPaintText, mShoppingText) / 5 * 8;        MAX_HEIGHT = textSize * 2;        if (MAX_WIDTH / (float) MAX_HEIGHT < 3.5) {            MAX_WIDTH = (int) (MAX_HEIGHT * 3.5);        }        mTextPosition = MAX_WIDTH / 2;        mMinusBtnPosition = MAX_HEIGHT / 2;    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        setMeasuredDimension(MAX_WIDTH, MAX_HEIGHT);    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        if (mState == STATE_NONE) {            drawBgMove(canvas);            drawShoppingText(canvas);        } else if (mState == STATE_MOVE) {            drawBgMove(canvas);        } else if (mState == STATE_MOVE_OVER) {            mState = STATE_ROTATE;            if (mIsForward) {                drawAddBtn(canvas);                startRotateAnim();            } else {                drawBgMove(canvas);                drawShoppingText(canvas);                mState = STATE_NONE;                mIsForward = true;                mNum = 0;            }        } else if (mState == STATE_ROTATE) {            mPaintMinus.setAlpha(mAlpha);            mPaintNum.setAlpha(mAlpha);            drawMinusBtn(canvas, mAngle);            drawNumText(canvas);            drawAddBtn(canvas);        } else if (mState == STATE_ROTATE_OVER) {            drawMinusBtn(canvas, mAngle);            drawNumText(canvas);            drawAddBtn(canvas);            if (!mIsForward) {                startMoveAnim();            }        }    }    /**     * 绘制移动的背景     *     * @param canvas 画板     */    private void drawBgMove(Canvas canvas) {        canvas.drawArc(new RectF(mWidth, 0, mWidth + MAX_HEIGHT, MAX_HEIGHT), 90, 180, false, mPaintBg);        canvas.drawRect(new RectF(mWidth + MAX_HEIGHT / 2, 0, MAX_WIDTH - MAX_HEIGHT / 2, MAX_HEIGHT), mPaintBg);        canvas.drawArc(new RectF(MAX_WIDTH - MAX_HEIGHT, 0, MAX_WIDTH, MAX_HEIGHT), 180, 270, false, mPaintBg);    }    /**     * 绘制购物车文案     *     * @param canvas 画板     */    private void drawShoppingText(Canvas canvas) {        canvas.drawText(mShoppingText, MAX_WIDTH / 2 - getTextWidth(mPaintText, mShoppingText) / 2f, MAX_HEIGHT / 2 + getTextHeight(mShoppingText, mPaintText) / 2f, mPaintText);    }    /**     * 绘制加号按钮     *     * @param canvas 画板     */    private void drawAddBtn(Canvas canvas) {        canvas.drawCircle(MAX_WIDTH - MAX_HEIGHT / 2, MAX_HEIGHT / 2, MAX_HEIGHT / 2, mPaintBg);        canvas.drawLine(MAX_WIDTH - MAX_HEIGHT / 2, MAX_HEIGHT / 4, MAX_WIDTH - MAX_HEIGHT / 2, MAX_HEIGHT / 4 * 3, mPaintText);        canvas.drawLine(MAX_WIDTH - MAX_HEIGHT / 2 - MAX_HEIGHT / 4, MAX_HEIGHT / 2, MAX_WIDTH - MAX_HEIGHT / 4, MAX_HEIGHT / 2, mPaintText);    }    /**     * 绘制减号按钮     *     * @param canvas 画板     * @param angle  旋转角度     */    private void drawMinusBtn(Canvas canvas, float angle) {        if (angle != 0) {            canvas.rotate(angle, mMinusBtnPosition, MAX_HEIGHT / 2);        }        canvas.drawCircle(mMinusBtnPosition, MAX_HEIGHT / 2, MAX_HEIGHT / 2 - MAX_HEIGHT / 20, mPaintMinus);        canvas.drawLine(mMinusBtnPosition - MAX_HEIGHT / 4, MAX_HEIGHT / 2, mMinusBtnPosition + MAX_HEIGHT / 4, MAX_HEIGHT / 2, mPaintMinus);        if (angle != 0) {            canvas.rotate(-angle, mMinusBtnPosition, MAX_HEIGHT / 2);        }    }    /**     * 绘制购买数量     *     * @param canvas 画板     */    private void drawNumText(Canvas canvas) {        drawText(canvas, String.valueOf(mNum), mTextPosition - getTextWidth(mPaintNum, String.valueOf(mNum)) / 2f, MAX_HEIGHT / 2 + getTextHeight(String.valueOf(mNum), mPaintNum) / 2f, mPaintNum, mAngle);    }    /**     * 绘制Text带角度     *     * @param canvas 画板     * @param text   文案     * @param x      x坐标     * @param y      y坐标     * @param paint  画笔     * @param angle  旋转角度     */    private void drawText(Canvas canvas, String text, float x, float y, Paint paint, float angle) {        if (angle != 0) {            canvas.rotate(angle, x, y);        }        canvas.drawText(text, x, y, paint);        if (angle != 0) {            canvas.rotate(-angle, x, y);        }    }    @Override    public boolean onTouchEvent(MotionEvent event) {        switch (event.getAction()) {            case MotionEvent.ACTION_DOWN:                if (mState == STATE_NONE) {                    mNum++;                    startMoveAnim();                    if (mShoppingClickListener != null) {                        mShoppingClickListener.onAddClick(mNum);                    }                } else if (mState == STATE_ROTATE_OVER) {                    if (isPointInCircle(new PointF(event.getX(), event.getY()), new PointF(MAX_WIDTH - MAX_HEIGHT / 2, MAX_HEIGHT / 2), MAX_HEIGHT / 2)) {                        if (mNum > 0) {                            mNum++;                            mIsForward = true;                            if (mShoppingClickListener != null) {                                mShoppingClickListener.onAddClick(mNum);                            }                        }                        invalidate();                    } else if (isPointInCircle(new PointF(event.getX(), event.getY()), new PointF(MAX_HEIGHT / 2, MAX_HEIGHT / 2), MAX_HEIGHT / 2)) {                        if (mNum > 1) {                            mNum--;                            if (mShoppingClickListener != null) {                                mShoppingClickListener.onMinusClick(mNum);                            }                            invalidate();                        } else {                            if (mShoppingClickListener != null) {                                mShoppingClickListener.onMinusClick(0);                            }                            mState = STATE_ROTATE;                            mIsForward = false;                            startRotateAnim();                        }                    }                }                return true;        }        return super.onTouchEvent(event);    }    /**     * 开始移动动画     */    private void startMoveAnim() {        mState = STATE_MOVE;        ValueAnimator valueAnimator;        if (mIsForward) {            valueAnimator = ValueAnimator.ofInt(0, MAX_WIDTH - MAX_HEIGHT);        } else {            valueAnimator = ValueAnimator.ofInt(MAX_WIDTH - MAX_HEIGHT, 0);        }        valueAnimator.setDuration(mDuration);        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator valueAnimator) {                mWidth = (Integer) valueAnimator.getAnimatedValue();                if (mIsForward) {                    if (mWidth == MAX_WIDTH - MAX_HEIGHT) {                        mState = STATE_MOVE_OVER;                    }                } else {                    if (mWidth == 0) {                        mState = STATE_MOVE_OVER;                    }                }                invalidate();            }        });        valueAnimator.start();    }    /**     * 开始旋转动画     */    private void startRotateAnim() {        Collection<Animator> animatorList = new ArrayList<>();        ValueAnimator animatorTextRotate;        if (mIsForward) {            animatorTextRotate = ValueAnimator.ofInt(0, 360);        } else {            animatorTextRotate = ValueAnimator.ofInt(360, 0);        }        animatorTextRotate.setDuration(mDuration);        animatorTextRotate.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator valueAnimator) {                mAngle = (Integer) valueAnimator.getAnimatedValue();                if (mIsForward) {                    if (mAngle == 360) {                        mState = STATE_ROTATE_OVER;                    }                } else {                    if (mAngle == 0) {                        mState = STATE_ROTATE_OVER;                    }                }            }        });        animatorList.add(animatorTextRotate);        ValueAnimator animatorAlpha;        if (mIsForward) {            animatorAlpha = ValueAnimator.ofInt(0, 255);        } else {            animatorAlpha = ValueAnimator.ofInt(255, 0);        }        animatorAlpha.setDuration(mDuration);        animatorAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator valueAnimator) {                mAlpha = (Integer) valueAnimator.getAnimatedValue();                if (mIsForward) {                    if (mAlpha == 255) {                        mState = STATE_ROTATE_OVER;                    }                } else {                    if (mAlpha == 0) {                        mState = STATE_ROTATE_OVER;                    }                }            }        });        animatorList.add(animatorAlpha);        ValueAnimator animatorTextMove;        if (mIsForward) {            animatorTextMove = ValueAnimator.ofInt(MAX_WIDTH - MAX_HEIGHT / 2, MAX_WIDTH / 2);        } else {            animatorTextMove = ValueAnimator.ofInt(MAX_WIDTH / 2, MAX_WIDTH - MAX_HEIGHT / 2);        }        animatorTextMove.setDuration(mDuration);        animatorTextMove.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator valueAnimator) {                mTextPosition = (Integer) valueAnimator.getAnimatedValue();                if (mIsForward) {                    if (mTextPosition == MAX_WIDTH / 2) {                        mState = STATE_ROTATE_OVER;                    }                } else {                    if (mTextPosition == MAX_WIDTH - MAX_HEIGHT / 2) {                        mState = STATE_ROTATE_OVER;                    }                }            }        });        animatorList.add(animatorTextMove);        ValueAnimator animatorBtnMove;        if (mIsForward) {            animatorBtnMove = ValueAnimator.ofInt(MAX_WIDTH - MAX_HEIGHT / 2, MAX_HEIGHT / 2);        } else {            animatorBtnMove = ValueAnimator.ofInt(MAX_HEIGHT / 2, MAX_WIDTH - MAX_HEIGHT / 2);        }        animatorBtnMove.setDuration(mDuration);        animatorBtnMove.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator valueAnimator) {                mMinusBtnPosition = (Integer) valueAnimator.getAnimatedValue();                if (mIsForward) {                    if (mMinusBtnPosition == MAX_HEIGHT / 2) {                        mState = STATE_ROTATE_OVER;                    }                } else {                    if (mMinusBtnPosition == MAX_WIDTH - MAX_HEIGHT / 2) {                        mState = STATE_ROTATE_OVER;                    }                }                invalidate();            }        });        animatorList.add(animatorBtnMove);        AnimatorSet animatorSet = new AnimatorSet();        animatorSet.setDuration(mDuration);        animatorSet.playTogether(animatorList);        animatorSet.start();    }    /**     * 设置购买数量     *     * @param num 购买数量     */    public void setTextNum(int num) {        mNum = num;        mState = STATE_ROTATE_OVER;        invalidate();    }    public void setOnShoppingClickListener(ShoppingClickListener shoppingClickListener) {        this.mShoppingClickListener = shoppingClickListener;    }    public interface ShoppingClickListener {        void onAddClick(int num);        void onMinusClick(int num);    }    /**     * 判断点是否在圆内     *     * @param pointF 待确定点     * @param circle 圆心     * @param radius 半径     * @return true在圆内     */    private boolean isPointInCircle(PointF pointF, PointF circle, float radius) {        return Math.pow((pointF.x - circle.x), 2) + Math.pow((pointF.y - circle.y), 2) <= Math.pow(radius, 2);    }    private int sp2px(float spValue) {        final float fontScale = getContext().getResources().getDisplayMetrics().scaledDensity;        return (int) (spValue * fontScale + 0.5f);    }    //获取Text高度    private int getTextHeight(String str, Paint paint) {        Rect rect = new Rect();        paint.getTextBounds(str, 0, str.length(), rect);        return (int) (rect.height() / 33f * 29);    }    //获取Text宽度    private int getTextWidth(Paint paint, String str) {        int iRet = 0;        if (str != null && str.length() > 0) {            int len = str.length();            float[] widths = new float[len];            paint.getTextWidths(str, widths);            for (int j = 0; j < len; j++) {                iRet += (int) Math.ceil(widths[j]);            }        }        return iRet;    }}
里面用到了自定义的属性所以需要创建attrs.xml

然后在里面定义

<declare-styleable name="ShoppingView">    <attr name="sv_bg_color" format="color"/>    <attr name="sv_text" format="string"/>    <attr name="sv_text_size" format="dimension"/>    <attr name="sv_duration" format="integer"/></declare-styleable>
有需要的同学可以去试试看,很好的效果


如果大家觉得我写的还可以的话请关注我的微信公众号:


会定时给大家推送技术点

原创粉丝点击