Android自定义View之仿QQ未读消息拖拽效果

2019-07-17 17:44:43 浏览数 (1)

真丶深红骑士

读完需要

15

分钟

速读仅需7分钟

作者:真丶深红骑士 链接:https://juejin.im/post/5d12e22e6fb9a07ee5662d82

1

前言

用QQ的时候,发现未读消息拖拽效果蛮有意思,就模仿了一下。

2

效果图

具体效果如下:

效果图具有以下特性:

  • 小圆点拖拽是有范围的
  • 在拖拽范围进行拖拽后释放小圆点会进行回弹后回到初始位置
  • 拖拽的时候,中心的圆会慢慢变小,拖拽的圆大小不变,中间连接的部分越来越长并且越来细,直至消失
  • 如果超出定义的拖拽范围后进行释放会有爆炸的效果并且消失

3

分析

3.1

组成

先分析这个视图的组成:

  • 中心的小圆:一个固定的圆
  • 跟着手指移动的小圆:一个拖拽的圆
  • 两个圆的连接部分
  • 两条直线(两个圆的直径),用来连接两条贝塞尔曲线,形成封闭图形

3.2

绘制

  • 中心的小圆和拖拽的小圆绘制小圆相对比较简单,直接调用canvas.drawCircle即可,定点中心圆的圆心是固定的,拖拽圆的圆形是手指触摸屏幕的坐标。
  • 两个圆之间连接的部分中间连接的部分其实是两条二阶贝塞尔曲线,具体分析如下:

那么(p1-p3)这条线可以用以下伪码来绘制:

代码语言:javascript复制
1Path rPathLeft = new Path();
2rPathLeft.moveTo(P1.x,P1.y);
3rPathLeft.quadTo(M.x,M.y,P3.x,P3.y);

同理(P2-P4)这条线可以用以下伪代码来绘制:

代码语言:javascript复制
1Path rPathRight = new Path();
2rPathRight.moveTo(P2.x,P2.y);
3rPathRight.quadTo(M.x,M.y,P4.x,P4.y);

  • 两条直线两条直线就是分别是(P1-P2)和(P3-P4)
代码语言:javascript复制
1rPathLeft.lineTo(P4.x,P4.y)
2rPathRight.lineTo(P2.x,P2.y)

绘制以上两条贝塞尔曲线和直线需要五个点:P1,P2,P3,P4,M,其中P1,P2,P3,P4是圆的切点,现在只知道两个圆的中心圆点O1和O2,那么怎么根据这两个点来求其余四个圆的切点呢?先分析:

根据上图可得知:

代码语言:javascript复制
1tan(a) = y / x
2a = arctan(y / x)
3P3.x = X1-r2*sina
4P3.y = Y1-r2*cosa
5P1.x = X0-r1*sina
6P1.y = X0-r1*cosa

同理P2,P4也可以求出。

静态实现

下面先静态实现绘制图像,直接继承帧布局(FrameLayout)

