您的位置:首页 > 其它

自定义购物车控件,使用起来就是这么丝滑

2017-04-19 15:09 363 查看
本购物车自定义控件包括了购物车的抛物线动画效果,根据需求可设置是否需要。本控件自定义view,详细实现,代码注释较详细,请参考注释。

首先过下效果图



按照自定义控件三部曲,首先创建attr属性文件,定义相关属性。

res/values/attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ShopCartView">
<!--加按钮是否开启fill模式 默认是stroke(false)-->
<attr name="isAddFillMode" format="boolean"/>
<!--加按钮的背景色前景色-->
<attr name="addEnableBgColor" format="color"/>
<attr name="addEnableFgColor" format="color"/>
<!--加按钮不可用时的背景色前景色-->
<attr name="addDisableBgColor" format="color"/>
<attr name="addDisableFgColor" format="color"/>

<!--减按钮是否开启fill模式 默认是stroke(false)-->
<attr name="isDelFillMode" format="boolean"/>
<!--减按钮的背景色前景色-->
<attr name="delEnableBgColor" format="color"/>
<attr name="delEnableFgColor" format="color"/>
<!--减按钮不可用时的背景色前景色-->
<attr name="delDisableBgColor" format="color"/>
<attr name="delDisableFgColor" format="color"/>

<!--圆的半径-->
<attr name="radius" format="dimension"/>
<!--圆圈的宽度-->
<attr name="circleStrokeWidth" format="dimension"/>
<!--线(+ - 符号)的宽度-->
<attr name="lineWidth" format="dimension"/>

<!--两个圆之间的间距-->
<attr name="gapBetweenCircle" format="dimension"/>
<!--绘制数量的textSize-->
<attr name="numTextSize" format="dimension"/>
<!--最大数量和当前数量-->
<attr name="maxCount" format="integer"/>
<attr name="count" format="integer"/>

<!-- 增加一个开关 ignoreHintArea:UI显示、动画是否忽略hint收缩区域-->
<attr name="ignoreHintArea" format="boolean"/>

<!--数量为0时,hint文字 背景色前景色 大小,圆角值-->
<attr name="hintText" format="string"/>
<attr name="hintBgColor" format="color"/>
<attr name="hintFgColor" format="color"/>
<attr name="hintTextSize" format="dimension"/>
<attr name="hintBgRoundValue" format="dimension"/>

<attr name="perAnimDuration" format="integer"/>

<!--in replenish 补货-->
<attr name="replenishTextColor" format="color"/>
<attr name="replenishTextSize" format="dimension"/>
<attr name="replenishText" format="string"/>
</declare-styleable>
</resources>
自定义View代码

ShopCartView.java

package androidbus.com.shopcartlib;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.LinearInterpolator;
import android.widget.ImageView;
import android.widget.LinearLayout;

/*
*  @项目名:  ShopCartViewLib
*  @包名:    androidbus.com.shopcartlib
*  @文件名:   ShopCartView
*  @创建者:   Administrator
*  @创建时间:  2017/4/17 0017 上午 9:46
*  @描述:    TODO
*/
public class ShopCartView extends View {
private static final String TAG = "ShopCartView";
private boolean mIsAddFillMode;
private int mAddEnableBgColor;
private int mAddEnableFgColor;
private int mAddDisableBgColor;
private int mAddDisableFgColor;
private boolean mIsDelFillMode;
private int mDelEnableBgColor;
private int mDelEnableFgColor;
private int mDelDisableBgColor;
private int mDelDisableFgColor;
private float mRadius;
private float mCircleStrokeWidth;
private float mLineWidth;
private float mGapBetweenCircle;
private float mNumTextSize;
private int mMaxCount;
private int mCount;
/**
* 是否需要显示hint提示文本
*/
private boolean mIgnoreHintArea;
private String mHintText;
private int mHintBgColor;
private int mHintFgColor;
private float mHintTextSize;
private float mHintBgRoundValue;
private int mPerAnimDuration;
private int mReplenishTextColor;
private float mReplenishTextSize;
private String mReplenishText;
private Paint mHintPaint;
private Paint mHintTextPaint;
private Paint mAddPaint;
private Paint mDelPaint;
private Paint mNumPaint;
private Region mAddRegion;
private Region mDelRegion;
private Path mAddPath;
private Path mDelPath;
private float mDelAnimatorFraction;
private float mHintAnimatorFraction;
private boolean showHintMode;
private boolean showHintText;
private ValueAnimator mDelExpandAnim;
private ValueAnimator mHintExpandAnim;
private ValueAnimator mHintClospAnim;
private ValueAnimator mDelClospAnim;
private int mWidth;
private int mLeft;
private int mHeight;
private int mTop;
private boolean isReplenishText = false;
private Paint mReplenshTextPaint;
private Rect mHintTextBound;
private Rect mReplenshTextBound;
private RectF mRectF;
private Region mRegion;
private OnAddOrDelListner mOnAddOrDelListner;
private boolean isBind;
private Context context;
private View startView;
private View endView;

public ShopCartView(Context context) {
this(context, null);
}

public ShopCartView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public ShopCartView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//初始化默认属性
initDefaultAttrs(context);
//获取资源属性
init(context, attrs, defStyleAttr);
//初始化画笔
initPaint();
//先暂停所有动画
cancelAnim();
//根据当前数量初始化UI
initUIByCount();
}

