前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android自定义View之仿QQ未读消息拖拽效果

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

作者头像
Rouse
发布2019-07-17 17:44:43
1.9K1
发布2019-07-17 17:44:43
举报
文章被收录于专栏:Android补给站

真丶深红骑士

读完需要

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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-07-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Android补给站 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 静态实现
  • 动态实现
  • 中心圆半径变化
  • 距离限制
  • 增加爆炸效果
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档