代码语言:javascript复制
 1public class RedPointView extends FrameLayout{
 2    ......
 3
 4    //绘制自己孩子方法
 5    //ViewGroup上绘制东西的时候往往重写的是dispatchDraw()方法而不是onDraw()方法
 6    protected void dispatchDraw(Canvas canvas){
 7        super.dispatchDraw(canvas);
 8
 9        //先绘制固定圆
10        canvas.drawCircle(tCenterPointF.x,tCenterPointF.y,tCenterRadius,rPaint);
11        //再绘制拖拽圆
12        canvas.drawCircle(tDragPointF.x,tDragPointF.y,tDragRadius,rPaint);
13
14        float x = tCenterPointF.x - tDragPointF.x;
15        float y = tDragPointF.y - tCenterPointF.y;
16        //求a的角度
17        double a = Math.atan(y / x);
18
19        //中心圆的p1 x坐标偏移
20        float offsetX1 = (float) (tCenterRadius * Math.sin(a));
21        float offsetY1= (float) (tCenterRadius * Math.cos(a));
22
23        //拖拽圆的p2 x坐标偏移
24        float offsetX2 = (float) (tDragRadius * Math.sin(a));
25        float offsetY2= (float) (tDragRadius * Math.cos(a));
26
27        //p1的坐标
28        float p1_x = tCenterPointF.x - offsetX1;
29        float p1_y = tCenterPointF.y - offsetY1;
30
31
32        //p2的坐标
33        float p2_x = tCenterPointF.x   offsetX1;
34        float p2_y = tCenterPointF.y   offsetY1;
35
36        //p3的坐标
37        float p3_x = tDragPointF.x - offsetX2;
38        float p3_y = tDragPointF.y - offsetY2;
39
40        //p4的坐标
41        float p4_x = tDragPointF.x   offsetX2;
42        float p4_y = tDragPointF.y   offsetY2;
43        //控制点M的坐标
44        float controll_x = (tCenterPointF.x   tDragPointF.x) / 2;
45        float controll_y = (tDragPointF.y   tCenterPointF.y) / 2;
46        //创建Path来绘制路径
47        rPath = new Path();
48        //绘制路径方向:P1->P3->P4->P1
49        rPath.reset();
50        rPath.moveTo(p1_x,p1_y);
51        rPath.quadTo(controll_x,controll_y,p3_x,p3_y);
52        rPath.lineTo(p4_x,p4_y);
53        rPath.quadTo(controll_x,controll_y,p2_x,p2_y);
54        rPath.lineTo(p1_x,p1_y);
55        rPath.close();
56        canvas.drawPath(rPath,tPaint);
57
58    }
59
60}

布局文件直接ConstraintLayout嵌套这个自定义View即可:

代码语言:javascript复制
 1<android.support.constraint.ConstraintLayout
 2    xmlns:android="http://schemas.android.com/apk/res/android"
 3    xmlns:app="http://schemas.android.com/apk/res-auto"
 4    xmlns:tools="http://schemas.android.com/tools"
 5    android:layout_width="match_parent"
 6    android:layout_height="match_parent"
 7    tools:context="com.knight.qq_redpoint.MainActivity">
 8
 9    <com.knight.qq_redpoint.RedPointView
10        android:layout_width="match_parent"
11        android:layout_height="match_parent"
12        app:layout_constraintTop_toTopOf="parent"
13        app:layout_constraintBottom_toBottomOf="parent"
14        app:layout_constraintLeft_toLeftOf="parent"
15        app:layout_constraintRight_toRightOf="parent"/>
16
17</android.support.constraint.ConstraintLayout>

效果图如下:

动态实现

静态效果绘制出来了,那么继续往下走,实现动态效果,实现动态无非是拖拽圆的切点和贝塞尔曲线的控制点在变化,而拖拽圆的圆心其实是触摸屏幕的坐标,那么其切点和控制点根据上一个步骤的公式来求出,下面直接在触摸方法onTouchEvent来处理:

代码语言:javascript复制
代码语言:javascript复制
 1    public boolean onTouchEvent(MotionEvent event){
 2        switch (event.getAction()){
 3            case MotionEvent.ACTION_DOWN:
 4                //event.getRawX:表示的是触摸点距离屏幕左边界的距离 
 5                //event.getRawY:表示的是触摸点距离屏幕上边界的距离
 6                //event.getX()取相对于你触摸的view的左边的偏移(X坐标)
 7                //event.getY()取相对于你触摸的view的顶边的偏移(Y坐标)
 8                float originalDragX = event.getX();
 9                float originalDragy = event.getY();
10                updateDragPoint(originalDragX,originalDragy);
11                break;
12            case MotionEvent.ACTION_MOVE:
13                float overDragX = event.getX();
14                float overDragy = event.getY();
15                //移动的时候不断更新拖拽圆的位置
16                updateDragPoint(overDragX,overDragy);
17                break;
18        }
19        return true;
20
21    }
22
23        //更新拖拽圆的圆心坐标
24    private void updateDragPoint(float x,float y){
25        tDragPointF.set(x,y);
26        postInvalidate();
27
28    }

