Android自定义View-点赞动画效果View-Demo

1、效果演示

在这里插入图片描述
在这里插入图片描述
点赞后,会有动画效果,绘制箭头。

2、实现步骤(代码有详细注释)

1、LikeClickView

package com.ywz.likeclickviewdemo.view;

import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.Nullable;

import com.ywz.likeclickviewdemo.R;
import com.ywz.likeclickviewdemo.utils.SystemUtil;

public class LikeClickView extends View {

    private boolean isLike;
    private Bitmap likeBitmap;
    private Bitmap unLikeBitmap;
    private Bitmap shiningBitmap;
    private Paint bitmapPaint;
    private int left;
    private int top;
    private float handScale = 1.0f;
    private float centerX;
    private int centerY;

    public LikeClickView(Context context) {
        this(context, null, 0);
        init();
    }

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

    public LikeClickView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.JiKeLikeView);
        isLike = typedArray.getBoolean(R.styleable.JiKeLikeView_is_like, false);//attrs中的属性
        typedArray.recycle();//回收资源
        init();
    }

    private void init() {
        Resources resources = getResources();
        likeBitmap = BitmapFactory.decodeResource(resources, R.mipmap.ic_message_like);
        unLikeBitmap = BitmapFactory.decodeResource(resources, R.mipmap.ic_message_unlike);
        shiningBitmap = BitmapFactory.decodeResource(resources, R.mipmap.ic_message_like_shining);
        bitmapPaint = new Paint();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int measureWidth = 0;
        int measureHeight = 0;
        int maxHeight = unLikeBitmap.getHeight() + SystemUtil.dp2px(getContext(), 20);
        int maxWidth = unLikeBitmap.getHeight() + SystemUtil.dp2px(getContext(), 30);
        // 拿到当前控件的测量模式
        int mode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        if (mode != MeasureSpec.EXACTLY) {
            //测量模式未指定   如果有背景 背景多大 我们这个控件就有多大
            int suggestedMinimumWidth = getSuggestedMinimumWidth();
            int suggestedMinimumHeight = getSuggestedMinimumHeight();
            if (suggestedMinimumWidth == 0) {
                measureWidth = maxWidth;
            } else {
                measureWidth = Math.min(suggestedMinimumWidth, maxWidth);
            }
            if (suggestedMinimumHeight == 0) {
                measureHeight = maxHeight;
            } else {
                measureHeight = Math.min(suggestedMinimumHeight, maxHeight);
            }
        } else {
            // 测量模式指定  根据用户定义大小来判断
            measureWidth = Math.min(maxWidth, widthSize);
            measureHeight = Math.min(maxHeight, heightSize);
        }
        setMeasuredDimension(measureWidth,measureHeight);
        getPading(measureWidth,measureHeight);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Bitmap handBitmap = isLike ? likeBitmap : unLikeBitmap;
        // 使用 canvas scale及其他的效果方法时  必须 先调用save 然后再调用restore (这两个方法成对出现的)
        canvas.save();
        canvas.scale(handScale, handScale, centerX, centerY);
        canvas.drawBitmap(handBitmap, left, top, bitmapPaint);
        canvas.restore();
        if (isLike) {
            canvas.drawBitmap(shiningBitmap, left + 10, 0, bitmapPaint);//绘制星星效果
        }
    }

    public void getPading(int measureWidth, int measureHeight) {
        int bitmapWidth = likeBitmap.getWidth();//取得bitmap的宽度
        int bitmapHeight = likeBitmap.getHeight();
        left = (measureWidth - bitmapWidth)/2; //求出间距
        top = (measureHeight - bitmapHeight)/2;
        int width = getMeasuredWidth();//返回的值是 View在Measure过程中测量的宽 / 高
        int height = getMeasuredHeight();
        centerX = width / 2;//取得中心点
        centerY = height / 2;
    }

    // 当这个自定义View 从 界面 脱离消失的时候
    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        likeBitmap.recycle();//回收资源
        unLikeBitmap.recycle();
        shiningBitmap.recycle();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                onClick();
                break;
            default:
                break;
        }
        return super.onTouchEvent(event);
    }
    // 待完善 动画的处理
    private void onClick() {
        isLike = !isLike;
//        ObjectAnimator handScale = ObjectAnimator.ofFloat(this, "handScale", 1.0f, 0.8f, 1.0f);
//        handScale.setDuration(250);
//        handScale.start();

        ValueAnimator valueAnimator = ValueAnimator.ofFloat(1.0f, 0.8f, 1.0f);
        valueAnimator.setDuration(250);
        valueAnimator.start();
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float animatedValue = (float) animation.getAnimatedValue();
                handScale = animatedValue;
                invalidate();//再次调用onDraw,重新绘制
            }
        });
    }
     /**
     * 使用ObjectAnimator 系统会自动调用 该属性的 Set 方法
     * @param value
     */
    public void setHandScale(float value) {
        this.handScale = value;
        invalidate();
    }
}

2、attrs

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="JiKeLikeView">
        <attr name="is_like" format="boolean" />
    </declare-styleable>
</resources>

3、布局文件

<?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:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.ywz.likeclickviewdemo.view.LikeClickView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:is_like="true" />

</LinearLayout>

3、源码下载

https://download.csdn.net/download/qq_31776219/12258870

4、参考资料

1.Android自定义View:源码解析通过getWidth() 与 getMeasuredWidth()获取宽高的区别:https://blog.csdn.net/carson_ho/article/details/98026070

2、动画
1、原生动画
①补间动画
平移:TranslateAnimation
旋转:RotateAnimation
缩放:ScaleAnimation
渐变:AlphaAnimation
小案例:给RecyclerView设置动画。
②属性动画
ObjectAnimatior: translation(x或y),rotation(x或y),scale(x或y)
ValueAnimator
ValueAnimator 类是先改变值,然后 手动赋值 给对象的属性从而实现动画;是 间接 对对象属性进行操作;
ObjectAnimator 类是先改变值,然后 自动赋值 给对象的属性从而实现动画;是 直接 对对象属性进行操作;
ObjectAnimator 类针对的是任意对象 & 任意属性值,并不是单单针对于View对象
如果需要采用ObjectAnimator 类实现动画效果,那么需要操作的对象就必须有该属性的set() & get()
③帧动画
AniamteDrawable

3、事件分发
onTouchEvent:触摸事件的处理
dispatchTouchEvent:传递触摸事件
onInterceptTouchEvent:拦截事件传递
requestDisallowedInterceptTouchEvent(boolean disallowIntercept):请求自己的父布局不要拦截事件

1、安卓中事件的消费会 优先让子View去处理。
2、如果ViewGroup很想处理这个事件怎么办? onInterceptTouchEvent
3、requestDisallowedInterceptTouchEvent 请求ViewGroup不要去处理这个事件。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sunywz

~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值