private void initUIByCount() {
if (mCount == 0) {
showHintMode = true;
showHintText = true;
mHintAnimatorFraction = 0;
mDelAnimatorFraction = 1;
} else {
showHintMode = false;
showHintText = false;
mHintAnimatorFraction = 1;
mDelAnimatorFraction = 0;
}
}

private void cancelAnim() {
if (mHintExpandAnim != null && mHintExpandAnim.isRunning()) {
mHintExpandAnim.cancel();
}

if (mHintClospAnim != null && mHintClospAnim.isRunn
ff89
ing()) {
mHintClospAnim.cancel();
}

if (mDelExpandAnim != null && mDelExpandAnim.isRunning()) {
mDelExpandAnim.cancel();
}

if (mDelClospAnim != null && mDelClospAnim.isRunning()) {
mDelClospAnim.cancel();
}
}

private void initPaint() {
mHintPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mHintPaint.setStyle(Paint.Style.FILL);

mHintTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mHintTextPaint.setStyle(Paint.Style.FILL);
mHintTextPaint.setTextSize(mHintTextSize);
mHintTextPaint.setColor(mHintFgColor);

mReplenshTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mReplenshTextPaint.setStyle(Paint.Style.FILL);
mReplenshTextPaint.setTextSize(mReplenishTextSize);
mReplenshTextPaint.setColor(mReplenishTextColor);

mAddPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
if (mIsAddFillMode) {
mAddPaint.setStyle(Paint.Style.FILL);
} else {
mAddPaint.setStyle(Paint.Style.STROKE);
}

mDelPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
if (mIsDelFillMode) {
mDelPaint.setStyle(Paint.Style.FILL);
} else {
mDelPaint.setStyle(Paint.Style.STROKE);
}

mNumPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mNumPaint.setStyle(Paint.Style.FILL);
mNumPaint.setTextSize(mNumTextSize);
mNumPaint.setColor(mReplenishTextColor);

mAddRegion = new Region();
mDelRegion = new Region();
mAddPath = new Path();
mDelPath = new Path();

//初始化动画属性
//减少按钮的伸展动画
mDelExpandAnim = ValueAnimator.ofFloat(1, 0);
mDelExpandAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mDelAnimatorFraction = (float) valueAnimator.getAnimatedValue();
invalidate();
}
});
mDelExpandAnim.setDuration(mPerAnimDuration);
mDelExpandAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
}
});

//提示语扩展动画
mHintExpandAnim = ValueAnimator.ofFloat(1, 0);
mHintExpandAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mHintAnimatorFraction = (float) valueAnimator.getAnimatedValue();
invalidate();
}
});
mHintExpandAnim.setDuration(mIgnoreHintArea
? 0
: mPerAnimDuration);
mHintExpandAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
//提示语扩展动画开始时要显示提示语背景
if (mCount == 0) {
showHintMode = true;
}
}

@Override
public void onAnimationEnd(Animator animation) {
//提示语扩展动画结束时要显示提示语文本
if (mCount == 0) {
showHintText = true;
}
}
});

//提示语收缩动画
mHintClospAnim = ValueAnimator.ofFloat(0, 1);
mHintClospAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mHintAnimatorFraction = (float) valueAnimator.getAnimatedValue();
invalidate();
}
});
mHintClospAnim.setDuration(mPerAnimDuration);
mHintClospAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
//提示语收缩动画开始时先隐藏提示语文本
if (mCount > 0) {
showHintText = false;
}
}

