Android自定义View实现点赞控件
作者:菜小徐
这篇文章主要介绍了Android自定义View实现点赞控件,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
本文实例为大家分享了Android点赞控件的具体代码,供大家参考,具体内容如下
预览效果
目录
图片类:LikeImageView
文字类:LikeCharTextView
整合类:LikeView.java
自定义属性:attrs.xml
代码
LikeCharTextView
public class LikeCharTextView extends View { public static final int DEFAULT_TEXTCOLOR = Color.BLACK; public static final int DEFAULT_TEXTSIZE = 36; private TextPaint newTextPaint, oldTextPaint; private AnimatorSet addAnimator; private AnimatorSet minusAnimator; private int measureWidth; private int measureHeight; private int textColor = DEFAULT_TEXTCOLOR; private int textSize = DEFAULT_TEXTSIZE; private int num; private int oldNum; private int newNum; private int animatorOldY; private float animatorOldAlpha = 1; private int animatorNewY; private float animatorNewAlpha = 0; private int baseline; public LikeCharTextView(Context context) { super(context); init(); } public LikeCharTextView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); initAttr(context, attrs); init(); } public LikeCharTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initAttr(context, attrs); init(); } /** * 初始化属性 * * @param context * @param attrs */ private void initAttr(Context context, @Nullable AttributeSet attrs) { TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.LikeCharTextView); textColor = typedArray.getColor(R.styleable.LikeCharTextView_textColor, DEFAULT_TEXTCOLOR); textSize = typedArray.getDimensionPixelSize(R.styleable.LikeCharTextView_textSize, DEFAULT_TEXTSIZE); num = typedArray.getInt(R.styleable.LikeCharTextView_number, 0); if (0 > num || num > 10) { throw new IllegalArgumentException("Number is only 0-9"); } oldNum = num; typedArray.recycle(); } /** * 初始化 */ private void init() { initPaints(); initParams(); } /** * 初始化画笔 */ private void initPaints() { newTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); newTextPaint.setStyle(Paint.Style.FILL); newTextPaint.setTextSize(textSize); newTextPaint.setColor(textColor); newTextPaint.setTextAlign(Paint.Align.CENTER); oldTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); oldTextPaint.set(newTextPaint); } /** * 初始化参数 */ private void initParams() { Paint.FontMetrics fontMetrics = newTextPaint.getFontMetrics(); measureWidth = (int) newTextPaint.measureText(String.valueOf(num)); measureHeight = (int) (fontMetrics.bottom - fontMetrics.top); float distance = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom; baseline = (int) (measureHeight * 1.0f / 2 + distance); animatorOldY = baseline; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); switch (widthMode) { case MeasureSpec.UNSPECIFIED: break; case MeasureSpec.AT_MOST: widthSize = measureWidth; break; case MeasureSpec.EXACTLY: break; } int heightMode = MeasureSpec.getMode(widthMeasureSpec); int heightSize = MeasureSpec.getSize(widthMeasureSpec); switch (heightMode) { case MeasureSpec.UNSPECIFIED: break; case MeasureSpec.AT_MOST: heightSize = measureHeight; break; case MeasureSpec.EXACTLY: break; } setMeasuredDimension(widthSize, heightSize); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int width = getWidth(); int height = getHeight(); oldTextPaint.setAlpha((int) (255 * animatorOldAlpha)); canvas.drawText(String.valueOf(oldNum), width / 2, animatorOldY, oldTextPaint); newTextPaint.setAlpha((int) (255 * animatorNewAlpha)); canvas.drawText(String.valueOf(newNum), width / 2, animatorNewY, newTextPaint); } public void setTextColor(int textColor) { this.textColor = textColor; init(); invalidate(); } public void setTextSize(int textSize) { this.textSize = textSize; init(); invalidate(); } public void setAnimatorOldY(int animatorOldY) { this.animatorOldY = animatorOldY; invalidate(); } public void setAnimatorOldAlpha(float animatorOldAlpha) { this.animatorOldAlpha = animatorOldAlpha; invalidate(); } public void setAnimatorNewY(int animatorNewY) { this.animatorNewY = animatorNewY; invalidate(); } public void setAnimatorNewAlpha(float animatorNewAlpha) { this.animatorNewAlpha = animatorNewAlpha; invalidate(); } public void setNum(int num) { this.num = num; if (0 > num || num > 10) { throw new IllegalArgumentException("Number is only 0-9"); } oldNum = num; invalidate(); } public void add() { Logger.e("执行加动画.基线:" + baseline); ObjectAnimator oldYAnimator = ObjectAnimator.ofInt(this, "animatorOldY", baseline, 0); ObjectAnimator oldAlphaAnimator = ObjectAnimator.ofFloat(this, "animatorOldAlpha", 1, 0); ObjectAnimator newYAnimator = ObjectAnimator.ofInt(this, "animatorNewY", baseline * 2, baseline); ObjectAnimator newAlphaAnimator = ObjectAnimator.ofFloat(this, "animatorNewAlpha", 0, 1); addAnimator = new AnimatorSet(); addAnimator.playTogether(oldYAnimator, oldAlphaAnimator, newYAnimator, newAlphaAnimator); addAnimator.setInterpolator(new LinearInterpolator()); addAnimator.setDuration(300); addAnimator.start(); } public void minus() { Logger.e("执行减动画.基线:" + baseline); ObjectAnimator oldYAnimator = ObjectAnimator.ofInt(this, "animatorOldY", baseline, baseline * 2); ObjectAnimator oldAlphaAnimator = ObjectAnimator.ofFloat(this, "animatorOldAlpha", 1, 0); ObjectAnimator newYAnimator = ObjectAnimator.ofInt(this, "animatorNewY", 0, baseline); ObjectAnimator newAlphaAnimator = ObjectAnimator.ofFloat(this, "animatorNewAlpha", 0, 1); minusAnimator = new AnimatorSet(); minusAnimator.playTogether(oldYAnimator, oldAlphaAnimator, newYAnimator, newAlphaAnimator); minusAnimator.setInterpolator(new LinearInterpolator()); minusAnimator.setDuration(300); minusAnimator.start(); } public void change(boolean isAdd) { Logger.e("charTextVie.点击事件:" + isAdd); if (isAdd) { if (null != addAnimator && addAnimator.isStarted()) { Logger.e("charTextVie.加动画已执行.取消"); addAnimator.cancel(); } if (null != minusAnimator && minusAnimator.isStarted()) { Logger.e("charTextVie.减动画已执行.取消"); minusAnimator.cancel(); } sumNum(false); minus(); } else { if (null != minusAnimator && minusAnimator.isStarted()) { Logger.e("charTextVie.减动画已执行.取消"); minusAnimator.cancel(); } if (null != addAnimator && addAnimator.isStarted()) { Logger.e("charTextVie.加动画已执行.取消"); addAnimator.cancel(); } sumNum(true); add(); } } /** * 重新计算绘画的值 * * @param isAdd */ private void sumNum(boolean isAdd) { Logger.e("计算值开始"); oldNum = num; newNum = num + (isAdd ? 1 : -1); if (newNum < 0) { newNum = 9; } else if (newNum > 9) { newNum = 0; } num = newNum; Logger.e("计算值结束:" + num); } }
LikeImageView
public class LikeImageView extends View { private Paint imagePaint, shiningPaint; private int shiningMoveX; private int shiningMoveY; private int measureWidth; private int measureHeight; private Bitmap selectedBtimap; private Bitmap selectedShiningBtimap; private Bitmap unSelectedBtimap; private boolean isAdd = false; private float shiningAlpha = isAdd ? 1f : 0f; public LikeImageView(Context context) { super(context); init(); } public LikeImageView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } public LikeImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { imagePaint = new Paint(Paint.ANTI_ALIAS_FLAG); shiningPaint = new Paint(Paint.ANTI_ALIAS_FLAG); selectedBtimap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_like_selected); selectedShiningBtimap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_like_selected_shining); unSelectedBtimap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_like_unselected); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); switch (widthMode) { case MeasureSpec.UNSPECIFIED: break; case MeasureSpec.AT_MOST: widthSize = Math.max(selectedBtimap.getWidth(), unSelectedBtimap.getWidth()); shiningMoveX = (int) (widthSize * 1.0f - selectedShiningBtimap.getWidth()) - 2; break; case MeasureSpec.EXACTLY: break; } int heightMode = MeasureSpec.getMode(widthMeasureSpec); int heightSize = MeasureSpec.getSize(widthMeasureSpec); switch (heightMode) { case MeasureSpec.UNSPECIFIED: break; case MeasureSpec.AT_MOST: heightSize = Math.max(selectedBtimap.getHeight(), unSelectedBtimap.getHeight()); shiningMoveY = (int) (selectedShiningBtimap.getHeight() * 1.0f / 3); heightSize += shiningMoveY; break; case MeasureSpec.EXACTLY: break; } setMeasuredDimension(widthSize, heightSize); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int width = getWidth(); int height = getHeight(); Rect src = new Rect(0, 0, width, height); Rect selectDst = new Rect(0, shiningMoveY, selectedBtimap.getWidth(), height); if (isAdd) { //画红赞 canvas.drawBitmap(selectedBtimap, src, selectDst, imagePaint); //画阴影 shiningPaint.setAlpha((int) (255 * shiningAlpha)); Rect shiningDst = new Rect(shiningMoveX, 0, shiningMoveX + selectedShiningBtimap.getWidth(), selectedShiningBtimap.getHeight()); canvas.drawBitmap(selectedShiningBtimap, src, shiningDst, shiningPaint); } else { //画灰赞 canvas.drawBitmap(unSelectedBtimap, src, selectDst, imagePaint); } } public void setShiningAlpha(float shiningAlpha) { this.shiningAlpha = shiningAlpha; invalidate(); } public void setAdd(boolean add) { isAdd = add; shiningAlpha = 1.0f; invalidate(); } public void changeLike(boolean isAdd) { this.isAdd = !isAdd; invalidate(); anim(); } private void anim() { ObjectAnimator scaleXAnim = ObjectAnimator.ofFloat(this, "scaleX", 0.7f, 1f); scaleXAnim.setDuration(500); ObjectAnimator scaleYAnim = ObjectAnimator.ofFloat(this, "scaleY", 0.7f, 1f); scaleYAnim.setDuration(500); ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(this, "shiningAlpha", 0f, 1f); alphaAnim.setDuration(500); AnimatorSet animatorSet = new AnimatorSet(); animatorSet.playTogether(scaleXAnim, scaleYAnim, alphaAnim); animatorSet.setInterpolator(new BounceInterpolator()); animatorSet.start(); } }
LikeView
public class LikeView extends LinearLayout { private final int IMAGEPADDING = 4; private boolean isAdd = false; private int num; private int textSize; private int textColor; private int imagePadding; private List<LikeCharTextView> charTvs = new ArrayList<>(); private LikeImageView likeImageView; public LikeView(Context context) { super(context); init(); } public LikeView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); initAttr(context, attrs); init(); } public LikeView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initAttr(context, attrs); init(); } private void initAttr(Context context, AttributeSet attrs) { TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.LikeView); textColor = typedArray.getColor(R.styleable.LikeView_textColor, LikeCharTextView.DEFAULT_TEXTCOLOR); textSize = typedArray.getDimensionPixelSize(R.styleable.LikeView_textSize, LikeCharTextView.DEFAULT_TEXTSIZE); num = typedArray.getInt(R.styleable.LikeView_number, 0); imagePadding = typedArray.getDimensionPixelSize(R.styleable.LikeView_imagePadding, IMAGEPADDING); typedArray.recycle(); } /** * 初始化 */ private void init() { initView(); } protected void initView() { removeAllViews(); likeImageView = new LikeImageView(getContext()); likeImageView.setAdd(isAdd); LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); layoutParams.rightMargin = imagePadding; likeImageView.setLayoutParams(layoutParams); addView(likeImageView); charTvs.clear(); String str_num = String.valueOf(num); for (int i = 0; i < str_num.length(); i++) { LikeCharTextView textView = new LikeCharTextView(getContext()); int show_num = Integer.valueOf(str_num.substring(i, i + 1)); Log.e("zanview", "show_num:" + show_num); textView.setTextSize(textSize); textView.setTextColor(textColor); textView.setNum(show_num); addView(textView); charTvs.add(textView); } } public void setNum(int num) { this.num = num; init(); invalidate(); } public void setAdd(boolean add) { isAdd = add; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //计算出所有的childView的宽高 measureChildren(widthMeasureSpec, heightMeasureSpec); setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec)); } /** * 测量宽度 * * @param widthMeasureSpec * @return */ private int measureWidth(int widthMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); switch (widthMode) { case MeasureSpec.UNSPECIFIED: break; case MeasureSpec.AT_MOST: widthSize = 0; for (int i = 0; i < getChildCount(); i++) { View childView = getChildAt(i); //获取子view的宽 int cWidth = childView.getMeasuredWidth(); MarginLayoutParams params = (MarginLayoutParams) childView.getLayoutParams(); widthSize += cWidth + params.leftMargin + params.rightMargin; } break; case MeasureSpec.EXACTLY: break; } return widthSize; } /** * 测量高度 * * @param widthMeasureSpec * @return */ private int measureHeight(int widthMeasureSpec) { int heightMode = MeasureSpec.getMode(widthMeasureSpec); int heightSize = MeasureSpec.getSize(widthMeasureSpec); switch (heightMode) { case MeasureSpec.UNSPECIFIED: break; case MeasureSpec.AT_MOST: heightSize = 0; for (int i = 0; i < getChildCount(); i++) { View childView = getChildAt(i); //获取子view的宽 int cWidth = childView.getMeasuredHeight(); MarginLayoutParams params = (MarginLayoutParams) childView.getLayoutParams(); int height = cWidth + params.leftMargin + params.rightMargin; heightSize = Math.max(heightSize, height); } break; case MeasureSpec.EXACTLY: break; } return heightSize; } private boolean click = false; private final int MOHUFANWEI = 10; private float lastX = 0; private float lastY = 0; @Override public boolean onTouchEvent(MotionEvent event) { super.onTouchEvent(event); float x = event.getX(); float y = event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: if (1 == event.getPointerCount()) { click = true; } break; case MotionEvent.ACTION_UP: if (click) { onClick(); } break; case MotionEvent.ACTION_MOVE: if (Math.abs(lastX - x) > MOHUFANWEI || Math.abs(lastY - y) > MOHUFANWEI) { click = false; } break; } lastX = x; lastY = y; return true; } private void onClick() { Logger.e("点击事件" + isAdd); String str_num = String.valueOf(num); Logger.e("点击事件,str_num:" + str_num); boolean nextAnim = false; if (isAdd) { likeImageView.changeLike(true); for (int i = (str_num.length() - 1); i >= 0; i--) { int chr_num = Integer.valueOf(str_num.substring(i, i + 1)); Logger.e("点击事件,chr_num:%d,charTvs.size:%d,i:%d", chr_num, charTvs.size(), i); Logger.e("是否执行动画:" + (charTvs.size() > i)); if (charTvs.size() > i) { if (i == (str_num.length() - 1) || nextAnim) { Logger.e("点击事件,执行个位动画||%b执行执行上%d位动画", nextAnim, i); charTvs.get(i).change(true); chr_num--; Logger.e("chr_num:%d,是否执行上一位动画:", chr_num, (chr_num < 0)); if (chr_num < 0) { nextAnim = true; } else { nextAnim = false; } Logger.e("nextAnim:" + nextAnim); } } } num--; isAdd = !isAdd; } else { likeImageView.changeLike(false); for (int i = (str_num.length() - 1); i >= 0; i--) { int chr_num = Integer.valueOf(str_num.substring(i, i + 1)); Logger.e("点击事件,chr_num:%d,charTvs.size:%d,i:%d", chr_num, charTvs.size(), i); Logger.e("是否执行动画:" + (charTvs.size() > i)); if (charTvs.size() > i) { if (i == (str_num.length() - 1) || nextAnim) { Logger.e("点击事件,执行个位动画||%b执行执行上%d位动画", nextAnim, i); charTvs.get(i).change(false); chr_num++; Logger.e("chr_num:%d,是否执行上一位动画:", chr_num, (chr_num > 9)); if (chr_num > 9) { nextAnim = true; } else { nextAnim = false; } Logger.e("nextAnim:" + nextAnim); } } } num++; isAdd = !isAdd; } } }
attrs.xml
<attr name="textSize" format="dimension" /> <attr name="textColor" format="color" /> <attr name="number" format="integer" /> <attr name="imageWidth" format="dimension" /> <attr name="imageHeight" format="dimension" /> <declare-styleable name="LikeView"> <attr name="textSize" /> <attr name="textColor" /> <attr name="number" /> <attr name="imageWithd" /> <attr name="imageHeight" /> <attr name="imagePadding" format="dimension" /> </declare-styleable> <declare-styleable name="LikeCharTextView"> <attr name="textSize" /> <attr name="textColor" /> <attr name="number" /> </declare-styleable> <declare-styleable name="LikeImageView"> <attr name="imageWithd" /> <attr name="imageHeight" /> </declare-styleable>
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。