One, foreword

When using QQ, found that did not read the message drag effect is very interesting, imitate once.

Second, the effect drawing

The specific effects are as follows:


The effect map has the following characteristics:

  • Dot drag has scope
  • Drag and release the dot in the drag range and it bounces back to its original position
  • When you drag, the center circle gets smaller and smaller. The size of the center circle stays the same. The connecting part gets longer and thinner until it disappears
  • If released beyond the defined drag range it will explode and disappear

Three, the analysis

Of 1.

Start by analyzing the composition of this view:

  • Small central circle: a fixed circle
  • Small circle followed by finger movement: a drag circle
  • The joining part of two circles
  • Two straight lines (diameters of two circles) are used to connect two Bessel curves to form a closed figure

2. Draw

  • Center circle and drag circle drawing circle is relatively simple, direct callcanvas.drawCircleCan, the center of the center circle is fixed, drag the circle of the circle is the finger touch screen coordinates.
  • The connected part between the two circles and the connected part in the middle are actually two second-order Bessel curves, which are analyzed as follows:

Then the line (P1-P3) can be drawn with the following pseudocode:

Path rPathLeft = new Path();
rPathLeft.moveTo(P1.x,P1.y);
rPathLeft.quadTo(M.x,M.y,P3.x,P3.y);
Copy the code

Similarly, the line (p2-p4) can be drawn using the following pseudocode:

Path rPathRight = new Path();
rPathRight.moveTo(P2.x,P2.y);
rPathRight.quadTo(M.x,M.y,P4.x,P4.y);
Copy the code
  • So these two lines these two lines are P1 minus P2 and P3 minus P4.
rPathLeft.lineTo(P4.x,P4.y)
rPathRight.lineTo(P2.x,P2.y)
Copy the code

Five points are needed to draw the above two Bezier curves and lines: P1,P2,P3,P4,M. Among them, P1,P2,P3 and P4 are the tangents of the circles. Now we only know the central points O1 and O2 of the two circles, so how to calculate the tangents of the other four circles according to these two points? First analysis:

According to the figure above, it can be known that:

tan(a) = y / x

a = arctan(y / x)

P3.x = X1-r2*sina

P3.y = Y1-r2*cosa

P1.x = X0-r1*sina

P1.y = X0-r1*cosa

Same thing for P2 and P4.

2.1. Static implementation

Let’s draw the image statically and inherit the FrameLayout directly.

public class RedPointView extends FrameLayout{
    / / brush
    private Paint rPaint,tPaint;
    / / radius
    private int rRadius;
    // Set the center of the circle
    PointF tCenterPointF = new PointF(300.400);
    // Drag the center of the circle
    PointF tDragPointF = new PointF(200.550);
    // Set the radius of the circle
    private float tCenterRadius = 30;
    // Drag the radius of the circle
    private float tDragRadius = 30;
    / / line
    private Path rPath;