效果图如下:

中心圆半径变化

仔细观察效果,发现随着拖拽距离的增加,中心圆的半径是越来越小的好像有那么一点点感觉了,但是远远还不够。那么我们可以定一个规则,拖拽距离和中心圆之间的关系,并且设置拖拽最大距离:

代码语言:javascript复制
代码语言:javascript复制
 1    //中心的最小半径
 2    private float minRadius = 8;
 3    //默认拖拽最大距离
 4    private float maxDistance = 160;
 5
 6    //计算拖动过程中中心圆的半径
 7    private float changeCenterRadius() {
 8        float mDistance_x = tDragPointF.x - tCenterPointF.x;
 9        float mDistance_y = tDragPointF.y - tCenterPointF.y;
10        //两个圆之间的距离
11        float mDistance = (float) Math.sqrt(Math.pow(mDistance_x, 2)   Math.pow(mDistance_y, 2));
12        //计算中心圆的半径 这里用拖拽圆默认的半径去减距离变化的长度(这里可以自己定义变化的半径)
13        float r = tDragRadius - minRadius * (mDistance / maxDistance);
14        //计算出半径如果小于最小的半径 就赋值最小半径
15        if (r < minRadius) {
16            r = minRadius;
17        }
18        return r;
19
20
21    }

最后在onDraw方法里,添加计算变化中心圆的半径即可:

代码语言:javascript复制
代码语言:javascript复制
1    //绘制方法
2    protected void onDraw(Canvas canvas) {
3        super.onDraw(canvas);
4        canvas.save();
5        //绘制固定中心圆
6        tCenterRadius = changeCenterRadius();
7        canvas.drawCircle(tCenterPointF.x, tCenterPointF.y, tCenterRadius, rPaint);
8        ....
9    }

效果图如下:

距离限制

下面增加拖拽距离限制,当拖拽距离大于给定的距离时,中心圆就会消失,逻辑很简单,也就是在onTouchEvent里的ACTION_MOVE,计算两个圆的拖拽距离,如果超出给定的拖拽距离,就不绘制贝塞尔曲线和中心固定圆:

代码语言:javascript复制
代码语言:javascript复制
 1    //标识 拖拽距离是否大于规定的拖拽范围
 2    private boolean isOut;
 3
 4    //标识 如果超出拖拽范围
 5    private boolean isOverStep;
 6    //绘制方法
 7    protected void onDraw(Canvas canvas) {
 8        super.onDraw(canvas);
 9        canvas.save();
10
11        if(!isOut){
12            //绘制固定中心圆
13            tCenterRadius = changeCenterRadius();
14            canvas.drawCircle(tCenterPointF.x, tCenterPointF.y, tCenterRadius, rPaint);
15            .....
16        }
17        //一旦超出给定的拖拽距离 就绘制拖拽圆
18        if(!isOverStep){
19            canvas.drawCircle(tDragPointF.x,tDragPointF.y,tDragRadius,rPaint);
20        }
21    }
22
23    //重写onTouchEvent方法
24    public boolean onTouchEvent(MotionEvent event) {
25        switch (event.getAction()) {
26            ........
27            case MotionEvent.ACTION_MOVE:
28                float overDragX = event.getX();
29                float overDragy = event.getY();
30                //移动的时候不断更新拖拽圆的位置
31                updateDragPoint(overDragX, overDragy);
32                float tDragDistance = getDistanceTwo(tCenterPointF,tDragPointF);
33                //判断如果拖拽距离大于给定距离时
34                if(tDragDistance > maxDistance){
35                    isOut = true;
36                }else{
37                    //这里要注意 不能赋值isOut为false 因为一旦超出给定的拖拽距离就没办法恢复了
38                    isOverStep = false;
39                }
40                break;
41        }
42        return true;
43
44    }
45
46    //计算两个圆之间的距离
47    private float getDistanceTwo(PointF tCenterPointF,PointF tDragPointF){
48        return (float) Math.sqrt(Math.pow(tCenterPointF.x - tDragPointF.x,2)   Math.pow(tCenterPointF.y - tDragPointF.y,2));
49    }