@Override
public void onAnimationEnd(Animator animation) {
//提示语收缩动画结束时先隐藏提示语背景
if (mCount > 0) {
showHintMode = false;
//提示语收缩动画结束时开启减少按钮扩展动画
if (mDelExpandAnim != null && !mDelExpandAnim.isRunning()) {
mDelExpandAnim.start();
}
}
}
});

//减少按钮的收缩动画
mDelClospAnim = ValueAnimator.ofFloat(0, 1);
mDelClospAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mDelAnimatorFraction = (float) valueAnimator.getAnimatedValue();
invalidate();
}
});
mDelClospAnim.setDuration(mPerAnimDuration);
mDelClospAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
//减少按钮的收缩动画结束后开启提示语扩展动画
if (mCount == 0) {
if (mHintExpandAnim != null && !mHintExpandAnim.isRunning()) {
mHintExpandAnim.start();
}
}
}
});

mHintTextBound = new Rect();
mHintTextPaint.getTextBounds(mHintText, 0, mHintText.length(), mHintTextBound);
mReplenshTextBound = new Rect();
mReplenshTextPaint.getTextBounds(mReplenishText, 0, mReplenishText
.length(), mReplenshTextBound);
}

private void init(Context context, AttributeSet attrs, int defStyleAttr) {
TypedArray ta = context.getTheme()
.obtainStyledAttributes(attrs, R.styleable.ShopCartView, defStyleAttr, 0);
int indexCount = ta.getIndexCount();

for (int i = 0; i < indexCount; i++) {
int attr = ta.getIndex(i);
if (attr == R.styleable.ShopCartView_isAddFillMode) {
mIsAddFillMode = ta.getBoolean(attr, mIsAddFillMode);
} else if (attr == R.styleable.ShopCartView_addEnableBgColor) {
mAddEnableBgColor = ta.getColor(attr, mAddEnableBgColor);
} else if (attr == R.styleable.ShopCartView_addEnableFgColor) {
mAddEnableFgColor = ta.getColor(attr, mAddEnableFgColor);
} else if (attr == R.styleable.ShopCartView_addDisableBgColor) {
mAddDisableBgColor = ta.getColor(attr, mAddDisableBgColor);
} else if (attr == R.styleable.ShopCartView_addDisableFgColor) {
mAddDisableFgColor = ta.getColor(attr, mAddDisableFgColor);
} else if (attr == R.styleable.ShopCartView_isDelFillMode) {
mIsDelFillMode = ta.getBoolean(attr, mIsDelFillMode);
} else if (attr == R.styleable.ShopCartView_delEnableBgColor) {
mDelEnableBgColor = ta.getColor(attr, mDelEnableBgColor);
} else if (attr == R.styleable.ShopCartView_delEnableFgColor) {
mDelEnableFgColor = ta.getColor(attr, mDelEnableFgColor);
} else if (attr == R.styleable.ShopCartView_delDisableBgColor) {
mDelDisableBgColor = ta.getColor(attr, mDelDisableBgColor);
} else if (attr == R.styleable.ShopCartView_delDisableFgColor) {
mDelDisableFgColor = ta.getColor(attr, mDelDisableFgColor);
} else if (attr == R.styleable.ShopCartView_radius) {
mRadius = ta.getDimension(attr, mRadius);
} else if (attr == R.styleable.ShopCartView_circleStrokeWidth) {
mCircleStrokeWidth = ta.getDimension(attr, mCircleStrokeWidth);
} else if (attr == R.styleable.ShopCartView_lineWidth) {
mLineWidth = ta.getDimension(attr, mLineWidth);
} else if (attr == R.styleable.ShopCartView_gapBetweenCircle) {
mGapBetweenCircle = ta.getDimension(attr, mGapBetweenCircle);
} else if (attr == R.styleable.ShopCartView_numTextSize) {
mNumTextSize = ta.getDimension(attr, mNumTextSize);
} else if (attr == R.styleable.ShopCartView_maxCount) {
mMaxCount = ta.getInt(attr, mMaxCount);
} else if (attr == R.styleable.ShopCartView_count) {
mCount = ta.getInt(attr, mCount);
} else if (attr == R.styleable.ShopCartView_ignoreHintArea) {
mIgnoreHintArea = ta.getBoolean(attr, mIgnoreHintArea);
} else if (attr == R.styleable.ShopCartView_hintText) {
mHintText = ta.getString(attr);
} else if (attr == R.styleable.ShopCartView_hintBgColor) {
mHintBgColor = ta.getColor(attr, mHintBgColor);
} else if (attr == R.styleable.ShopCartView_hintFgColor) {
mHintFgColor = ta.getColor(attr, mHintFgColor);
} else if (attr == R.styleable.ShopCartView_hintTextSize) {
mHintTextSize = ta.getDimension(attr, mHintTextSize);
} else if (attr == R.styleable.ShopCartView_hintBgRoundValue) {
mHintBgRoundValue = ta.getDimension(attr, mHintBgRoundValue);
} else if (attr == R.styleable.ShopCartView_perAnimDuration) {
mPerAnimDuration = ta.getInt(attr, mPerAnimDuration);
} else if (attr == R.styleable.ShopCartView_replenishTextColor) {
mReplenishTextColor = ta.getColor(attr, mReplenishTextColor);
} else if (attr == R.styleable.ShopCartView_replenishTextSize) {
mReplenishTextSize = ta.getDimension(attr, mReplenishTextSize);
} else if (attr == R.styleable.ShopCartView_replenishText) {
mReplenishText = ta.getString(attr);
}
}
ta.recycle();
}