    // Call the constructor of two arguments
    public RedPointView(Context context) {
        this(context,null);
    }
    // Two arguments constructor, call three arguments constructor
    public RedPointView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }
    // A three-argument constructor
    public RedPointView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }
    // Initializes the small circle
    private void init(a){
        // Initialize the Paint object
        rPaint = new Paint();
        // Set the color to red
        rPaint.setColor(Color.RED);
        // Set anti-aliasing
        rPaint.setAntiAlias(true);
        // Set the fill
        rPaint.setStyle(Paint.Style.FILL);

        tPaint = new Paint();
        // Set the color to red
        tPaint.setColor(Color.RED);
        // Set anti-aliasing
        tPaint.setAntiAlias(true);
        / / radius of 25
        rRadius = 25;
    }

    // Draw your own child method
    // The dispatchDraw() method instead of onDraw() method is overridden when drawing on a ViewGroup
    protected void dispatchDraw(Canvas canvas){
        super.dispatchDraw(canvas);

        // Draw the fixed circle first
        canvas.drawCircle(tCenterPointF.x,tCenterPointF.y,tCenterRadius,rPaint);
        // Draw the drag circle again
        canvas.drawCircle(tDragPointF.x,tDragPointF.y,tDragRadius,rPaint);

        float x = tCenterPointF.x - tDragPointF.x;
        float y = tDragPointF.y - tCenterPointF.y;
        // Find the Angle of A
        double a = Math.atan(y / x);

        // P1 x offset of the center circle
        float offsetX1 = (float) (tCenterRadius * Math.sin(a));
        float offsetY1= (float) (tCenterRadius * Math.cos(a));

        // Drag the circle's p2 x offset
        float offsetX2 = (float) (tDragRadius * Math.sin(a));
        float offsetY2= (float) (tDragRadius * Math.cos(a));

        / / p1 coordinates
        float p1_x = tCenterPointF.x - offsetX1;
        float p1_y = tCenterPointF.y - offsetY1;


        / / p2 coordinates
        float p2_x = tCenterPointF.x + offsetX1;
        float p2_y = tCenterPointF.y + offsetY1;
        
        / / p3 coordinates
        float p3_x = tDragPointF.x - offsetX2;
        float p3_y = tDragPointF.y - offsetY2;

        / / p4 coordinates
        float p4_x = tDragPointF.x + offsetX2;
        float p4_y = tDragPointF.y + offsetY2;
        // Control point M coordinates
        float controll_x = (tCenterPointF.x + tDragPointF.x) / 2;
        float controll_y = (tDragPointF.y + tCenterPointF.y) / 2;
        // Create Path to draw the Path
        rPath = new Path();
        // Draw path direction :P1->P3->P4->P1rPath.reset(); rPath.moveTo(p1_x,p1_y); rPath.quadTo(controll_x,controll_y,p3_x,p3_y); rPath.lineTo(p4_x,p4_y); rPath.quadTo(controll_x,controll_y,p2_x,p2_y); rPath.lineTo(p1_x,p1_y); rPath.close(); canvas.drawPath(rPath,tPaint); }}Copy the code

The layout file simply ConstraintLayout nested the custom View:

<android.support.constraint.ConstraintLayout
    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"
    tools:context="com.knight.qq_redpoint.MainActivity">

    <com.knight.qq_redpoint.RedPointView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"/>

</android.support.constraint.ConstraintLayout>
Copy the code

The renderings are as follows:

2.2. Dynamic implementation

Static effect is drawn out, then continue to go down, realize the dynamic effect, realize the dynamic was simply drag the circle tangent point and bezier control point in the change, and drag the circle in the center is touching the screen coordinates, then the point of tangency and control points based on a formula to calculate step, directly in touch method onTouchEvent to deal with below:

    public boolean onTouchEvent(MotionEvent event){
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                //event.getRawX: represents the distance between the touch point and the left edge of the screen
                //event.getRawY: Represents the distance between the touch point and the on-screen boundary
                // event.getx () takes the offset relative to the left of the view you touched (X coordinates)
                // event.gety () takes the offset relative to the top edge of the view you are touching.
                float originalDragX = event.getX();
                float originalDragy = event.getY();
                updateDragPoint(originalDragX,originalDragy);
                break;
            case MotionEvent.ACTION_MOVE:
                float overDragX = event.getX();
                float overDragy = event.getY();
                // Update the position of the drag circle as you move it
                updateDragPoint(overDragX,overDragy);
                break;
        }
        return true;

    }
    
        // Update the center coordinates of the drag circle
    private void updateDragPoint(float x,float y){
        tDragPointF.set(x,y);
        postInvalidate();

    }
Copy the code

The renderings are as follows:

2.2.1 Radius change of center circle