效果图如下:

上面只做了超出拖拽范围的效果,下面添加没有超出拖拽范围效果,松开手后拖拽圆会回弹原来的位置,那就要在MotionEvent.ACTION_UP做处理:

代码语言:javascript复制
代码语言:javascript复制
 1    //重写onTouchEvent方法
 2    public boolean onTouchEvent(MotionEvent event) {
 3        switch (event.getAction()) {
 4            ....
 5            case MotionEvent.ACTION_UP:
 6                getDistanceTwo(tCenterPointF,tDragPointF);
 7                //这里要判断
 8                if(!isOut){
 9                    //没有超出
10                    kickBack();
11                }
12                postInvalidate();
13                break;
14        }
15        return true;
16
17    }
18
19   /**
20     * 拖拽圆回弹动画
21     *
22     */
23    private void kickBack() {
24        final PointF initPoint = new PointF(tDragPointF.x,
25                tDragPointF.y);
26        final PointF finishPoint = new PointF(tCenterPointF.x,
27                tCenterPointF.y);
28        //值从0平滑过渡1
29        ValueAnimator animator = ValueAnimator.ofFloat(0.0f,1.0f);
30        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
31
32            @Override
33            public void onAnimationUpdate(ValueAnimator animation) {
34                //获取动画执行进度
35                float rFraction = animation.getAnimatedFraction();
36                //更新拖拽圆的圆心
37                PointF updateDragPoint = getPoint(
38                        initPoint, finishPoint, rFraction);
39                updateDragPoint(updateDragPoint.x, updateDragPoint.y);
40            }
41        });
42        //设置动画插值器
43        animator.setInterpolator(new OvershootInterpolator(3.0f));
44        //动画时间
45        animator.setDuration(500);
46        animator.start();
47    }
48
49
50    /**
51     *
52     * 根据百分比获取两点之间的某个点坐标
53     * @param initPoint 初识圆
54     * @param finishPoint 最终圆
55     * @param percent 百分比
56     * @return
57     * 
58     */
59    public PointF getPoint(PointF initPoint, PointF finishPoint, float percent) {
60        return new PointF(getValue(initPoint.x , finishPoint.x,percent), getValue(initPoint.y , finishPoint.y,percent));
61    }
62
63    /**
64     * 获取分度值
65     * @param start
66     * @param finish
67     * @param fraction
68     * @return
69     */
70    public float getValue(Number start, Number finish,float fraction){
71        return start.floatValue()   (finish.floatValue() - start.floatValue()) * fraction;
72    }

效果图:

增加爆炸效果

当拖拽圆超出拖拽范围后,会有一个爆炸效果后并消失,下面添加爆炸效果:

代码语言:javascript复制
代码语言:javascript复制
 1    //初始化爆炸图片
 2    private int[] explodeImgae = new int[]{
 3        R.mipmap.explode_1,
 4        R.mipmap.explode_2,
 5        R.mipmap.explode_3,
 6        R.mipmap.explode_4,
 7        R.mipmap.explode_5
 8    };
 9    //爆炸ImageView
10    private ImageView explodeImage;
11复制代码并在init初始化方法里添加对这爆炸图像:
12        //添加爆炸图像
13        explodeImage = new ImageView(getContext());
14        //设置布局参数
15        LayoutParams lp = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);
16        explodeImage.setLayoutParams(lp);
17        explodeImage.setImageResource(R.mipmap.explode_1);
18        //一开始不显示
19        explodeImage.setVisibility(View.INVISIBLE);
20        //增加到viewGroup中
21        addView(explodeImage);

并实现播放动画方法:

