抖音APP双击点赞效果实现

春天来了,万物复苏,大草原又到了动物们交配的季节。。。

相信大部分的同学都有刷抖音的经历吧,那么当你疯狂给小姐姐点赞的时候,有没有想过这个蹦出来的那些疯狂跳动的心心的是怎么实现的呢?

= 。=,先看效果预览

抖音APP双击点赞效果实现

分析:

效果特点:

1.双击屏幕任意位置,就生成一个心动图

2.心动图不断放大

3.心动图逐渐消失

开始:

1.创建一个新的项目,添加一个class并取名为HotHeartFrameLayout,继承FrameLayout,如下:

ublic class HeartFrameLayout extends FrameLayout{
    public HeartFrameLayout(@NonNull Context context) {
        super(context);
    }

    public HeartFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        //这里绘制图形
    }
}

2.创建一个HeartBean,用来存放需要绘制的心形图属性:

public class HeartBean {
	int alpha; // 透明度
	int X; // X坐标点
	int Y; // Y坐标点
	float scanle; // 缩放比例
	int degrees; // 旋转角度
	Paint paint; //
}

3.创建一个Handler,用来处理界面刷新,表现出动画的效果:

class MyHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 0:
                    Refresh();
                    invalidate();
                    if (list != null && list.size() > 0) {
                        sendEmptyMessageDelayed(0, refreshRate);// 延时
                    }
                    break;
                default:
                    break;
            }
        }
    }
4.初始化相关值:
    List<HeartBean> list;//存放多个心形图
    int MaxAlpha = 255;//透明度,默认为255,0为消失不可见
    boolean START = true;//true为开始动画,false为结束动画
    int refreshRate = 16;//动画刷新频率
    int degreesMin = -30;//最小旋转角度
    int degreesMax = 30;//最大旋转角度
    MyHandler handler = new MyHandler();
    Bitmap bitmap;//初始图片
    Matrix matrix = new Matrix();//控制bitmap旋转角度和缩放的矩阵

5.不断刷新图片

    /***
     * 刷新
     */
    private void Refresh() {
        for (int i = 0; i < list.size(); i++) {
            HeartBean bean = list.get(i);
            if (!START && bean.alpha == 0) {
                list.remove(i);
                bean.paint = null;
                continue;
            } else if (START) {
                START = false;
            }
            bean.scanle += refreshRate > 16 ? 0.03 : 0.1;//放大倍数 默认0.1
            bean.alpha -= 10;//透明度
            if (bean.alpha < 0) {
                bean.alpha = 0;
            }
            bean.paint.setAlpha(bean.alpha);
        }
    }

原理是通过不断的更新图片缩放和透明度,来让图片逐渐放大并同时逐渐消失的效果。

6.双击事件时调用

    /**
     * 开始心动动画
     *
     * @param event 点击事件
     */
    private void startSwipe(MotionEvent event) {
        //
        HeartBean bean = new HeartBean();
        bean.scanle = 1; //
        bean.alpha = MaxAlpha; //
        bean.X = (int) event.getX(); //
        bean.Y = (int) event.getY(); //
        bean.paint = initPaint(bean.alpha);
        bean.degrees = degrees(degreesMin, degreesMax);

        if (list.size() == 0) {
            START = true;
        }
        list.add(bean);
        invalidate();
        if (START) {
            handler.sendEmptyMessage(0);
        }
    }

7.双击事件监听:

    DoubleClickListener mDoubleClickListener;
    int timeout = 400;//双击间格毫秒延时
    long singleClickTime;//记录第一次点击的时间

    @Override
    public boolean onTouchEvent(final MotionEvent event) {
        super.onTouchEvent(event);

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                long newClickTime = System.currentTimeMillis();
                //双击以上事件都会调用心动动画
                if (newClickTime - singleClickTime < timeout) {
                    //开始心动动画
                    startSwipe(event);
                    //调用双击事件
                    if (mDoubleClickListener != null)
                        mDoubleClickListener.onDoubleClick(this);
                }
                singleClickTime = newClickTime;
                break;
        }
        return true;
    }

    /**
     * 接口
     */
    public interface DoubleClickListener {
        void onDoubleClick(View view);
    }

    /**
     * 双击监听接口的方法
     *
     * @param mDoubleClickListener 双击监听
     */
    public void setOnDoubleClickListener(
            final DoubleClickListener mDoubleClickListener) {
        this.mDoubleClickListener = mDoubleClickListener;
    }

8.绘制图片:

@Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        for (int i = 0; i < list.size(); i++) {
            HeartBean heartBean = list.get(i);
            // 重置
            matrix.reset();
            // 缩放原图
            matrix.postScale(heartBean.scanle,
                    heartBean.scanle,
                    heartBean.X + bitmap.getWidth() / 2,
                    heartBean.Y + bitmap.getHeight() / 2);
            // 旋转
            matrix.postRotate(heartBean.degrees,
                    heartBean.X + bitmap.getWidth() / 2,
                    heartBean.Y + bitmap.getHeight() / 2);

            canvas.save();
            canvas.concat(matrix);
            canvas.drawBitmap(bitmap,
                    heartBean.X - bitmap.getWidth() / 2,
                    heartBean.Y - bitmap.getHeight() / 2,
                    heartBean.paint);
            canvas.restore();
        }
    }

 

简单使用

如果觉得自己写起来很不耐烦,没关系,我已经将上面的效果做了一个很好的封装,希望对你有用~

下面简单说下使用方法:

第一步: 在项目根目录的build.gradle文件中加入

allprojects {
	repositories {
		...
		maven { url 'https://jitpack.io' }
	}
}

第二步: 添加依赖项

dependencies {
	implementation 'com.github.KevinYou128:HotHeart:v1.1'
}

第三步:直接在布局文件里调用

<com.yqw.hotheart.HeartFrameLayout 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"
    app:degrees_interval_max="20"
    app:degrees_interval_min="-20"
    app:swipe_image="@drawable/ic_heart"
    tools:context=".MainActivity">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/douyin" />
</com.yqw.hotheart.HeartFrameLayout>

直接在你的父布局外层包裹上hotheart布局就可以了,另外,考虑到缩减布局层数,我特意添加了HeartLinearLayout、HeartConstraintLayout、HeartRelativeLayout、HeartFrameLayout等父容器,你可以直接使用它们来替换你的原生布局,以达到缩减布局层数的效果。

XML属性说明

swipe_image:点击时需要显示的图片

refresh_rate:设置动画刷新频率,默认为16,数值越大动画表现越慢,建议使用默认就好了

degrees_interval_min:图片最小旋转角度,默认-30,取值范围为-360到360(注意取值小于或等于max)

degrees_interval_max:图片最大旋转角度,默认30,取值范围为-360到360(注意取值大于或等于min)

java代码属性说明

setOnDoubleClickListener:双击事件监听

示例:

heartViewGroup.setOnDoubleClickListener(new HeartViewGroup.DoubleClickListener() {
        @Override
        public void onDoubleClick(View view) {
            //双击事件处理
        }
    });


setSwipeImage(int id):设置点击时需要显示的图片

setRefreshRate(int refreshRate):设置动画刷新频率,默认为16,数值越大动画表现越慢,建议使用默认就好了

setDegreesInterval(int min,int max):设置图片旋转角度区间,默认-30到30
min取值范围为-360到360
max取值范围为-360到360

如果还有不明白的地方,可以去查看源码=。=

欢迎star~~

抖音APP双击点赞效果实现源码