Careful observation of the effect, found that as the drag distance increases, the radius of the center circle is getting smaller and smaller, as if there is a little feeling, but far from enough. Then we can make a rule, the relationship between the drag distance and the center circle, and set the drag maximum distance:

    // The minimum radius of the center
    private float minRadius = 8;
    // Default maximum drag distance
    private float maxDistance = 160;// Calculate the radius of the center circle during dragging
    private float changeCenterRadius(a) {
        float mDistance_x = tDragPointF.x - tCenterPointF.x;
        float mDistance_y = tDragPointF.y - tCenterPointF.y;
        // The distance between two circles
        float mDistance = (float) Math.sqrt(Math.pow(mDistance_x, 2) + Math.pow(mDistance_y, 2));
        // Calculate the radius of the center circle. Here drag the default radius of the circle to reduce the length of the distance change (you can define the radius change here).
        float r = tDragRadius - minRadius * (mDistance / maxDistance);
        // Assign the minimum radius if the radius is less than the minimum radius
        if (r < minRadius) {
            r = minRadius;
        }
        return r;


    }


Copy the code

Finally, in the onDraw method, add the calculation of the radius of the change center circle:

    // Draw method
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.save();
        // Draw the fixed center circletCenterRadius = changeCenterRadius(); canvas.drawCircle(tCenterPointF.x, tCenterPointF.y, tCenterRadius, rPaint); . }Copy the code

The renderings are as follows:

2.2.2 Distance limitation

When the drag distance is greater than the given distance, the center circle will disappear. The logic is very simple, that is, the ACTION_MOVE in onTouchEvent, calculate the drag distance of the two circles. If the drag distance exceeds the given distance, the Bezier curve and the fixed center circle will not be drawn:

    // Indicate whether the drag distance is greater than the specified drag range
    private boolean isOut;

    // indicate if the drag is out of range
    private boolean isOverStep;
    // Draw method
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.save();

        if(! isOut){// Draw the fixed center circletCenterRadius = changeCenterRadius(); canvas.drawCircle(tCenterPointF.x, tCenterPointF.y, tCenterRadius, rPaint); . }// Draw a drag circle once the given drag distance is exceeded
        if(!isOverStep){
            canvas.drawCircle(tDragPointF.x,tDragPointF.y,tDragRadius,rPaint);
        }
    }
    
    // Override the onTouchEvent method
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            ........
            case MotionEvent.ACTION_MOVE:
                float overDragX = event.getX();
                float overDragy = event.getY();
                // Update the position of the drag circle as you move it
                updateDragPoint(overDragX, overDragy);
                float tDragDistance = getDistanceTwo(tCenterPointF,tDragPointF);
                // Determine if the drag distance is greater than the specified distance
                if(tDragDistance > maxDistance){
                    isOut = true;
                }else{
                    // Do not set isOut to false because there is no recovery once the drag distance is exceeded
                    isOverStep = false;
                }
                break;
        }
        return true;

    }
    
    // Calculate the distance between two circles
    private float getDistanceTwo(PointF tCenterPointF,PointF tDragPointF){
        return (float) Math.sqrt(Math.pow(tCenterPointF.x - tDragPointF.x,2) + Math.pow(tCenterPointF.y - tDragPointF.y,2));
    }

Copy the code

The renderings are as follows:

MotionEvent.ACTION_UP

    // Override the onTouchEvent method
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            ....
            case MotionEvent.ACTION_UP:
                getDistanceTwo(tCenterPointF,tDragPointF);
                // Here to judge
                if(! isOut){// No more than
                    kickBack();
                }
                postInvalidate();
                break;
        }
        return true;

    }
    
   /** * Drag circle rebound animation ** /
    private void kickBack(a) {
        final PointF initPoint = new PointF(tDragPointF.x,
                tDragPointF.y);
        final PointF finishPoint = new PointF(tCenterPointF.x,
                tCenterPointF.y);
        // The value transitions smoothly from 0 to 1
        ValueAnimator animator = ValueAnimator.ofFloat(0.0 f.1.0 f);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                // Get the animation execution progress
                float rFraction = animation.getAnimatedFraction();
                // Update the center of the drag circlePointF updateDragPoint = getPoint( initPoint, finishPoint, rFraction); updateDragPoint(updateDragPoint.x, updateDragPoint.y); }});// Set the animation interpolator
        animator.setInterpolator(new OvershootInterpolator(3.0 f));
        // Animation time
        animator.setDuration(500);
        animator.start();
    }


    /** ** gets the coordinates of some point between two points * based on the percentage@paramInitPoint identifies the circle for the first time@paramFinishPoint Final circle *@paramPercent *@return* * /
    public PointF getPoint(PointF initPoint, PointF finishPoint, float percent) {
        return new PointF(getValue(initPoint.x , finishPoint.x,percent), getValue(initPoint.y , finishPoint.y,percent));
    }

    /** * get the indexing value *@param start
     * @param finish
     * @param fraction
     * @return* /
    public float getValue(Number start, Number finish,float fraction){
        return start.floatValue() + (finish.floatValue() - start.floatValue()) * fraction;
    }
Copy the code

Effect:

2.2.3 Increase explosion effect

When the drag circle exceeds the drag range, there will be an explosion effect and then disappear. Add the explosion effect below:

    // Initialize the explosion image
    private int[] explodeImgae = new int[]{
        R.mipmap.explode_1,
        R.mipmap.explode_2,
        R.mipmap.explode_3,
        R.mipmap.explode_4,
        R.mipmap.explode_5
    };
    / / ImageView explosion
    private ImageView explodeImage;
Copy the code

Add the explosion image to the init initialization method:

        // Add an explosion image
        explodeImage = new ImageView(getContext());
        // Set layout parameters
        LayoutParams lp = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);
        explodeImage.setLayoutParams(lp);
        explodeImage.setImageResource(R.mipmap.explode_1);
        // Do not display at first
        explodeImage.setVisibility(View.INVISIBLE);
        // Add to viewGroup
        addView(explodeImage);
Copy the code

And implement the method of playing animation:

    /** ** Display explosion effect outside drag range ** /
    private void showExplodeImage(a){
        // Property animation
        ValueAnimator animator = ValueAnimator.ofInt(0,explodeImgaes.length - 1);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                // Update the image constantly
                explodeImage.setBackgroundResource(explodeImgaes[(int) animation.getAnimatedValue()]); }});// Add a listener for the animation
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationCancel(Animator animation) {
                super.onAnimationCancel(animation);
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                // Make the image invisible
                explodeImage.setVisibility(View.GONE);
            }

            @Override
            public void onAnimationRepeat(Animator animation) {
                super.onAnimationRepeat(animation);
            }

            @Override
            public void onAnimationStart(Animator animation) {
                super.onAnimationStart(animation);
                // Start with visible
                explodeImage.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationPause(Animator animation) {
                super.onAnimationPause(animation);
            }

            @Override
            public void onAnimationResume(Animator animation) {
                super.onAnimationResume(animation); }});/ / time
        animator.setDuration(600);
        // Play once
        animator.setRepeatMode(ValueAnimator.RESTART);
        / / difference
        animator.setInterpolator(new OvershootInterpolator());
        animator.start();
    }

Copy the code

The MotionEvent. ACTION_UP in:

            case MotionEvent.ACTION_UP:
                getDistanceTwo(tCenterPointF,tDragPointF);
                // Here to judge
                if(! isOut){// No more than
                    kickBack();
                }
                if(isOut){
                    // Lift the flag
                    isOverandUp = true;
                    // Place the explosion image in the center of the origin
                    explodeImage.setX(event.getX() - tDragRadius);
                    explodeImage.setY(event.getY() - tDragRadius);
                    // If the center circle and drag circle are larger than the drag distance, play explosion
                    if(getDistanceTwo(tCenterPointF,tDragPointF) > maxDistance){
                        showExplodeImage();
                    }
                    // If the distance between the drag circle and the center circle exceeds the drag distance and then moves the drag circle to a distance greater than 30, it will still explode
                    if(getDistanceTwo(tCenterPointF,tDragPointF) >=30){
                        showExplodeImage();
                    }

                }
                postInvalidate();
                break;
Copy the code

In dispatchView beyond the drag distance to less than the recovery center circle distance logic:

        if(isOut){
            // If the distance between the drag circle and the center circle is less than 30, the center circle is restored
            if(getDistanceTwo(tCenterPointF,tDragPointF) < 30 && isOverandUp){
                canvas.drawCircle(tCenterPointF.x, tCenterPointF.y, tCenterRadius, rPaint);
                isOut = false;
                isOverandUp = false; }}// Draw a drag circle once the given drag distance is exceeded
        if(! isOverStep){// If out and up
            if(!isOverandUp && isOut){
                canvas.drawCircle(tDragPointF.x,tDragPointF.y,tDragRadius,rPaint);
            }

        }
Copy the code

The renderings are as follows:

Add to ListView

1. Add to WindowManager

The above effect is far from enough, how to like QQ, in ListView or Recycleview small dots can be free in the screen drag it? Since a view can only be drawn within its parent control, it can only be moved within its own list, so how can you drag and drop in full screen? Custom drag view changes from inheriting ViewGroup to inheriting View only with the help of WindowManager when the dot to be dragged is added to WindowManager and touch listener is set:

   public class BetterRedPointView extends View{ 

    / / WindowManager object
    private WindowManager windowManager;
    // Drag the width of the view
    private int dragViewWidth;
    // Drag the height of the view
    private int dragViewHeight;
    //WindowManager layout parameters
    private WindowManager.LayoutParams params;
    // Status monitoring
    private DragViewStatusListener dragViewStatusListener;
    // Layout parameters
    params = new WindowManager.LayoutParams();
    // Background transparent
    params.format = PixelFormat.TRANSLUCENT;
    params.height = WindowManager.LayoutParams.WRAP_CONTENT;
    params.width = WindowManager.LayoutParams.WRAP_CONTENT;
    // Base on the upper-left corner
    params.gravity = Gravity.TOP | Gravity.LEFT;
    
}
Copy the code

The constructor passes in the dragged View and WindowManager objects and initializes some parameters:

    // constructor
    public BetterRedPointView(Context context, View dragView, WindowManager windowManager){
        super(context);
        this.context = context;
        this.dragView = dragView;
        this.windowManager = windowManager;
        init();

    }
    
   // Initializes the small circle
    private void init(a) {
        // Manual measurement
        dragView.measure(1.1);
        dragViewWidth = dragView.getMeasuredWidth() / 2;
        dragViewHeight = dragView.getMeasuredHeight() / 2;

        tDragRadius = dragViewHeight;
        // The radius of the center circle
        tCenterRadius = SystemUtil.dp2px(context,8);
        // Maximum drag distance
        maxDistance = SystemUtil.dp2px(context,80);
        // Minimum radius
        minRadius = SystemUtil.dp2px(context,3);

        // Layout parameters
        params = new WindowManager.LayoutParams();
        // Background transparent
        params.format = PixelFormat.TRANSLUCENT;
        params.height = WindowManager.LayoutParams.WRAP_CONTENT;
        params.width = WindowManager.LayoutParams.WRAP_CONTENT;
        // Base on the upper-left corner
        params.gravity = Gravity.TOP | Gravity.LEFT;

    }
Copy the code

2. Update the position of the drag view

In the example above update updateDragPoint drag the circle method, also through the WindowManager. UpdateViewLayout to update the drag and drop the view of the position:

    /** * Update drag center coordinates *@param x
     * @param y
     */
    private void updateDragPoint(float x, float y) {
        tDragPointF.set(x, y);
        changeManagerView(x,y);
        postInvalidate();

    }
    
    /** * Redraw the layout of the drag circle *@paramX x star@paramY, y coordinate */
    private void changeManagerView(float x,float y){
        params.x = (int)(x - dragViewWidth);
        params.y = (int)(y - dragViewHeight - statusBarHeight);
        windowManager.updateViewLayout(dragView,params);
    }