private void initDefaultAttrs(Context context) {
mIsAddFillMode = true; //默认添加按钮为fill模式
mAddEnableBgColor = Color.CYAN;
mAddEnableFgColor = Color.WHITE;
mAddDisableBgColor = Color.GRAY;
mAddDisableFgColor = Color.WHITE;
mIsDelFillMode = false;
mDelEnableBgColor = Color.GRAY;
mDelEnableFgColor = Color.GRAY;
mDelDisableBgColor = Color.GRAY;
mDelDisableFgColor = Color.LTGRAY;
mRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 12.5f, context
.getResources().getDisplayMetrics());
mCircleStrokeWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1f, context
.getResources().getDisplayMetrics());
mLineWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2f, context
.getResources().getDisplayMetrics());
mGapBetweenCircle = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 34f, context
.getResources().getDisplayMetrics());
mNumTextSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 14f, context
.getResources().getDisplayMetrics());
mMaxCount = 100;
mCount = 1;
mIgnoreHintArea = false;
mHintText = getResources().getString(R.string.str_hintText);
mHintBgColor = Color.CYAN;
mHintFgColor = Color.WHITE;
mHintTextSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 14f, context
.getResources().getDisplayMetrics());
mHintBgRoundValue = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5f, context
.getResources().getDisplayMetrics());
mPerAnimDuration = 500;
mReplenishTextColor = Color.BLACK;
mReplenishTextSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 14f, context
.getResources().getDisplayMetrics());
mReplenishText = getResources().getString(R.string.str_replenishText);
}

private Region getRegion() {
if (mRegion == null) {
mRegion = new Region();
}
mRegion.set(mLeft, mTop, mWidth - getPaddingRight(), mHeight - getPaddingBottom());
return mRegion;
}

@NonNull
private RectF getRectF() {
if (mRectF == null) {
mRectF = new RectF();
}
mRectF.left = mLeft + (mWidth - mRadius * 2 - mCircleStrokeWidth * 2) * mHintAnimatorFraction;
mRectF.top = mTop;
mRectF.right = mLeft + mWidth;
mRectF.bottom = mTop + mHeight;
return mRectF;
}

@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
float x = event.getX();
float y = event.getY();
if (isReplenishText) {
return true;
}

if (mHintExpandAnim.isRunning() || mHintClospAnim.isRunning() || mDelExpandAnim
.isRunning() || mDelClospAnim.isRunning())
{
//如果当前有动画在运行就不能有其他操作
return true;
}

switch (action) {
case MotionEvent.ACTION_DOWN:
if (mCount == 0) {
addClick();
return true;
} else {

if (mAddRegion.contains((int) x, (int) y)) {
addClick();

return true;
} else if (mDelRegion.contains((int) x, (int) y)) {
delClick();
return true;
}
}
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
break;
}
return super.onTouchEvent(event);
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = w;
mLeft = getPaddingLeft();
mHeight = h;
mTop = getPaddingTop();
}