代码语言:javascript复制
代码语言:javascript复制
 1    /**
 2     *
 3     * 超过拖拽范围外显示爆炸效果
 4     *
 5     */
 6    private void showExplodeImage(){
 7        //属性动画
 8        ValueAnimator animator = ValueAnimator.ofInt(0,explodeImgaes.length - 1);
 9        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
10            @Override
11            public void onAnimationUpdate(ValueAnimator animation) {
12                //不断更新图像变化
13                explodeImage.setBackgroundResource(explodeImgaes[(int) animation.getAnimatedValue()]);
14            }
15        });
16        //为动画添加监听
17        animator.addListener(new AnimatorListenerAdapter() {
18            @Override
19            public void onAnimationCancel(Animator animation) {
20                super.onAnimationCancel(animation);
21            }
22
23            @Override
24            public void onAnimationEnd(Animator animation) {
25                super.onAnimationEnd(animation);
26                //结束了 把图像设置不可见状态
27                explodeImage.setVisibility(View.GONE);
28            }
29
30            @Override
31            public void onAnimationRepeat(Animator animation) {
32                super.onAnimationRepeat(animation);
33            }
34
35            @Override
36            public void onAnimationStart(Animator animation) {
37                super.onAnimationStart(animation);
38                //开始时 设置为可见
39                explodeImage.setVisibility(View.VISIBLE);
40            }
41
42            @Override
43            public void onAnimationPause(Animator animation) {
44                super.onAnimationPause(animation);
45            }
46
47            @Override
48            public void onAnimationResume(Animator animation) {
49                super.onAnimationResume(animation);
50            }
51        });
52        //时间
53        animator.setDuration(600);
54        //播放一次
55        animator.setRepeatMode(ValueAnimator.RESTART);
56        //差值器
57        animator.setInterpolator(new OvershootInterpolator());
58        animator.start();
59    }

在MotionEvent.ACTION_UP里:

代码语言:javascript复制
代码语言:javascript复制
 1            case MotionEvent.ACTION_UP:
 2                getDistanceTwo(tCenterPointF,tDragPointF);
 3                //这里要判断
 4                if(!isOut){
 5                    //没有超出
 6                    kickBack();
 7                }
 8                if(isOut){
 9                    //抬起标识
10                    isOverandUp = true;
11                    //让爆炸图片在原点中央
12                    explodeImage.setX(event.getX() - tDragRadius);
13                    explodeImage.setY(event.getY() - tDragRadius);
14                    //如果中心圆和拖拽圆大于拖拽距离 就播放爆炸
15                    if(getDistanceTwo(tCenterPointF,tDragPointF) > maxDistance){
16                        showExplodeImage();
17                    }
18                    //这里是如果拖拽圆和中心圆距离已经超出拖拽距离 然后又把拖拽圆移动与中心圆大于30 还是会爆炸
19                    if(getDistanceTwo(tCenterPointF,tDragPointF) >=30){
20                        showExplodeImage();
21                    }
22
23                }
24                postInvalidate();
25                break;

在dispatchView超出拖拽距离到小于恢复中心圆的距离逻辑:

代码语言:javascript复制
代码语言:javascript复制
 1        if(isOut){
 2            //如果一开始超出拖拽范围 后面又移动拖拽圆与中心圆的距离少于30,就恢复中心圆位置
 3            if(getDistanceTwo(tCenterPointF,tDragPointF) < 30 && isOverandUp){
 4                canvas.drawCircle(tCenterPointF.x, tCenterPointF.y, tCenterRadius, rPaint);
 5                isOut = false;
 6                isOverandUp = false;
 7            }
 8
 9
10        }
11
12
13        //一旦超出给定的拖拽距离 就绘制拖拽圆
14        if(!isOverStep){
15            //如果超出并且抬起
16            if(!isOverandUp && isOut){
17                canvas.drawCircle(tDragPointF.x,tDragPointF.y,tDragRadius,rPaint);
18            }
19
20        }

效果图如下:

4

添加到ListView

4.1

添加到WindowManager