Copy the code

3. Add status listeners

Added drag-and-drop monitor for circle and center circle:

public interface DragViewStatusListener {

    /** * move ** outside the drag range@param dragPoint
     */
    void outDragMove(PointF dragPoint);

    /** * Moves outside the drag range * explosion effect ** /
    void outDragMoveUp(PointF dragPoint);

    /** * moves ** within the drag range@param dragPoint
     */
    void inDragUp(PointF dragPoint);


    /** * Restore center circle ** / when removed from drag range
    void recoverCenterPoint(PointF centerPoint);


}

Copy the code

Implement a listening callback in case of a corresponding trigger, such as an explosion animation:

            case MotionEvent.ACTION_UP:
                getDistanceTwo(tCenterPointF,tDragPointF);
                // Here to judge
                if(! isOut){// No more than
                    kickBack();
                }
                if(isOut){
                    // Lift the flag
                    isOverandUp = true;
                    // Place the explosion image in the center of the origin
                    //explodeImage.setX(event.getX() - tDragRadius);
                    //explodeImage.setY(event.getY() - tDragRadius);
                    // If the center circle and drag circle are larger than the drag distance, play explosion
                    if(getDistanceTwo(tCenterPointF,tDragPointF) > maxDistance){
                        // Listen here for explosion effect
                        if(dragViewStatusListener ! =null){ dragViewStatusListener.outDragMoveUp(tDragPointF); }}// If the distance between the drag circle and the center circle exceeds the drag distance and then moves the drag circle to a distance greater than 30, it will still explode
                    if(getDistanceTwo(tCenterPointF,tDragPointF) >=30) {if(dragViewStatusListener ! =null){ dragViewStatusListener.outDragMoveUp(tDragPointF); }}}Copy the code

4. Build intermediate Bridges

Create a new class, mainly used to assist, mainly used to create drag custom view and create WindowManager object initialization data, and make various cases (drag in the scope, drag out of the scope) logic and explosion logic, the main code is as follows:

public class BetterRedPointViewControl implements View.OnTouchListener.DragViewStatusListener{


    / / context
    private Context context;
    // Drag and drop the layout id
    private int mDragViewId;
    / / WindowManager object
    private WindowManager windowManager;
    // Layout parameters
    private WindowManager.LayoutParams params;

    private BetterRedPointView betterRedPointView;
    // Dragged View
    private View dragView;
    // The View to display
    private View showView;

    // Status bar height
    private int statusHeight;
    // Maximum drag distance
    private float maxDistance = 560;
    // The radius of the center circle
    private float tCenterRadius = 30;
    // The minimum radius of a small circle
    private float minRadius = 8;

    
    // constructor
    public BetterRedPointViewControl(Context context,View showView,int mDragViewId,DragStatusListener dragStatusListener){
        this.context = context;
        this.showView = showView;
        this.mDragViewId = mDragViewId;
        this.dragStatusListener = dragStatusListener;
        // Set the listener to execute its own touch event
        showView.setOnTouchListener(this);
        params = new WindowManager.LayoutParams();
        params.format = PixelFormat.TRANSLUCENT;

    }
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        int action = MotionEventCompat.getActionMasked(event);
        if(action == MotionEvent.ACTION_DOWN){

            ViewParent parent = v.getParent();
            if(parent == null) {return false;
            }

            parent.requestDisallowInterceptTouchEvent(true);
            statusHeight = SystemUtil.getStatusBarHeight(showView);
            showView.setVisibility(View.INVISIBLE);
            dragView = LayoutInflater.from(context).inflate(mDragViewId,null.false);
            // Get the text content
            getText();
            windowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
            // Create a drag circle every time you touch it
            betterRedPointView = new BetterRedPointView(context,dragView,windowManager);
            // Initialize the data
            init();
            // Set the listening callback
            betterRedPointView.setDragViewStatusListener(this);
            // Add to form for display
            windowManager.addView(betterRedPointView,params);
            windowManager.addView(dragView,params);

        }
        betterRedPointView.onTouchEvent(event);
        return true;
    }

    @Override
    public void outDragMove(PointF dragPoint) {}@Override
    public void outDragMoveUp(PointF dragPoint) {
      removeView();
      showExplodeImage(dragPoint);
      dragStatusListener.outScope();
    }

    @Override
    public void inDragUp(PointF dragPoint) {
      removeView();
      dragStatusListener.inScope();
    }

    @Override
    public void recoverCenterPoint(PointF centerPoint) {
      removeView();
      dragStatusListener.inScope();
    }


    /** * Initializes data ** /
    private void init(a){
        // Compute the coordinates of the small circle in the screen
        int[] points = new int[2];
        showView.getLocationInWindow(points);
        int x = points[0] + showView.getWidth() / 2;
        int y = points[1] + showView.getHeight() / 2;
        betterRedPointView.setStatusBarHeight(statusHeight);
// betterRedPointView.setMaxDistance(maxDistance);
// betterRedPointView.setCenterRadius(tCenterRadius);
// betterRedPointView.setMinRadius(minRadius);
        betterRedPointView.setCenterDragPoint(x,y);
    }
    /** * get text content ** /
    private void getText(a){
        if(showView instanceof TextView && dragView instanceofTextView){ ((TextView)dragView).setText((((TextView) showView).getText().toString())); }}/** * remove the view object ** /
    private void removeView(a){
        if(windowManager ! =null&& betterRedPointView.getParent() ! =null&& dragView.getParent() ! =null) { windowManager.removeView(betterRedPointView); windowManager.removeView(dragView); }}}Copy the code

5. Call

Recycleview:

public class RecycleviewAdapter extends RecyclerView.Adapter<ItemHolder> {
    /** * The view position to be deleted is used to update the RV operation */
    ArrayList<Integer> needRemoveList =new ArrayList<Integer>();