@Override
protected void onDraw(Canvas canvas) {
if (isReplenishText) {
//计算"补货中"文本的起终点
int startX = mWidth / 2 - mReplenshTextBound.width() / 2;
int startY = (int) (mHeight / 2 - (mReplenshTextPaint.ascent() + mReplenshTextPaint
.descent()) / 2);
//绘制"补货中"文本
canvas.drawText(mReplenishText, startX, startY, mReplenshTextPaint);
return;
}

if (!mIgnoreHintArea && showHintMode) {
//绘制提示语背景
mHintPaint.setColor(mHintBgColor);
RectF rectF = getRectF();
canvas.drawRoundRect(rectF, mHintBgRoundValue, mHintBgRoundValue, mHintPaint);
//绘制提示语文本
if (showHintText) {
int startX = mWidth / 2 - mHintTextBound.width() / 2;
int startY = (int) (mHeight / 2 - (mHintTextPaint.ascent() + mHintTextPaint
.descent()) / 2);
canvas.drawText(mHintText, startX, startY, mHintTextPaint);
}
} else {
//绘制加减按钮
//获取按钮动画的最大位移距离
int animOffsetMax = (int) (mRadius * 2 + mCircleStrokeWidth * 2 + mGapBetweenCircle);
int alphaMax = 255;
int animRotateMax = 360;

//绘制删除按钮
if (mCount > 0) {
mDelPaint.setColor(mDelEnableBgColor);
} else {
mDelPaint.setColor(mDelDisableBgColor);
}
mDelPaint.setStrokeWidth(mCircleStrokeWidth);
mDelPaint.setAlpha((int) (alphaMax * (1 - mDelAnimatorFraction)));
mDelPath.reset();
canvas.save();
//删除按钮动画
canvas.translate(animOffsetMax * mDelAnimatorFraction + mLeft, 0);
canvas.rotate((int) (animRotateMax * (1 - mDelAnimatorFraction)), mCircleStrokeWidth + mRadius, mTop + mCircleStrokeWidth + mRadius);
mDelPath.addCircle(mLeft + mRadius + mCircleStrokeWidth, mTop + mRadius + mCircleStrokeWidth, mRadius, Path.Direction.CW);
mDelRegion.setPath(mDelPath, getRegion());
canvas.drawPath(mDelPath, mDelPaint);
if (mCount > 0) {
mDelPaint.setColor(mDelEnableFgColor);
} else {
mDelPaint.setColor(mDelDisableFgColor);
}
mDelPaint.setStrokeWidth(mLineWidth);
//绘制删除按钮中的横线
canvas.drawLine(mLeft + mCircleStrokeWidth + mRadius / 2, mTop + mCircleStrokeWidth + mRadius, mLeft + mCircleStrokeWidth + mRadius / 2 + mRadius, mTop + mCircleStrokeWidth + mRadius, mDelPaint);
canvas.restore();

//绘制数量文本
//获取数量文本的最大位移距离
mNumPaint.setAlpha((int) (alphaMax * (1 - mDelAnimatorFraction)));
canvas.save();

canvas.translate((animOffsetMax - mRadius * 2 - mCircleStrokeWidth * 2 - mGapBetweenCircle / 2 + mNumPaint
.measureText(mCount + "") / 2) * mDelAnimatorFraction + mLeft + mRadius * 2 + mCircleStrokeWidth * 2 + mGapBetweenCircle / 2 - mNumPaint
.measureText(mCount + "") / 2, 0);
canvas.rotate((int) (animRotateMax * (1 - mDelAnimatorFraction)), mNumPaint
.measureText(mCount + "") / 2, mHeight / 2);
canvas.drawText(mCount + "", 0, mHeight / 2 - (mNumPaint.ascent() + mNumPaint
.descent()) / 2, mNumPaint);
canvas.restore();

//绘制添加按钮
if (mCount < 100) {
mAddPaint.setColor(mAddEnableBgColor);
} else {
mAddPaint.setColor(mAddDisableBgColor);
}
mAddPaint.setStrokeWidth(mCircleStrokeWidth);
int left = (int) (mLeft + animOffsetMax + mCircleStrokeWidth);
mAddPath.reset();
mAddPath.addCircle(left + mRadius, mTop + mCircleStrokeWidth + mRadius, mRadius, Path.Direction.CW);
mAddRegion.setPath(mAddPath, getRegion());
canvas.drawPath(mAddPath, mAddPaint);

//绘制添加按钮间的“+”
if (mCount < 100) {
mAddPaint.setColor(mAddEnableFgColor);
} else {
mAddPaint.setColor(mAddDisableFgColor);
}
mAddPaint.setStrokeWidth(mLineWidth);
canvas.drawLine(left + mRadius / 2, mTop + mCircleStrokeWidth + mRadius, left + mRadius / 2 + mRadius, mTop + mCircleStrokeWidth + mRadius, mAddPaint);
canvas.drawLine(left + mRadius, mTop + mCircleStrokeWidth + mRadius / 2, left + mRadius, mTop + mCircleStrokeWidth + mRadius / 2 + mRadius, mAddPaint);
}
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);

