我们的效果图
在录制gif的时候有点卡顿,感觉控件中的动画看得不是很清楚,此自定义控件是有旋转和向右扩展和收缩的效果的
自定义属性
<!--仿饿了么自定义控件属性-->
<declare-styleable name="AnimShopButton">
<!--加按钮是否开启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模式(false)-->
<attr name="isDelFillMode" format="boolean"/>
<!--减按钮的背景色前景色-->
<attr name="delEnableBgColor" format="color"/>
<attr name="delEnableFgColor" format="color"/>
<!--减按钮不可用时的背景色前景色-->
<attr name="delDisableBgColor" format="color"/>
<attr name="delDisbleFgColor" 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"/>
<!--count为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="replenishTextSize" format="dimension"/>
<attr name="replenishTextColor" format="color"/>
<attr name="replenishText" format="string"/>
</declare-styleable>
此控件一共有三种状态:
1.商品无货
2.有货但是还没选择数量
3,诱惑选择的数量>0
自定义view的所有代码
public class AnimShopButton extends View{
protected static final String TAG = AnimShopButton.class.getName();
protected static final int DEFAULT_DURATION = 350;
//控件paddingLeft,paddingTop.+ paint的width
protected int mLeft,mTop;
protected int mWidth,mHeight;
//加减的圆的path和Region
protected Region mAddRegion,mDelRegion;
protected Path mAddPath,mDelPath;
/**
* 加按钮
*/
protected Paint mAddPaint;
//加按钮是否开启fill模式.默认是false
protected boolean isAddFillMode;
//加按钮的背景色前景色
protected int mAddEnableBgColor;
protected int mAddEnableFgColor;
//加按钮不可用时的背景色前景色
protected int mAddDisableBgColor;
protected int mAddDisableFgColor;
/**
* 减按钮
*/
protected Paint mDelPaint;
//按钮是否开启fill模式,默认是storke(false)
protected boolean isDelFillMode;
//按钮的背景色前景色
protected int mDelEnableBgColor;
protected int mDelEnableFgColor;
//按钮不可用时的背景色前景色
protected int mDelDisableBgColor;
protected int mDelDisableFgColor;
//最大数量和当前数量
protected int mMaxCount;
protected int mCount;
//圆的半径
protected float mRadius;
//圆圈的宽度
protected float mCircleWidth;
//线的宽度
protected float mLineWidth;
/**
* 两个圆之间的间距
*/
protected float mGapBetweenCircle;
//绘制数量的Textsize
protected float mTextSize;
protected Paint mTextPaint;
protected Paint.FontMetrics mFontMetrics;
//动画的基准值,动画:减0~1,加1~0;
//普通状态下都是显示的0
protected ValueAnimator mAnimAdd,mAnimDel;
protected float mAnimFraction;
//展开,加入购物车动画
protected ValueAnimator mAnimExpandHint;
protected ValueAnimator mAnimReduceHint;
protected int mPerAnimDuration = DEFAULT_DURATION;
/**
* 增加一个开关,ignoreHintArea:UI显示,动画是否忽略hint收缩区域
*/
protected boolean ignoreHintArea;
//是否处于hintmode下count=0时,且第一段动画做完了.是true
protected boolean isaHintMode;
//提示语收缩动画 0-1,展开1-0
//普通模式时,应该是1,只在isHintMode true才有效
protected float mAnimExpandHintFraction;
//展开动画结束后才显示文字
protected boolean isShowHintText;
//数量为0时.hint文字背景色前景色大小
protected Paint mHintPaint;
protected int mHintBgColor;
protected int mHintTextSize;
protected String mHintText;
protected int mHintFgColor;
/**
* 圆角值
*/
protected int mHintBgRoundValue;
//Feature : 显示正在补货中,此时不允许点击
protected boolean isReplenish;
//画笔,颜色,大小文字
protected Paint mReplenishPaint;
protected int mReplenishTextColor;
protected int mReplenishTextSize;
protected String mReplenishText;
//点击回调
protected IOnAddDelListener mOnAddDelListener;
public AnimShopButton(Context context) {
this(context,null);
}
public AnimShopButton(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public AnimShopButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context,attrs,defStyleAttr);
}
public int getCount() {
return mCount;
}
/**
* 设置当前的数量
*/
public AnimShopButton setCount(int c) {
mCount = c;
//先暂停所有动画
cancelAllAnim();
//复用机制的处理
initAnimSettingsByCount();
return this;
}
/**
* 根据当前count数量,初始化hint提示语相关变量
*/
private void initAnimSettingsByCount() {
mAnimFraction = mCount == 0 ? 1 : 0;
if (mCount == 0) {
isaHintMode = true;
isShowHintText = true;
mAnimExpandHintFraction = 0;
} else {
isaHintMode = false;
isShowHintText = false;
mAnimExpandHintFraction = 1;
}
}
/**
* 暂停所有动画
*/
private void cancelAllAnim() {
if (mAnimAdd != null && mAnimAdd.isRunning()) {
mAnimAdd.cancel();
}
if (mAnimDel != null && mAnimDel.isRunning()) {
mAnimDel.cancel();
}
if (mAnimExpandHint != null && mAnimExpandHint.isRunning()) {
mAnimExpandHint.cancel();
}
if (mAnimReduceHint != null && mAnimReduceHint.isRunning()) {
mAnimReduceHint.cancel();
}
}
public IOnAddDelListener getIOnAddDelListener() {
return mOnAddDelListener;
}
public int getMaxCount() {
return mMaxCount;
}
/**
* 设置最大数量
*/
public AnimShopButton setMaxCount(int c) {
mMaxCount = c;
return this;
}
public boolean isReplenish() {
return isReplenish;
}
public AnimShopButton setIsReplenish(boolean replenish) {
isReplenish = replenish;
if (isReplenish && null == mReplenishPaint) {
mReplenishPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mReplenishPaint.setTextSize(mReplenishTextSize);
mReplenishPaint.setColor(mReplenishTextColor);
}
return this;
}
public int getReplenishTextColor() {
return mReplenishTextColor;
}
public AnimShopButton setReplenishTextColor(int color) {
mReplenishTextColor = color;
return this;
}
public int getReplenishTextSize() {
return mReplenishTextSize;
}
public AnimShopButton setReplenishTextSize(int replenishTextSize) {
this.mReplenishTextSize = replenishTextSize;
return this;
}
public String getReplenishText() {
return mReplenishText;
}
public AnimShopButton setReplenshiText(String replenshiText) {
mReplenishText = replenshiText;
return this;
}
public boolean isAddFillMode() {
return isAddFillMode;
}
public AnimShopButton setAddFillMode(boolean addFillMode) {
isAddFillMode = addFillMode;
return this;
}
public int getAddEnableBgColor() {
return mAddEnableBgColor;
}
public AnimShopButton setAddEnableBgColor(int addEnableBgColor) {
mAddEnableBgColor = addEnableBgColor;
return this;
}
public int getAddEnableFgColor() {
return mAddEnableFgColor;
}
public AnimShopButton setAddEnableFgColor(int addEnableFgColor) {
mAddEnableFgColor = addEnableFgColor;
return this;
}
public int getAddDisableBgColor() {
return mAddDisableBgColor;
}
public AnimShopButton setAddDisableBgColor(int addDisableBgColor) {
mAddDisableBgColor = addDisableBgColor;
return this;
}
public int getAddDisableFgColor() {
return mAddDisableFgColor;
}
public AnimShopButton setAddDisableFgColor(int addDisableFgColor) {
mAddDisableFgColor = addDisableFgColor;
return this;
}
public boolean isDelFillMode() {
return isDelFillMode;
}
public AnimShopButton setIsDelFillMode(boolean isDelFillMode) {
this.isDelFillMode = isDelFillMode;
return this;
}
public int getDelEnableBgColor(){
return mDelEnableBgColor;
}
public AnimShopButton setDelEnableBgColor(int delEnableBgColor) {
mDelEnableBgColor = delEnableBgColor;
return this;
}
public int getDelEnableFgColor() {
return mDelEnableFgColor;
}
public AnimShopButton setDelEnableFgColor(int delEnableFgColor) {
mDelEnableFgColor = delEnableFgColor;
return this;
}
public int getDelDisableBgColor() {
return mDelDisableBgColor;
}
public AnimShopButton setDelDisableBgColor(int delDisableBgColor) {
mDelDisableBgColor = delDisableBgColor;
return this;
}
public int getDelDisableFgColor() {
return mDelDisableFgColor;
}
public AnimShopButton setDelDisableFgColor(int delDisableFgColor){
mDelDisableFgColor = delDisableFgColor;
return this;
}
public float getRadius() {
return mRadius;
}
public AnimShopButton setRadius(float radius) {
mRadius = radius;
return this;
}
public float getCircleWidth() {
return mCircleWidth;
}
public AnimShopButton setCircleWidth(float circleWidth) {
mCircleWidth = circleWidth;
return this;
}
public float getLineWidth() {
return mLineWidth;
}
public AnimShopButton setLineWidth(float lineWidth) {
mLineWidth = lineWidth;
return this;
}
public float getTextSize() {
return mTextSize;
}
public AnimShopButton setTextSize(float textSize) {
mTextSize = textSize;
return this;
}
public float getGapBetweenCircle() {
return mGapBetweenCircle;
}
public AnimShopButton setGapBenweenCircle(float gapBenweenCircle) {
mGapBetweenCircle = gapBenweenCircle;
return this;
}
public int getPerAnimDuration() {
return mPerAnimDuration;
}
public AnimShopButton setPerAnimDuration(int perAnimDuration) {
mPerAnimDuration = perAnimDuration;
return this;
}
public boolean isIgnoreHintArea() {
return ignoreHintArea;
}
public AnimShopButton setIgnoreHintArea(boolean ignoreHintArea) {
this.ignoreHintArea = ignoreHintArea;
return this;
}
public int getHintBgColor()
{
return mHintBgColor;
}
public AnimShopButton setHintBgColor(int hintBgColor){
this.mHintBgColor = hintBgColor;
return this;
}
public int getHintTextSize() {
return mHintTextSize;
}
public AnimShopButton setHintTextSize(int hintTextSize) {
mHintTextSize = hintTextSize;
return this;
}
public String getHintText() {
return mHintText;
}
public AnimShopButton setHintText(String hintText) {
mHintText = hintText;
return this;
}
public int getHintFgColor() {
return mHintFgColor;
}
public AnimShopButton setHintFgColor(int hintFgColor) {
mHintFgColor = hintFgColor;
return this;
}
public int getHintBgRoundValue() {
return mHintBgRoundValue;
}
public AnimShopButton setHintBgRoundValue(int hintBgRoundValue) {
mHintBgRoundValue = hintBgRoundValue;
return this;
}
/**
* 设置加减监听器
* @param iOnAddDelListener
* @return
*/
public AnimShopButton setOnAddDelListener(IOnAddDelListener iOnAddDelListener) {
mOnAddDelListener = iOnAddDelListener;
return this;
}
private void init(Context context, AttributeSet attrs, int defStyleAttr) {
//模拟参数传入设置初始值
initDefaultValue(context);
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.AnimShopButton,defStyleAttr,0);
int indexCount = a.getIndexCount();
for (int i = 0 ; i < indexCount ; i++) {
int index = a.getIndex(i);
switch (index) {
case R.styleable.AnimShopButton_gapBetweenCircle:
mGapBetweenCircle = a.getDimension(index,mGapBetweenCircle);
break;
case R.styleable.AnimShopButton_isAddFillMode:
isAddFillMode = a.getBoolean(index,isAddFillMode);
break;
case R.styleable.AnimShopButton_addEnableBgColor:
mAddEnableBgColor = a.getColor(index,mAddEnableBgColor);
break ;
case R.styleable.AnimShopButton_addEnableFgColor:
mAddEnableFgColor = a.getColor(index,mAddEnableFgColor);
break;
case R.styleable.AnimShopButton_addDisableBgColor:
mAddDisableBgColor = a.getColor(index,mAddDisableBgColor);
break;
case R.styleable.AnimShopButton_addDisableFgColor:
mAddDisableFgColor = a.getColor(index,mAddDisableFgColor);
break;
case R.styleable.AnimShopButton_isDelFillMode:
isDelFillMode = a.getBoolean(index,isDelFillMode);
break;
case R.styleable.AnimShopButton_delEnableBgColor:
mDelEnableBgColor = a.getColor(index,mDelEnableBgColor);
break;
case R.styleable.AnimShopButton_delEnableFgColor:
mDelEnableFgColor = a.getColor(index,mDelEnableFgColor);
break;
case R.styleable.AnimShopButton_delDisableBgColor:
mDelDisableBgColor = a.getColor(index,mDelDisableBgColor);
break;
case R.styleable.AnimShopButton_delDisbleFgColor:
mDelDisableFgColor = a.getColor(index,mDelDisableFgColor);
break;
case R.styleable.AnimShopButton_maxCount:
mMaxCount = a.getInteger(index,mMaxCount);
break;
case R.styleable.AnimShopButton_count:
mCount = a.getInteger(index,mCount);
break;
case R.styleable.AnimShopButton_radius:
mRadius = a.getDimension(index,mRadius);
break;
case R.styleable.AnimShopButton_circleStrokeWidth:
mCircleWidth = a.getDimension(index,mCircleWidth);
break;
case R.styleable.AnimShopButton_lineWidth:
mLineWidth = a.getDimension(index,mLineWidth);
break;
case R.styleable.AnimShopButton_numTextSize:
mTextSize = a.getDimension(index,mTextSize);
break;
case R.styleable.AnimShopButton_hintText:
mHintText = a.getString(index);
break;
case R.styleable.AnimShopButton_hintBgColor:
mHintBgColor = a.getColor(index,mHintBgColor);
break;
case R.styleable.AnimShopButton_hintFgColor:
mHintFgColor = a.getColor(index,mHintFgColor);
break;
case R.styleable.AnimShopButton_hintTextSize:
mHintTextSize = a.getDimensionPixelSize(index,mHintTextSize);
break;
case R.styleable.AnimShopButton_hintBgRoundValue:
mHintBgRoundValue = a.getDimensionPixelOffset(index,mHintBgRoundValue);
break;
case R.styleable.AnimShopButton_ignoreHintArea:
ignoreHintArea = a.getBoolean(index,false);
break;
case R.styleable.AnimShopButton_perAnimDuration:
mPerAnimDuration = a.getInteger(index,DEFAULT_DURATION);
break;
case R.styleable.AnimShopButton_replenishText:
mReplenishText = a.getString(index);
break;
case R.styleable.AnimShopButton_replenishTextColor:
mReplenishTextColor = a.getColor(index,mReplenishTextColor);
break;
case R.styleable.AnimShopButton_replenishTextSize:
mReplenishTextSize = a.getDimensionPixelSize(index,mReplenishTextSize);
break;
}
}
a.recycle();
mAddRegion = new Region();
mDelRegion = new Region();
mAddPath = new Path();
mDelPath = new Path();
mAddPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
if (isAddFillMode) {
mAddPaint.setStyle(Paint.Style.FILL);
}else {
mAddPaint.setStyle(Paint.Style.STROKE);
}
mDelPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
if (isDelFillMode) {
mDelPaint.setStyle(Paint.Style.FILL);
}else {
mDelPaint.setStyle(Paint.Style.STROKE);
}
mHintPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mHintPaint.setStyle(Paint.Style.FILL);
mHintPaint.setTextSize(mHintTextSize);
mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setTextSize(mTextSize);
mFontMetrics = mTextPaint.getFontMetrics();
//+动画
mAnimAdd = ValueAnimator.ofFloat(1,0);
mAnimAdd.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mAnimFraction = (float) animation.getAnimatedValue();
invalidate();
}
});
mAnimAdd.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
}
});
mAnimAdd.setDuration(mPerAnimDuration);
//提示语收缩动画 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) {
//底色不显示了
isaHintMode = 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(ignoreHintArea ? 0 : mPerAnimDuration);
//动画 -
mAnimDel = ValueAnimator.ofFloat(0,1);
mAnimDel.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mAnimFraction = (float) animation.getAnimatedValue();
invalidate();
}
});
//1-0;
mAnimDel.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (mCount == 0) {
Log.d(TAG,"现在还是0onAnimtionEnd() called with : animation = {"+ animation+"}");
if (mAnimExpandHint != null && !mAnimExpandHint.isRunning()){
mAnimExpandHint.start();
}
}
}
});
mAnimDel.setDuration(mPerAnimDuration);
//提示语展开动画
//分析这个动画,最初是个圆,就是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) {
isaHintMode = true;
}
}
});
mAnimExpandHint.setDuration(ignoreHintArea ? 0 : mPerAnimDuration);
}
private void initDefaultValue(Context context) {
mGapBetweenCircle = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 34, context.getResources().getDisplayMetrics());
isAddFillMode = true;
mAddEnableBgColor = 0xFFFFDC5B;
mAddEnableFgColor = Color.BLACK;
mAddDisableBgColor = 0xff979797;
mAddDisableFgColor = Color.BLACK;
isDelFillMode = false;
mDelEnableBgColor = 0xff979797;
mDelEnableFgColor = 0xff979797;
mDelDisableBgColor = 0xff979797;
mDelDisableFgColor = 0xff979797;
/* mMaxCount = 4;
mCount = 1;*/
mRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 12.5f, getResources().getDisplayMetrics());
mCircleWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1f, getResources().getDisplayMetrics());
mLineWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2f, getResources().getDisplayMetrics());
mTextSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 14.5f, getResources().getDisplayMetrics());
mHintText = "加入购物车";
mHintBgColor = mAddEnableBgColor;
mHintFgColor = mAddEnableFgColor;
mHintTextSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12, context.getResources().getDisplayMetrics());
mHintBgRoundValue = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, context.getResources().getDisplayMetrics());
mReplenishText = "补货中";
mReplenishTextColor = 0xfff32d3b;
mReplenishTextSize = mHintTextSize;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mLeft = (int) (getPaddingLeft() + mCircleWidth);
mTop = (int) (getPaddingTop() + mCircleWidth);
mWidth = w;
mHeight = h;
}
@Override
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 ? hSize : computeSize;
break;
case MeasureSpec.UNSPECIFIED:
computeSize = (int) (getPaddingTop() + mRadius * 2 + getPaddingBottom() + mCircleWidth * 2);
hSize = computeSize;
break;
}
setMeasuredDimension(wSize,hSize);
//先暂停动画
cancelAllAnim();
//d复用时会走这里
initAnimSettingsByCount();
}
@Override
protected void onDraw(Canvas canvas) {
if (isReplenish) {
//计算baseline绘制的起点x轴坐标
int baseX = (int) (mWidth / 2 - mReplenishPaint.measureText(mReplenishText) / 2);
//计算baseline绘制的起点y轴的坐标
int baseY = (int) (mHeight / 2 - (mReplenishPaint.descent() + mReplenishPaint.ascent()) / 2);
canvas.drawText(mReplenishText,baseX,baseY,mReplenishPaint);
return;
}
if (!ignoreHintArea && isaHintMode) {
//add hint 展开动画
//if(mCount = 0)
//背景
mHintPaint.setColor(mHintBgColor);
RectF rectF = new RectF(mLeft + (mWidth + mRadius * 2 ) * mAnimExpandHintFraction,mTop,
mWidth - mCircleWidth,mHeight - mCircleWidth);
canvas.drawRoundRect(rectF,mHintBgRoundValue,mHintBgRoundValue,mHintPaint);
if (isShowHintText) {
//前景文字
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 {
//动画mAnimFraction 减 0 -1 加 1 - 0
//动画位移max
float animOffSetMazx = mRadius * 2 + mGapBetweenCircle;
//透明度动画的基准
int animAlphaMax = 255;
int animRotateMax = 360;
//左边 背景 圆
if (mCount > 0) {
mDelPaint.setColor(mDelEnableBgColor);
}else {
mDelPaint.setColor(mDelDisableBgColor);
}
mDelPaint.setAlpha((int) (animAlphaMax * (1 - mAnimFraction)));
mDelPaint.setStrokeWidth(mCircleWidth);
mDelPath.reset();//清除掉path中的线条和曲线,但是不会改变他的fill-type
//改变圆心x坐标实现位移
mDelPath.addCircle(animOffSetMazx * mAnimFraction + 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.save();
canvas.translate(animOffSetMazx * mAnimFraction + mLeft + mRadius,mTop + mRadius);
canvas.rotate((animRotateMax * ( 1 - mAnimFraction)));
canvas.drawLine(-mRadius / 2, 0 , +mRadius / 2,0,mDelPaint);
canvas.restore();
//数量
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)));
//是没有动画的普通写法
canvas.drawText(mCount + "",mGapBetweenCircle / 2 - mTextPaint.measureText(mCount + "") / 2 + mLeft + mRadius * 2
,mTop + mRadius - (mFontMetrics.top + mFontMetrics.bottom) / 2,mTextPaint);
canvas.restore();
//右边背景圆
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);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
if (isReplenish) {
break;
}
//hint模式
if (isaHintMode) {
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);
}
//使用时可以重写onDelClick()和onAddClick()方法,并在合适的时机回调onCountAddSuccess()和onCountDelSuccess()方法
protected void onDelClick() {
if(mCount > 0) {
mCount--;
onCountDelSuccess();
if (null != mOnAddDelListener) {
mOnAddDelListener.onDelSuccess(mCount);
}
}else {
if (null != mOnAddDelListener) {
mOnAddDelListener.onAddFailed(mCount,IOnAddDelListener.FailType.COUNT_MIN);
}
}
}
protected void onAddClick() {
if (mCount < mMaxCount) {
mCount++;
onCountAddSuccess();
if (null != mOnAddDelListener) {
mOnAddDelListener.onAddSuccess(mCount);
}
}else {
if (null != mOnAddDelListener) {
mOnAddDelListener.onAddFailed(mCount,IOnAddDelListener.FailType.COUNT_MAX);
}
}
}
/**
* 数量减少成功后,使用者回调以执行动画
*
*/
public void onCountDelSuccess() {
if(mCount == 0) {
cancelAllAnim();
mAnimDel.start();
}else {
mAnimFraction = 0;
invalidate();
}
}
/**
* 数量增加成功后,使用者回调以执行动画
*/
public void onCountAddSuccess() {
if (mCount == 1) {
cancelAllAnim();
mAnimReduceHint.start();
}else {
mAnimFraction = 0;
invalidate();
}
}
public interface IOnAddDelListener {
enum FailType{
COUNT_MAX,COUNT_MIN
}
void onAddSuccess(int count);
void onAddFailed(int count, FailType failType);
void onDelSuccess(int count);
void onDelFailed(int count, FailType failType);
}
}
文章前面的demo的布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_anim_shop_button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.bailong.myapplication.AnimShopButtonActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="补货中"
/>
<com.example.bailong.myapplication.view.AnimShopButton
android:id="@+id/btn1"
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:ignoreHintArea="true"
app:maxCount="99"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="默认UI属性"
/>
<com.example.bailong.myapplication.view.AnimShopButton
android:id="@+id/asb_1"
app:maxCount="3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:hintFgColor="#ffffff"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="设置了两圆间距、hint和颜色、每段动画动画时间"/>
<com.example.bailong.myapplication.view.AnimShopButton
android:id="@+id/btn2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:count="3"
app:gapBetweenCircle="90dp"
app:hintFgColor="#ffffff"
app:hintText="卫龙拉条,你值得拥有"
app:maxCount="99"
app:perAnimDuration="500"/>
/>
</LinearLayout>
Activity里的代码
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_anim_shop_button);
AnimShopButton btn = (AnimShopButton) findViewById(R.id.btn1);
btn.setIsReplenish(true);
}
相关文章: