Android windowmanger 添加view 可自由拖动

windowmanger addview 权限问题

要先获取权限,

package com.hrg.gys.rebot.service;

import android.app.Activity;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.net.Uri;
import android.os.Build;
import android.os.IBinder;
import android.provider.Settings;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;

import androidx.annotation.Nullable;

import com.hrg.hefei.R;
import com.xin.common.utils.LogUtils;

public class BaseMoveWindowService extends Service {
    private View rootView;
    private WindowManager windowManager;

    @Override
    public void onCreate() {
        super.onCreate();
        windowManager = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
        addView();
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    /**
     * 是否已获得权限
     * @param context
     * @return
     */

    public static boolean canDrawOverlays(Context context) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            return true;
        } else {
            return Settings.canDrawOverlays(context);
        }
    }

    /**
     * 获得权限
     * @param activity
     * @param requestCode
     */
    public static void requestOVERLAYPermission(Activity activity, int requestCode) {
        Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
        intent.setData(Uri.parse("package:" + activity.getPackageName()));
        activity.startActivityForResult(intent, requestCode);
    }

    public void addView() {
        try {
            rootView = LayoutInflater.from(this).inflate(R.layout.service_move_view, null);
            WindowManager.LayoutParams params = new WindowManager.LayoutParams();
            int flag = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
                    WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH |
                    WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
            params.type = WindowManager.LayoutParams.TYPE_PHONE;
            params.flags = flag;
            params.format = PixelFormat.RGBA_8888; /*透明背景,否则会黑色*/
            params.width = WindowManager.LayoutParams.WRAP_CONTENT;
            params.height = WindowManager.LayoutParams.WRAP_CONTENT;
            params.gravity = Gravity.LEFT | Gravity.TOP;
            windowManager.addView(rootView, params);
        } catch (Exception e) {
            e.printStackTrace();
        }


    }

    private void log(String s) {
        LogUtils.log("BaseMoveWindowService", s);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        try {
            windowManager.removeView(rootView);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


}



可拖动组件

package com.xin.view;

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Rect;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.animation.LinearInterpolator;
import android.widget.LinearLayout;

import androidx.annotation.Nullable;

import com.xin.common.utils.LogUtils;

/**
 * 作为添加到WindowManager的根布局
 */
public class AttachLayoutWindow extends LinearLayout {

    public static final int DELAY_MILLIS_GO_EDGE = 3000;
    private final String TAG = "AttachButton";
    protected float mLastRawX;
    protected float mLastRawY;
    protected boolean isDrug = false;
    protected int mRootMeasuredWidth = 0;
    protected int mRootMeasuredHeight = 0;
    protected int mRootTopY = 0;
    protected int customAttachDirect; /*-1不吸附 0所有的边 1左 2上 3右 4下 5左右 6上下 */
    protected boolean customIsDrag;
    protected boolean touchIsTargetView = true;
    protected View targetView;
    private WindowManager windowManager;
    private Handler handler;
    private GoEdgeRunnable goEdge;

    public AttachLayoutWindow(Context context) {
        this(context, null, 0);
    }

    public AttachLayoutWindow(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public AttachLayoutWindow(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }

    private void initView() {
        windowManager = (WindowManager) this.getContext().getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
        int statusBarHeight = getStatusBarHeight();
        mRootMeasuredHeight = displayMetrics.heightPixels - statusBarHeight;
        mRootMeasuredWidth = displayMetrics.widthPixels;
        mRootTopY = statusBarHeight;
        customAttachDirect = 5;
        customIsDrag = true;
        handler = new android.os.Handler();
        goEdge = new GoEdgeRunnable();

    }

    private int getStatusBarHeight() {
        int statusBarHeight = (int) Math.ceil(25 * getResources().getDisplayMetrics().density);
        return statusBarHeight;
    }

    private void log(String s) {
        LogUtils.log("AttachLayoutWindow", s);
    }

    protected void doTouch(MotionEvent ev) {
        log("-----------------------------------");
        log("doTouch() called with: ev = [" + ev + "]");
        if (customIsDrag) {
            //当前手指的坐标
            float mRawX = ev.getRawX();
            float mRawY = ev.getRawY();
            switch (ev.getAction()) {
                case MotionEvent.ACTION_DOWN://手指按下
                    handler.removeCallbacks(goEdge);
                    isDrug = false;
                    //记录按下的位置
                    mLastRawX = mRawX;
                    mLastRawY = mRawY;
                    break;
                case MotionEvent.ACTION_MOVE://手指滑动
                    if (mRawX >= 0 && mRawX <= mRootMeasuredWidth && mRawY >= mRootTopY && mRawY <= (mRootMeasuredHeight + mRootTopY)) {
                        //手指X轴滑动距离
                        float differenceValueX = mRawX - mLastRawX;
                        //手指Y轴滑动距离
                        float differenceValueY = mRawY - mLastRawY;
                        log("doTouch()  move called with: differenceValueX = [" + differenceValueX + "]");
                        log("doTouch()  move called with: differenceValueY = [" + differenceValueY + "]");
                        //判断是否为拖动操作
                        if (!isDrug) {
                            if (Math.sqrt(differenceValueX * differenceValueX + differenceValueY * differenceValueY) < 2) {
                                isDrug = false;
                            } else {
                                isDrug = true;
                            }
                        }
                        WindowManager.LayoutParams layoutParams = (WindowManager.LayoutParams) getLayoutParams();
                        //获取手指按下的距离与控件本身X轴的距离
                        float ownX = layoutParams.x;
                        //获取手指按下的距离与控件本身Y轴的距离
                        float ownY = layoutParams.y;
                        log("doTouch()  move called with: ownX = [" + ownX + "]");
                        log("doTouch()  move called with: ownY = [" + ownY + "]");
                        //理论中X轴拖动的距离
                        float endX = ownX + differenceValueX;
                        //理论中Y轴拖动的距离
                        float endY = ownY + differenceValueY;
                        log("doTouch()  move called with: endX = [" + endX + "]");
                        log("doTouch()  move called with: endX = [" + endX + "]");

                        log("doTouch()  move called with: getWidth() = [" + getWidth() + "]");
                        log("doTouch()  move called with: getHeight() = [" + getHeight() + "]");
                        //X轴可以拖动的最大距离
                        float maxX = mRootMeasuredWidth - getWidth();
                        //Y轴可以拖动的最大距离
                        float maxY = mRootMeasuredHeight - getHeight();
                        log("doTouch()  move called with: maxX = [" + maxX + "]");
                        log("doTouch()  move called with: maxY = [" + maxY + "]");
                        //X轴边界限制
                        endX = endX < 0 ? 0 : endX > maxX ? maxX : endX;
                        //Y轴边界限制
                        endY = endY < 0 ? 0 : endY > maxY ? maxY : endY;
                        //开始移动
                        layoutParams.x = (int) endX;
                        layoutParams.y = (int) endY;
                        windowManager.updateViewLayout(this, layoutParams);
                        //记录位置
                        mLastRawX = mRawX;
                        mLastRawY = mRawY;
                    }

                    break;
                case MotionEvent.ACTION_UP://手指离开
                    handler.postDelayed(goEdge, DELAY_MILLIS_GO_EDGE);
                    //根据自定义属性判断是否需要贴边
                    if (customAttachDirect >= 0 && isDrug && false) {
                        float[] edgePosition = getEdgePosition();
                        goEdge(edgePosition);
                    }
                    break;
            }
        }

    }

    /**
     * @param attachDirect 0所有的边 1左 2上 3右 4下 5左右 6上下
     */
    public void setCustomAttachDirect(int attachDirect) {
        this.customAttachDirect = customAttachDirect;
    }

    /**
     * 返回最终的边距值
     *
     * @return
     */
    private float[] getEdgePosition() {
        //判断是否为点击事件 0所有的边 1左 2上 3右 4下 5左右 6上下
        float centerX = mRootMeasuredWidth / 2;
        float centerY = mRootMeasuredHeight / 2;
        WindowManager.LayoutParams layoutParams_up = (WindowManager.LayoutParams) getLayoutParams();
        int startX = layoutParams_up.x;
        int startY = layoutParams_up.y;
        float x = -1, y = -1;
        if (customAttachDirect == 1) { /*左*/
            x = 0;
            y = -1;
        } else if (customAttachDirect == 2) { /*上*/
            x = -1;
            y = 0;
        } else if (customAttachDirect == 3) { /*右*/
            x = mRootMeasuredWidth - getWidth();
            y = -1;
        } else if (customAttachDirect == 4) {
            x = -1;
            y = mRootMeasuredWidth - getHeight();
        } else if (customAttachDirect == 5) { /*左右*/
            x = startX <= centerX ? 0 : (mRootMeasuredWidth - getWidth());
            y = startY;
        } else if (customAttachDirect == 6) { /*上下*/
            x = startX;
            y = startY <= centerY ? 0 : (mRootMeasuredHeight - getHeight());
        } else if (customAttachDirect == 0) { /*距那个边近去那个*/
            // TODO: 2020/5/28 0028
        }
        return new float[]{x, y};

    }

    private void goEdge(float[] edgePosition) {
        if (edgePosition == null && edgePosition.length < 2) {
            return;
        }
        final float endX_ = edgePosition[0];
        final float endY_ = edgePosition[1];
        WindowManager.LayoutParams layoutParams_up = (WindowManager.LayoutParams) getLayoutParams();
        int startX = layoutParams_up.x, startY = layoutParams_up.y;
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
        valueAnimator.setDuration(1000);
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.addUpdateListener(animation -> {
            float value = (float) animation.getAnimatedValue();
            /*1->0*/
            float _x = (endX_ - startX) * value + startX;
            float _y = (endY_ - startY) * value + startY;
            layoutParams_up.x = (int) _x;
            layoutParams_up.y = (int) _y;
            windowManager.updateViewLayout(this, layoutParams_up);
        });
        valueAnimator.start();
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        log("onAttachedToWindow() called");
        /*右上角*/
        WindowManager.LayoutParams layoutParams_up = (WindowManager.LayoutParams) getLayoutParams();
        if (layoutParams_up != null) {
            if (getMeasuredWidth() == 0) {
                measure(0, 0);
            }
            layoutParams_up.x = mRootMeasuredWidth - getMeasuredWidth();
            layoutParams_up.y = (int) (30 * getResources().getDisplayMetrics().density);
            windowManager.updateViewLayout(this, layoutParams_up);
        }
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        log("onFinishInflate() called");
        /*(WindowManager.LayoutParams) getLayoutParams() 此时获取到的是 null*/
    }


    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        log("dispatchTouchEvent() called with: event = [" + event + "]");
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                touchIsTargetView = true;
        }
        if (targetView != null) {
            // /*判断是否有目标view,若有不在目标view内不执行*/
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN://手指按下
                    Rect rect = new Rect();
                    targetView.getHitRect(rect);
                    if (!rect.contains((int) event.getX(), (int) event.getY())) {
                        touchIsTargetView = false;
                    }
            }
        }
        if (touchIsTargetView) {
            doTouch(event);
        }
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        log("onTouchEvent() called with: ev = [" + ev + "]");
        //判断是否需要滑动
//        doTouch(ev); /*不能放在这,放在这子view收不到事件了*/
        //是否拦截事件
        if (isDrug) {
            return true;
        }
        return super.onTouchEvent(ev);
    }

    /**
     * 自动贴边
     */
    private class GoEdgeRunnable implements Runnable {
        @Override
        public void run() {
            float[] edgePosition = getEdgePosition();
            goEdge(edgePosition);
        }
    }
}


©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。