switch (widthMode) {
case MeasureSpec.EXACTLY:
break;
case MeasureSpec.AT_MOST:
int defaultWidth = (int) (getPaddingLeft() + mRadius * 2 + mGapBetweenCircle + mRadius * 2 + mCircleStrokeWidth * 4 + getPaddingRight());
width = defaultWidth < width
? defaultWidth
: width;
break;
case MeasureSpec.UNSPECIFIED:
defaultWidth = (int) (getPaddingLeft() + mRadius * 2 + mGapBetweenCircle + mRadius * 2 + mCircleStrokeWidth * 4 + getPaddingRight());
width = defaultWidth;
break;
}

switch (heightMode) {
case MeasureSpec.EXACTLY:
break;
case MeasureSpec.AT_MOST:
int defaultHeight = (int) (getPaddingTop() + mRadius * 2 + mCircleStrokeWidth * 2 + getPaddingBottom());
height = defaultHeight < height
? defaultHeight
: height;
break;
case MeasureSpec.UNSPECIFIED:
defaultHeight = (int) (getPaddingTop() + mRadius * 2 + mCircleStrokeWidth * 2 + getPaddingBottom());
width = defaultHeight;
break;
}

setMeasuredDimension(width, height);
//        cancelAnim();
//        initUIByCount();
}

private void delClick() {
if (mCount > 0) {
mCount--;
if (mCount == 0) {
cancelAnim();
if (mDelClospAnim != null && !mDelClospAnim.isRunning()) {
mDelClospAnim.start();
}
} else {
mDelAnimatorFraction = 0;
invalidate();
}

if (mOnAddOrDelListner != null) {
mOnAddOrDelListner.onDelClick(mCount);
}
} else {

}
}

private void addClick() {
if (mCount < mMaxCount) {
mCount++;
if (mCount == 1) {
cancelAnim();
if (mHintClospAnim != null && !mHintClospAnim.isRunning()) {
mHintClospAnim.start();
}
} else {
mDelAnimatorFraction = 0;
invalidate();
}

if (mOnAddOrDelListner != null) {
mOnAddOrDelListner.onAddClick(mCount);
}

if (isBind) {
bindToCartAnim();
}
} else {

}
invalidate();
}

public boolean isAddFillMode() {
return mIsAddFillMode;
}

public void setAddFillMode(boolean addFillMode) {
mIsAddFillMode = addFillMode;
}

public int getAddEnableBgColor() {
return mAddEnableBgColor;
}

public void setAddEnableBgColor(int addEnableBgColor) {
mAddEnableBgColor = addEnableBgColor;
}

public int getAddEnableFgColor() {
return mAddEnableFgColor;
}

public void setAddEnableFgColor(int addEnableFgColor) {
mAddEnableFgColor = addEnableFgColor;
}

public int getAddDisableBgColor() {
return mAddDisableBgColor;
}

public void setAddDisableBgColor(int addDisableBgColor) {
mAddDisableBgColor = addDisableBgColor;
}

public int getAddDisableFgColor() {
return mAddDisableFgColor;
}

public void setAddDisableFgColor(int addDisableFgColor) {
mAddDisableFgColor = addDisableFgColor;
}

public boolean isDelFillMode() {
return mIsDelFillMode;
}

public void setDelFillMode(boolean delFillMode) {
mIsDelFillMode = delFillMode;
}

public int getDelEnableBgColor() {
return mDelEnableBgColor;
}

public void setDelEnableBgColor(int delEnableBgColor) {
mDelEnableBgColor = delEnableBgColor;
}

public int getDelEnableFgColor() {
return mDelEnableFgColor;
}

public void setDelEnableFgColor(int delEnableFgColor) {
mDelEnableFgColor = delEnableFgColor;
}