上面所实现的效果还远远不够,怎么像QQ那样,在ListView或者Recycleview里小圆点能自由在屏幕内拖拽呢?因为view只能在它的父控件内绘制,所以也只能在自己的列表内移动,那怎么能在全屏拖拽呢?只能借助WindowManager,也就是当将要拖拽的圆点添加到windowManager,并且设置触摸监听,自定义拖拽view从继承ViewGroup变为继承View

代码语言:javascript复制

构造函数将拖拽的view和WindowManager对象传进来,并初始化一些参数:

代码语言:javascript复制
 1   //初始化小圆
 2    private void init() {
 3        //手动测量
 4        dragView.measure(1,1);
 5        dragViewWidth = dragView.getMeasuredWidth() / 2;
 6        dragViewHeight = dragView.getMeasuredHeight() / 2;
 7
 8        tDragRadius = dragViewHeight;
 9        //中心圆的半径
10        tCenterRadius = SystemUtil.dp2px(context,8);
11        //最大拖拽距离
12        maxDistance = SystemUtil.dp2px(context,80);
13        //最小半径
14        minRadius = SystemUtil.dp2px(context,3);
15
16        //布局参数
17        params = new WindowManager.LayoutParams();
18        //背景透明
19        params.format = PixelFormat.TRANSLUCENT;
20        params.height = WindowManager.LayoutParams.WRAP_CONTENT;
21        params.width = WindowManager.LayoutParams.WRAP_CONTENT;
22        //以左上角为基准
23        params.gravity = Gravity.TOP | Gravity.LEFT;
24
25    }
代码语言:javascript复制

4.2

更新拖拽view的位置

在上面例子中更新拖拽圆updateDragPoint的方法,也同样通过WindowManager.updateViewLayout来更新拖拽view的的位置:

代码语言:javascript复制
代码语言:javascript复制
 1    /**
 2     * 更新拖拽圆心坐标
 3     * @param x
 4     * @param y
 5     */
 6    private void updateDragPoint(float x, float y) {
 7        tDragPointF.set(x, y);
 8        changeManagerView(x,y);
 9        postInvalidate();
10
11    }
12
13    /**
14     * 重新绘制拖拽圆的布局
15     * @param x x坐标
16     * @param y y坐标
17     */
18    private void changeManagerView(float x,float y){
19        params.x = (int)(x - dragViewWidth);
20        params.y = (int)(y - dragViewHeight - statusBarHeight);
21        windowManager.updateViewLayout(dragView,params);
22    }
代码语言:javascript复制

4.3

添加状态监听

增加拖拽圆和中心圆的拖拽情况监听:

代码语言:javascript复制
代码语言:javascript复制
 1public interface DragViewStatusListener {
 2
 3    /**
 4     * 在拖拽范围外移动
 5     *
 6     * @param dragPoint
 7     */
 8    void outDragMove(PointF dragPoint);
 9
10    /**
11     * 在拖拽范围外移动
12     * 产生爆炸效果
13     *
14     */
15    void outDragMoveUp(PointF dragPoint);
16
17    /**
18     * 在拖拽范围内移动
19     *
20     * @param dragPoint
21     */
22    void inDragUp(PointF dragPoint);
23
24
25    /**
26     * 当移出拖拽范围 后拖拽到范围内 恢复中心圆
27     *
28     */
29    void recoverCenterPoint(PointF centerPoint);
30
31
32}

在对应触发的情况下实现监听回调,如爆炸的动画:

代码语言:javascript复制
代码语言:javascript复制
 1            case MotionEvent.ACTION_UP:
 2                getDistanceTwo(tCenterPointF,tDragPointF);
 3                //这里要判断
 4                if(!isOut){
 5                    //没有超出
 6                    kickBack();
 7                }
 8                if(isOut){
 9                    //抬起标识
10                    isOverandUp = true;
11                    //让爆炸图片在原点中央
12                    //explodeImage.setX(event.getX() - tDragRadius);
13                    //explodeImage.setY(event.getY() - tDragRadius);
14                    //如果中心圆和拖拽圆大于拖拽距离 就播放爆炸
15                    if(getDistanceTwo(tCenterPointF,tDragPointF) > maxDistance){
16                        //这里监听做爆炸效果
17                        if(dragViewStatusListener != null){
18                             dragViewStatusListener.outDragMoveUp(tDragPointF);
19                        }
20                    }
21                    //这里是如果拖拽圆和中心圆距离已经超出拖拽距离 然后又把拖拽圆移动与中心圆大于30 还是会爆炸
22                    if(getDistanceTwo(tCenterPointF,tDragPointF) >=30){
23                        if(dragViewStatusListener != null){
24                           dragViewStatusListener.outDragMoveUp(tDragPointF);
25                        }
26                    }
27                }
代码语言:javascript复制

