FloatingMagnetView.java 6.54 KB
package com.wd.common.floatingview;

import android.content.Context;
import android.content.res.Configuration;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.FrameLayout;

import com.wd.fastcoding.base.R;


/**
 * @Author :张泽昊
 * @Email :1064771680@qq.com
 * @Date :$ $
 * @Description :
 */
public class FloatingMagnetView extends FrameLayout {

    public static final int MARGIN_EDGE = 13;
    private float mOriginalRawX;
    private float mOriginalRawY;
    private float mOriginalX;
    private float mOriginalY;
    protected MoveAnimator mMoveAnimator;
    protected int mScreenWidth;
    private int mScreenHeight;
    private boolean isNearestLeft = true;
    private float mPortraitY;
    private int scaledTouchSlop;

    /**
     * 是否展开
     */
    private boolean isExpand = true;

    public FloatingMagnetView(Context context) {
        this(context, null);
    }

    public FloatingMagnetView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public FloatingMagnetView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        scaledTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
        mMoveAnimator = new MoveAnimator();
        setClickable(false);
//        updateSize();
    }

    private float rawX;
    private float rawY;
    private boolean intercept;

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        // 禁止拖动
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                intercept = false;
                rawX = event.getRawX();
                rawY = event.getRawY();
                changeOriginalTouchParams(event);
                updateSize();
                mMoveAnimator.stop();
                break;
            case MotionEvent.ACTION_MOVE:
                if (Math.abs(event.getRawX() - rawX) >= scaledTouchSlop ||
                        Math.abs(event.getRawY() - rawY) >= scaledTouchSlop) {
                    intercept = true;
                }
                updateViewPosition(event);
                break;
            case MotionEvent.ACTION_UP:
                clearPortraitY();
                moveToEdge();
                return super.onInterceptTouchEvent(event) || intercept;
        }
        return super.onInterceptTouchEvent(event);
    }

    private void updateViewPosition(MotionEvent event) {
        /*if(isExpand){
            //展开不让拖动
            return;
        }*/
//        setX(mOriginalX + event.getRawX() - mOriginalRawX);
        //只上下拖动,X轴位置不变
        setX(mOriginalX);
        // 限制不可超出屏幕高度
        float desY = mOriginalY + event.getRawY() - mOriginalRawY;
        float topY = getResources().getDimension(R.dimen.rmrb_dp45);
        float bottomY = getResources().getDimension(R.dimen.rmrb_dp45);
        if (desY < topY) {
            desY = topY;
        }
        if (desY > mScreenHeight - getHeight() - bottomY) {
            desY = mScreenHeight - getHeight() - bottomY;
        }
        setY(desY);
    }

    private void changeOriginalTouchParams(MotionEvent event) {
        mOriginalX = getX();
        mOriginalY = getY();
        mOriginalRawX = event.getRawX();
        mOriginalRawY = event.getRawY();
    }

    protected void updateSize() {
        ViewGroup viewGroup = (ViewGroup) getParent();
        if (viewGroup != null) {
            mScreenWidth = viewGroup.getWidth() - getWidth();
            mScreenHeight = viewGroup.getHeight();
        }
//        mScreenWidth = (SystemUtils.getScreenWidth(getContext()) - this.getWidth());
//        mScreenHeight = SystemUtils.getScreenHeight(getContext());
    }

    public void moveToEdge() {
        moveToEdge(isNearestLeft(), false);
    }

    public void moveToEdge(boolean isLeft, boolean isLandscape) {
        float moveDistance = isLeft ? MARGIN_EDGE : mScreenWidth - MARGIN_EDGE;
        float y = getY();
        if (!isLandscape && mPortraitY != 0) {
            y = mPortraitY;
            clearPortraitY();
        }
        mMoveAnimator.start(moveDistance, Math.min(Math.max(0, y), mScreenHeight - getHeight()));
    }

    private void clearPortraitY() {
        mPortraitY = 0;
        rawY = 0;
        mOriginalY = 0;
        mOriginalRawY = 0;
    }

    protected boolean isNearestLeft() {
//        int middle = mScreenWidth / 2;
//        isNearestLeft = getX() < middle;
//        return isNearestLeft;
        return true;
    }

    protected class MoveAnimator implements Runnable {

        private final Handler handler = new Handler(Looper.getMainLooper());
        private float destinationX;
        private float destinationY;
        private long startingTime;

        void start(float x, float y) {
            this.destinationX = x;
            this.destinationY = y;
            startingTime = System.currentTimeMillis();
            handler.post(this);
        }

        @Override
        public void run() {
            if (getRootView() == null || getRootView().getParent() == null) {
                return;
            }
            float progress = Math.min(1, (System.currentTimeMillis() - startingTime) / 400f);
            float deltaX = (destinationX - getX()) * progress;
            float deltaY = (destinationY - getY()) * progress;
            move(deltaX, deltaY);
            if (progress < 1) {
                handler.post(this);
            }
        }

        private void stop() {
            handler.removeCallbacks(this);
        }
    }

    private void move(float deltaX, float deltaY) {
        setX(getX() + deltaX);
        setY(getY() + deltaY);
    }

    @Override
    protected void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        if (getParent() != null) {
            final boolean isLandscape = newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE;
            markPortraitY(isLandscape);
            ((ViewGroup) getParent()).post(() -> {
                updateSize();
                moveToEdge(isNearestLeft, isLandscape);
            });
        }
    }

    private void markPortraitY(boolean isLandscape) {
        if (isLandscape) {
            mPortraitY = getY();
        }
    }

    public boolean isExpand() {
        return isExpand;
    }

    public void setExpand(boolean expand) {
        isExpand = expand;
    }
}