    private Context mContext;
    public RecycleviewAdapter(Context mContext){
        this.mContext = mContext;
    }



    @NonNull
    @Override
    public ItemHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
        // Load the layout file
        View itemView = LayoutInflater.from(mContext).inflate(R.layout.item,null);
        return new ItemHolder(itemView);
    }

    @Override
    public void onBindViewHolder(@NonNull ItemHolder itemHolder, final int i) {
        itemHolder.tv_dragView.setText(String.valueOf(i));

        Glide.with(mContext).load(R.mipmap.iv_image).apply(RequestOptions.bitmapTransform(new CircleCrop()).override(200.200)).into(itemHolder.iv_head);
        // Whether to hide the view to drag
        if(needRemoveList.contains(i)){
            itemHolder.tv_dragView.setVisibility(View.GONE);
        }
        else {
            itemHolder.tv_dragView.setVisibility(View.VISIBLE);
            itemHolder.tv_dragView.setText(String.valueOf(i));
        }
        // One is a drag view and the other is a drag view layout
        new BetterRedPointViewControl(mContext, itemHolder.tv_dragView, R.layout.includeview, new BetterRedPointViewControl.DragStatusListener() {
            /** ** within the range ** /
            @Override
            public void inScope(a) {
                notifyDataSetChanged();
            }

            /** ** out of range ** /
            @Override
            public void outScope(a) { needRemoveList.add(i); notifyDataSetChanged(); }}); }@Override
    public int getItemCount(a) {
        return 100; }}Copy the code

6. End result

The renderings are as follows:

5. Project examples

Github address: github.com/KnightAndro…