4.4

新建中间桥梁

新建一个类,主要用来辅助,主要用来创建拖拽自定义view和创建WindowManager对象初始化数据,并且作出各种情况下(在范围内拖拽,范围外拖拽)的逻辑和爆炸逻辑,主要代码在BetterRedPointViewControl中

代码语言:javascript复制
 1public class BetterRedPointViewControl implements View.OnTouchListener,DragViewStatusListener{
 2    .......
 3
 4    @Override
 5    public boolean onTouch(View v, MotionEvent event) {
 6        int action = MotionEventCompat.getActionMasked(event);
 7        if(action == MotionEvent.ACTION_DOWN){
 8
 9            ViewParent parent = v.getParent();
10            if(parent == null){
11                return false;
12            }
13
14            parent.requestDisallowInterceptTouchEvent(true);
15            statusHeight = SystemUtil.getStatusBarHeight(showView);
16            showView.setVisibility(View.INVISIBLE);
17            dragView = LayoutInflater.from(context).inflate(mDragViewId,null,false);
18            //获取文本内容
19            getText();
20            windowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
21            //每当触摸的时候就创建拖拽的小圆
22            betterRedPointView = new BetterRedPointView(context,dragView,windowManager);
23            //初始化数据
24            init();
25            //设置监听回调
26            betterRedPointView.setDragViewStatusListener(this);
27            //添加到窗体进行显示
28            windowManager.addView(betterRedPointView,params);
29            windowManager.addView(dragView,params);
30
31        }
32        betterRedPointView.onTouchEvent(event);
33        return true;
34    }
35    ......
36}
代码语言:javascript复制

4.5

调用

在Recycleview内执行调用即可,主要代码在RecycleviewAdapter中

代码语言:javascript复制
 1public class RecycleviewAdapter extends RecyclerView.Adapter<ItemHolder> {
 2    ......
 3
 4   @Override
 5    public void onBindViewHolder(@NonNull ItemHolder itemHolder, final int i) {
 6        itemHolder.tv_dragView.setText(String.valueOf(i));
 7
 8        Glide.with(mContext).load(R.mipmap.iv_image).apply(RequestOptions.bitmapTransform(new CircleCrop()).override(200,200)).into(itemHolder.iv_head);
 9        //是否隐藏要拖拽的view
10        if(needRemoveList.contains(i)){
11            itemHolder.tv_dragView.setVisibility(View.GONE);
12        }
13        else {
14            itemHolder.tv_dragView.setVisibility(View.VISIBLE);
15            itemHolder.tv_dragView.setText(String.valueOf(i));
16        }
17        //一个是拖拽的view 一个是拖拽的view布局
18        new BetterRedPointViewControl(mContext, itemHolder.tv_dragView, R.layout.includeview, new BetterRedPointViewControl.DragStatusListener() {
19            /**
20             * 在范围内
21             * 
22             */
23            @Override
24            public void inScope() {
25                notifyDataSetChanged();
26            }
27
28            /**
29             * 在范围外
30             * 
31             */
32            @Override
33            public void outScope() {
34                needRemoveList.add(i);
35                notifyDataSetChanged();
36
37            }
38        });
39    }
40}
代码语言:javascript复制

4.6

最终效果

效果图如下:

5

项目例子

github地址:

https://github.com/KnightAndroid/QQ_RedPoint

0 人点赞