public int getDelDisableBgColor() {
return mDelDisableBgColor;
}

public void setDelDisableBgColor(int delDisableBgColor) {
mDelDisableBgColor = delDisableBgColor;
}

public int getDelDisableFgColor() {
return mDelDisableFgColor;
}

public void setDelDisableFgColor(int delDisableFgColor) {
mDelDisableFgColor = delDisableFgColor;
}

public float getRadius() {
return mRadius;
}

public void setRadius(float radius) {
mRadius = radius;
}

public float getCircleStrokeWidth() {
return mCircleStrokeWidth;
}

public void setCircleStrokeWidth(float circleStrokeWidth) {
mCircleStrokeWidth = circleStrokeWidth;
}

public float getLineWidth() {
return mLineWidth;
}

public void setLineWidth(float lineWidth) {
mLineWidth = lineWidth;
}

public float getGapBetweenCircle() {
return mGapBetweenCircle;
}

public void setGapBetweenCircle(float gapBetweenCircle) {
mGapBetweenCircle = gapBetweenCircle;
}

public float getNumTextSize() {
return mNumTextSize;
}

public void setNumTextSize(float numTextSize) {
mNumTextSize = numTextSize;
}

public int getMaxCount() {
return mMaxCount;
}

public void setMaxCount(int maxCount) {
mMaxCount = maxCount;
}

public int getCount() {
return mCount;
}

public void setCount(int count) {
mCount = count;
cancelAnim();
initUIByCount();
}

public boolean isIgnoreHintArea() {
return mIgnoreHintArea;
}

public void setIgnoreHintArea(boolean ignoreHintArea) {
mIgnoreHintArea = ignoreHintArea;
}

public String getHintText() {
return mHintText;
}

public void setHintText(String hintText) {
mHintText = hintText;
}

public int getHintBgColor() {
return mHintBgColor;
}

public void setHintBgColor(int hintBgColor) {
mHintBgColor = hintBgColor;
}

public int getHintFgColor() {
return mHintFgColor;
}

public void setHintFgColor(int hintFgColor) {
mHintFgColor = hintFgColor;
}

public float getHintTextSize() {
return mHintTextSize;
}

public void setHintTextSize(float hintTextSize) {
mHintTextSize = hintTextSize;
}

public float getHintBgRoundValue() {
return mHintBgRoundValue;
}

public void setHintBgRoundValue(float hintBgRoundValue) {
mHintBgRoundValue = hintBgRoundValue;
}

public int getPerAnimDuration() {
return mPerAnimDuration;
}

public void setPerAnimDuration(int perAnimDuration) {
mPerAnimDuration = perAnimDuration;
}

public int getReplenishTextColor() {
return mReplenishTextColor;
}

public void setReplenishTextColor(int replenishTextColor) {
mReplenishTextColor = replenishTextColor;
}

public float getReplenishTextSize() {
return mReplenishTextSize;
}

public void setReplenishTextSize(float replenishTextSize) {
mReplenishTextSize = replenishTextSize;
}

public String getReplenishText() {
return mReplenishText;
}

public void setReplenishText(String replenishText) {
mReplenishText = replenishText;
}

/**
* 设置是否绑定购物车动画
* @param isBind
* @param context
* @param startView
* @param endView
*/

public void setCartAnim(boolean isBind, Context context, @NonNull View startView,
@NonNull View endView) {
this.isBind = isBind;
this.context = context;
this.startView = startView;
this.endView = endView;
}

public void bindToCartAnim() {
//创建购物车小球
ImageView target = new ImageView(context);
target.setImageResource(R.drawable.traint);
//获取startView和endView在屏幕的坐标
int startX = getLocation(startView, 0);
int startY = getLocation(startView, 1);
int endX = getLocation(endView, 0);
int endY = getLocation(endView, 1);

Drawable drawable = context.getResources().getDrawable(R.drawable.traint);
int intrinsicWidth = 0;
int intrinsicHeight = 0;
if (drawable != null) {
intrinsicWidth = drawable.getIntrinsicWidth();
intrinsicHeight = drawable.getIntrinsicHeight();
}

if (startView instanceof ShopCartView) {
if (drawable != null) {
startX = (int) (startX + mWidth - mCircleStrokeWidth - mRadius - intrinsicWidth / 2);
startY = (int) (startY + mCircleStrokeWidth + mRadius - intrinsicHeight / 2);
}
} else {
startX = startX + startView.getWidth() / 2 - intrinsicWidth / 2;
startY = startY + startView.getHeight() / 2 - intrinsicHeight / 2;
}

endX = endX + endView.getWidth() / 2 - intrinsicWidth / 2;
endY = endY + endView.getHeight() / 2 - intrinsicHeight / 2;

//将购物车小球加载入窗体相应位置
traintImgToWindow(context, startX, startY, target);
//执行购物车抛物线动画
invokeAnim(target, startX, startY, endX, endY);
}

