美文网首页自定义控件
Android(java)应用内部无需权限悬浮可拖动的View

Android(java)应用内部无需权限悬浮可拖动的View

作者: 想看烟花么 | 来源:发表于2022-07-06 18:05 被阅读0次

    #######IFloatingView

    package com.xxx.xxxxx.view.floatingview;
    
    import android.view.View;
    import android.view.ViewGroup;
    
    import androidx.annotation.LayoutRes;
    
    import org.jetbrains.annotations.NotNull;
    
    public interface IFloatingView {
        ChatFloatingView remove();
    
        ChatFloatingView show(ViewGroup container, @LayoutRes int layoutId, @NotNull ViewGroup.LayoutParams layoutParams);
    
        ChatFloatingView show(ViewGroup container, @LayoutRes int layoutId, int left, int top, int right, int bottom, FloatingGravity gravity);
    
        View getContentView();
    
        ChatFloatingView setListener(View.OnClickListener viewListener);
    
    }
    

    #######FloatingGravity

    package com.xxx.xxxx.view.floatingview;
    public enum FloatingGravity {
        LEFT_TOP,
        LEFT_BOTTOM,
        RIGHT_TOP,
        RIGHT_BOTTOM
    }
    

    #######ChatFloatingView

    package com.xxx.xxxx.view.floatingview;
    
    import android.os.Handler;
    import android.os.Looper;
    import android.view.Gravity;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.FrameLayout;
    import android.widget.RelativeLayout;
    
    import androidx.annotation.LayoutRes;
    import androidx.annotation.NonNull;
    import androidx.constraintlayout.widget.ConstraintLayout;
    import androidx.core.view.ViewCompat;
    
    import org.jetbrains.annotations.NotNull;
    import org.jetbrains.annotations.Nullable;
    
    import java.lang.ref.WeakReference;
    
    public class ChatFloatingView implements IFloatingView {
        private static volatile ChatFloatingView mInstance;
        private WeakReference<ViewGroup> mContainer;
        private WeakReference<View> mFloatingView = null;
    
        private ChatFloatingView() {
        }
    
        public static ChatFloatingView get() {
            if (mInstance == null) {
                synchronized (ChatFloatingView.class) {
                    if (mInstance == null) {
                        mInstance = new ChatFloatingView();
                    }
                }
            }
            return mInstance;
        }
    
        @Override
        public ChatFloatingView remove() {
            new Handler(Looper.getMainLooper()).post(() -> {
                if (getFloatingView() == null) {
                    return;
                }
                if (ViewCompat.isAttachedToWindow(getFloatingView()) && getContainer() != null) {
                    getContainer().removeView(getFloatingView());
                }
            });
            return this;
        }
    
        @Override
        public ChatFloatingView show(ViewGroup container, @LayoutRes int layoutId, @NonNull ViewGroup.LayoutParams layoutParams) {
            add2ViewGroup(container, layoutId, layoutParams);
            return this;
        }
    
        /**
         * @param container | only support [FrameLayout,ConstraintLayout,RelativeLayout]
         * @return this
         */
        @Override
        public ChatFloatingView show(ViewGroup container, @LayoutRes int layoutId, int left, int top, int right, int bottom, FloatingGravity gravity) {
            ViewGroup.MarginLayoutParams marginLayoutParams = getMarginLayoutParams(container, gravity);
            marginLayoutParams.setMargins(left, top, right, bottom);
            add2ViewGroup(container, layoutId, marginLayoutParams);
            return this;
        }
    
        private ViewGroup.MarginLayoutParams getMarginLayoutParams(ViewGroup container, FloatingGravity gravity) {
            if (container instanceof FrameLayout) {
                FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
                        ViewGroup.LayoutParams.WRAP_CONTENT,
                        ViewGroup.LayoutParams.WRAP_CONTENT);
                layoutParams.gravity = getFrameLayoutGravity(gravity);
                return layoutParams;
            } else if (container instanceof ConstraintLayout) {
                ConstraintLayout.LayoutParams layoutParams = new ConstraintLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                        ViewGroup.LayoutParams.WRAP_CONTENT);
                if (gravity == FloatingGravity.LEFT_TOP) {
                    layoutParams.startToStart = ConstraintLayout.LayoutParams.PARENT_ID;
                    layoutParams.topToTop = ConstraintLayout.LayoutParams.PARENT_ID;
                } else if (gravity == FloatingGravity.LEFT_BOTTOM) {
                    layoutParams.startToStart = ConstraintLayout.LayoutParams.PARENT_ID;
                    layoutParams.bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID;
                } else if (gravity == FloatingGravity.RIGHT_TOP) {
                    layoutParams.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID;
                    layoutParams.topToTop = ConstraintLayout.LayoutParams.PARENT_ID;
                } else {
                    layoutParams.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID;
                    layoutParams.bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID;
                }
                return layoutParams;
            } else if (container instanceof RelativeLayout) {
                RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                        ViewGroup.LayoutParams.WRAP_CONTENT);
                if (gravity == FloatingGravity.LEFT_TOP) {
                    layoutParams.addRule(RelativeLayout.ALIGN_PARENT_START);
                    layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
                } else if (gravity == FloatingGravity.LEFT_BOTTOM) {
                    layoutParams.addRule(RelativeLayout.ALIGN_PARENT_START);
                    layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
                } else if (gravity == FloatingGravity.RIGHT_TOP) {
                    layoutParams.addRule(RelativeLayout.ALIGN_PARENT_END);
                    layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
                } else {
                    layoutParams.addRule(RelativeLayout.ALIGN_PARENT_END);
                    layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
                }
                return layoutParams;
            } else {
                FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
                        ViewGroup.LayoutParams.WRAP_CONTENT,
                        ViewGroup.LayoutParams.WRAP_CONTENT);
                layoutParams.gravity = getFrameLayoutGravity(gravity);
                return layoutParams;
            }
        }
    
        private int getFrameLayoutGravity(FloatingGravity gravity) {
            int newGravity;
            if (gravity == FloatingGravity.LEFT_TOP) {
                newGravity = Gravity.START | Gravity.TOP;
            } else if (gravity == FloatingGravity.LEFT_BOTTOM) {
                newGravity = Gravity.START | Gravity.BOTTOM;
            } else if (gravity == FloatingGravity.RIGHT_TOP) {
                newGravity = Gravity.END | Gravity.TOP;
            } else {
                newGravity = Gravity.END | Gravity.BOTTOM;
            }
            return newGravity;
        }
    
        private void add2ViewGroup(ViewGroup container, @LayoutRes int layoutId, @NotNull ViewGroup.LayoutParams layoutParams) {
            if (container == null) {
                return;
            }
            if (getFloatingView() == null) {
                LayoutInflater factory = LayoutInflater.from(container.getContext());
                mFloatingView = new WeakReference<>(factory.inflate(layoutId, null));
                mContainer = new WeakReference<>(container);
                getFloatingView().setLayoutParams(layoutParams);
                container.addView(getFloatingView());
                return;
            }
            ViewGroup parentView = (ViewGroup) getFloatingView().getParent();
            if (parentView != container) {
                mContainer = new WeakReference<>(container);
            } else {
                container.removeView(getFloatingView());
            }
            getFloatingView().setLayoutParams(layoutParams);
            container.addView(getFloatingView());
        }
    
        @Override
        public View getContentView() {
            return getFloatingView();
        }
    
        @Override
        public ChatFloatingView setListener(View.OnClickListener viewListener) {
            return this;
        }
    
        @Nullable
        private ViewGroup getContainer() {
            if (mContainer == null) {
                return null;
            }
            return mContainer.get();
        }
    
        @Nullable
        private View getFloatingView() {
            if (mFloatingView == null) {
                return null;
            }
            return mFloatingView.get();
        }
    
    }
    

    #######FloatingViewListener

    package com.xxx.xxxxx.view.floatingview;
    
    public interface FloatingViewListener {
        void onRemove(FloatingMagnetLayout floatingLayout);
    
        void onClick(FloatingMagnetLayout floatingLayout);
    }
    
    

    #######FloatingMagnetLayout

    package com.xxx.xxxx.view.floatingview;
    
    import android.annotation.SuppressLint;
    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.ViewGroup;
    
    import androidx.constraintlayout.widget.ConstraintLayout;
    
    import com.greendotcorp.chatsdk.utils.AppUtils;
    
    public class FloatingMagnetLayout extends ConstraintLayout {
    
        public static final int MARGIN_EDGE = 13;
        private float mOriginalRawX;
        private float mOriginalRawY;
        private float mOriginalX;
        private float mOriginalY;
        private FloatingViewListener mFloatingViewListener;
        private static final int TOUCH_TIME_THRESHOLD = 150;
        private long mLastTouchDownTime;
        protected MoveAnimator mMoveAnimator;
        protected int mScreenWidth;
        private int mScreenHeight;
        private int mStatusBarHeight;
        private boolean isNearestLeft = true;
        private float mPortraitY;
    
        public void setFloatingViewListener(FloatingViewListener magnetViewListener) {
            this.mFloatingViewListener = magnetViewListener;
        }
    
        public FloatingMagnetLayout(Context context) {
            this(context, null);
        }
    
        public FloatingMagnetLayout(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public FloatingMagnetLayout(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        private void init() {
            mMoveAnimator = new MoveAnimator();
    //        mStatusBarHeight = AppUtils.getStatusBarHeight(getContext());
            mStatusBarHeight = 0;
            setClickable(true);
    //        updateSize();
        }
    
        @SuppressLint("ClickableViewAccessibility")
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if (event == null) {
                return false;
            }
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    changeOriginalTouchParams(event);
                    updateSize();
                    mMoveAnimator.stop();
                    break;
                case MotionEvent.ACTION_MOVE:
                    updateViewPosition(event);
                    break;
                case MotionEvent.ACTION_UP:
                    clearPortraitY();
                    moveToEdge();
                    if (isOnClickEvent()) {
                        dealClickEvent();
                    }
                    break;
            }
            return true;
        }
    
        protected void dealClickEvent() {
            if (mFloatingViewListener != null) {
                mFloatingViewListener.onClick(this);
            }
        }
    
        protected boolean isOnClickEvent() {
            return System.currentTimeMillis() - mLastTouchDownTime < TOUCH_TIME_THRESHOLD;
        }
    
        private void updateViewPosition(MotionEvent event) {
            setX(mOriginalX + event.getRawX() - mOriginalRawX);
            // 限制不可超出屏幕高度
            float desY = mOriginalY + event.getRawY() - mOriginalRawY;
            if (desY < mStatusBarHeight) {
                desY = mStatusBarHeight;
            }
            if (desY > mScreenHeight - getHeight()) {
                desY = mScreenHeight - getHeight();
            }
            setY(desY);
        }
    
        private void changeOriginalTouchParams(MotionEvent event) {
            mOriginalX = getX();
            mOriginalY = getY();
            mOriginalRawX = event.getRawX();
            mOriginalRawY = event.getRawY();
            mLastTouchDownTime = System.currentTimeMillis();
        }
    
        protected void updateSize() {
            ViewGroup viewGroup = (ViewGroup) getParent();
            if (viewGroup != null) {
                mScreenWidth = viewGroup.getWidth() - getWidth();
                mScreenHeight = viewGroup.getHeight();
            }
        }
    
        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;
        }
    
        protected boolean isNearestLeft() {
            int middle = mScreenWidth / 2;
            isNearestLeft = getX() < middle;
            return isNearestLeft;
        }
    
        public void onRemove() {
            if (mFloatingViewListener != null) {
                mFloatingViewListener.onRemove(this);
            }
        }
    
        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();
            }
        }
    }
    
    

    -----------------------------End-----------------------------

    我也是有底线的,感谢您的耐心阅读,欢迎支持与点赞。

    相关文章

      网友评论

        本文标题:Android(java)应用内部无需权限悬浮可拖动的View

        本文链接:https://www.haomeiwen.com/subject/ykvybrtx.html