Some time ago, there was a need to do an activity, and the entrance was a floating and dragging button. If it’s just a draggable View, you can find a lot of custom draggable views in articles, and there’s also a custom draggable ImageView in projects. But now the requirement is that this button can be closed, add a close button, that can only use ViewGroup, and finally I customized a LinearLayout

The first problem is to keep the active images and the close button in the LinearLayout so that the click event is not triggered when moving. According to the event distribution mechanism, we need to check whether to intercept the event in the onInterceptTouchEvent() method. If it is in motionEvent.action_move, we need to intercept the event

@Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        int action = ev.getAction() & MotionEvent.ACTION_MASK;
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                mLastTouchX = ev.getX();
                mLastTouchY= ev.getY();
                return false;
            caseMotionEvent.ACTION_MOVE: mMoveX = ev.getX(); mMoveY = ev.getY(); // Moving a small distance is also considered a clickif(Math. Abs (mMoveX - mLastTouchX) < 5 | | math.h abs (mMoveY - mLastTouchY) < 5) / / no event interceptorreturn false;
                else
                    return true;
        }
        return false;
    }
Copy the code

The second problem is to choose which methods to use to move the custom LinearLayout control while dragging it. Layout (L, T, R,b) is used to rearrange the image while dragging. However, there is a problem with pages that have banners or pull-down refresh or pull-up load. OnLayout (Boolean changed, int L, int T, int R, int B) ¶

I wanted to use the layout() method in the onLayout() method and pass the position of the moved argument, but there is a problem. I can open layout() and this method will still go onLayout(), so it will cause an endless loop. This is a dead end. Later, I still want to judge onLayout(). If it is lifted after dragging, I will no longer go to layout(). I don’t know exactly why.

It is no longer possible to use layout() to rearrange the onLayout() method. When fixing this bug, I pulled my colleague to look at the problem and he said that we can use other ways to move the layout. Tried several methods with offsetTopAndBottom/offsetLeftAndRight finally perfect solution.

The last problem, is after dragging, to carry out adsorption on the left and right sides, and control to limit the screen do not cross the boundary, this is still relatively easy to solve. To solve this problem, remember that getTop(), getRight(), getBottom(), and getLeft() are the distance from the top of the control to the top of the screen, the distance from the right edge of the control to the left edge of the screen, the distance from the bottom of the control to the top of the screen, and the distance from the left edge of the control to the left edge of the screen.

If it is over 1/2 of the screen, it is adsorbed to the right side of the screen. Then move the distance from the right edge of the control to the right edge of the screen, which is the screen width minus getRight(). Similarly, adsorbed to the left side is the same. I solved the problem by dragging and lifting the gesture and sliding it back. Or you can restrict controls to the screen directly during move. The complete code is as follows

package com.dudou.demo;

import android.content.Context;
import android.support.annotation.Nullable;
import android.support.v4.view.MotionEventCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.LinearLayout;

public class DragLinearLayout extends LinearLayout {

    private float mLastTouchX = 0;
    private float mLastTouchY= 0;

    private float mMoveX = 0;
    private float mMoveY = 0;

    private float mLeft;
    private float mTop;
    private float mRight;
    private float mBottom;

    private int mDx;
    private int mDy;
    private boolean isLeft = false;
    boolean moveRight = false;
    boolean moveLeft = false; / / screen width private static final ints screenWidth = ScreenUtil. GetScreenWidth (); / / screen height private static final ints screenHeight = ScreenUtil. GetScreenHeight () public TouchLinearLayout (Context Context) { super(context); } public TouchLinearLayout(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public TouchLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { int action = ev.getAction() & MotionEvent.ACTION_MASK; switch (action) {case MotionEvent.ACTION_DOWN:
                mLastTouchX = ev.getX();
                mLastTouchY= ev.getY();
                return false;
            caseMotionEvent.ACTION_MOVE: mMoveX = ev.getX(); mMoveY = ev.getY(); // Moving a small distance is also considered a clickif(Math. Abs (mMoveX - mLastTouchX) < 5 | | math.h abs (mMoveY - mLastTouchY) < 5) / / no event interceptorreturn false;
                else
                    return true;
        }
        return false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        final int action = MotionEventCompat.getActionMasked(ev);

        switch (action) {
            case MotionEvent.ACTION_DOWN: {
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                moveRight = false;
                moveLeft = false;
                final float x = ev.getX();
                final float y = ev.getY();
                final float dx = x - mLastTouchX;
                final float dy = y - mLastTouchY;
                mLeft = getLeft() + dx;
                mTop = getTop() + dy;
                mRight = getRight() + dx;
                mBottom = getBottom() + dy;
                if(mLeft < 0){
                    moveLeft = true;
                    mLeft = 0;
                    mRight = mLeft + getWidth();
                }
                if(mRight > screenWidth){
                    moveRight = true;
                    mRight = screenWidth;
                    mLeft = mRight - getWidth();
                }
                if(mTop < 0){
                    mTop = 0;
                    mBottom = mTop + getHeight();
                }
                if(mBottom > screenHeight){
                    mBottom = screenHeight;
                    mTop = mBottom - getHeight();
                }
                mDx += dx;
                mDy += dy;
                offsetLeftAndRight((int)dx);
                offsetTopAndBottom((int)dy);
                if(moveLeft){
                    offsetLeftAndRight(-getLeft());
                }
                if(moveRight){
                    offsetLeftAndRight(screenWidth-getRight());
                }
                break;
            }
            case MotionEvent.ACTION_UP: {
                int upX = (int) ev.getRawX();
                if (upX > (screenWidth / 2)) {
                    isLeft = false;
                    offsetLeftAndRight(screenWidth-getRight());
                    invalidate();
                } else {
                    isLeft = true;
                    offsetLeftAndRight(-getLeft());
                    invalidate();
                }
                if(getTop()<0){
                    mDy += -getTop();
                    offsetTopAndBottom(-getTop());
                }
                if(getBottom()>screenHeight){
                    mDy += screenHeight-getBottom();
                    offsetTopAndBottom(screenHeight-getBottom());
                }
                break;
            }
            case MotionEvent.ACTION_CANCEL: {
                break;
            }
            case MotionEvent.ACTION_POINTER_UP: {
                break; }}return true;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        offsetTopAndBottom(mDy);
        if(isLeft){
            offsetLeftAndRight(-getLeft());
        }else{ offsetLeftAndRight(screenWidth-getRight()); }}}Copy the code