private void invokeAnim(final ImageView target, int startX, int startY, int endX, int endY) {
Log.d("getLocation", startX + "," + startY + " " + endX + "," + endY);
//抛物线动画分解为x轴匀速移动和y轴加速移动
ObjectAnimator translateX = ObjectAnimator.ofFloat(target, "translationX", endX - startX);
translateX.setInterpolator(new LinearInterpolator());

ObjectAnimator translateY = ObjectAnimator.ofFloat(target, "translationY", endY - startY);
translateY.setInterpolator(new AccelerateInterpolator());
translateY.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
//动画开始时显示小球
target.setVisibility(VISIBLE);
}

@Override
public void onAnimationEnd(Animator animation) {
//动画结束时隐藏小球
target.setVisibility(GONE);
}
});

//缩放动画
ObjectAnimator scaleX = ObjectAnimator.ofFloat(target, "scaleX", 1f, 0.4f);
ObjectAnimator scaleY = ObjectAnimator.ofFloat(target, "scaleY", 1f, 0.4f);

AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(translateX, translateY,scaleX,scaleY);
animatorSet.setDuration(500);
animatorSet.start();

}

/**
* 将购物车小球添加屏幕相应位置
* @param context
* @param startX
* @param startY
* @param target
*/

private void traintImgToWindow(Context context, int startX, int startY, ImageView target) {
ViewGroup rootView = (ViewGroup) ((Activity) context).getWindow().getDecorView();
LinearLayout layout = new LinearLayout(context);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
layout.setBackgroundColor(getResources().getColor(android.R.color.transparent));
layout.setLayoutParams(layoutParams);
rootView.addView(layout);

LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.leftMargin = startX;
params.topMargin = startY;
target.setLayoutParams(params);
layout.addView(target);
}

/**
* 获取控件在屏幕相应位置
* @param target
* @param i
* @return
*/
private int getLocation(View target, int i) {
int[] location = new int[2];
target.getLocationInWindow(location);
return location[i];
}

public void setOnAddOrDelListner(OnAddOrDelListner onAddOrDelListner) {
mOnAddOrDelListner = onAddOrDelListner;
}

public interface OnAddOrDelListner {
void onAddClick(int count);

void onDelClick(int count);
}
}


附上代码中的Drawable文件 trant.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<corners android:radius="12.5dp"/>
<size
android:width="25dp"
android:height="25dp"/>
<solid android:color="@android:color/holo_red_dark"/>

</shape>


购物车自定义控件到这里就写完了,接下来看下如何使用

activity_main.xml布局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="androidbus.com.shopcartviewlib.MainActivity">

<Button
android:layout_alignParentRight="true"
android:id="@+id/endView"
android:text="endView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>

<androidbus.com.shopcartlib.ShopCartView
android:id="@+id/scv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"/>
</RelativeLayout>


MainActivity.java代码

package androidbus.com.shopcartviewlib;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Button;

import androidbus.com.shopcartlib.ShopCartView;

public class MainActivity extends AppCompatActivity implements ShopCartView.OnAddOrDelListner {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button endView = (Button) findViewById(R.id.endView);
ShopCartView scv = (ShopCartView) findViewById(R.id.scv);
scv.setCount(6); //设置数量
scv.setOnAddOrDelListner(this); //设置点击添加或减少按钮监听
scv.setCartAnim(true,this,scv,endView);  //设置是否需要购物车的抛物线动画
scv.setIgnoreHintArea(false);  //设置在数量为零时,是否需要显示提示文本
}

@Override
public void onAddClick(int count) {
//数量添加的回调
}

@Override
public void onDelClick(int count) {
//数量减少的回调
}}


OK,如上,实现购物车效果只需几句代码,使用起来就是这么丝滑!代码传送门:https://github.com/ItStepMore/shopcartview/tree/master
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