Android service中悬浮窗兼容及拖动点击长按事件

写文章的前言,我还不知道以什么开始所以省略吧,你知道什么是悬浮窗就好了。

悬浮窗主要是由WindowManager管理实现,我们平常的需求可以在app内部实现弹出窗,也可以通过service里面弹出悬浮窗,这样子就可以在任意位置拖动处理悬浮窗。

我们主要注意的是,在Android6.0以后需要权限(用户主动赋予),Android8.0修改了权限API这两点。好了,我知道字太多你也不想看,我也写不出。直接看代码。。。。

WindowManager mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);

上面是获取windowManager对象,我们是通过这个来添加你想展示的布局,他提供以下几个方法

 public void addView(View view, ViewGroup.LayoutParams params);
 public void updateViewLayout(View view, ViewGroup.LayoutParams params);
 public void removeView(View view);

应该看方法名都知道他们分别是用来干嘛的,我们添加view需要第二个参数ViewGroup.LayoutParams params来定义样式了类型,在Android不同版本,悬浮窗的类型也不同,这个需要特别注意,要不然会报错崩溃。

        wmParams = new WindowManager.LayoutParams();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            wmParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
        } else {
            wmParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
        }

整个悬浮框的代码:

   private static WindowManager mWindowManager;
    private static WindowManager.LayoutParams wmParams;

public void showFloatView(Context context,int layoutId){
        mContext = context;

        if(isShow){
            return;
        }

        mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        wmParams = new WindowManager.LayoutParams();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            wmParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
        } else {
            wmParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
        }


        wmParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
        //悬浮框居中
        wmParams.gravity = Gravity.CENTER;
        wmParams.format = PixelFormat.RGBA_8888;
        //悬浮窗的坐标,以左上角为中心,固定初始x,y值
        wmParams.x = context.getResources().getDisplayMetrics().widthPixels;
        wmParams.y = 0;
        //悬浮窗宽高,可以固定大小
        wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
        //加载布局
        mView = LayoutInflater.from(context).inflate(layoutId, null);

       
        //添加布局到manager
        mWindowManager.addView(mView, wmParams);
        isShow = true;
    }

接下来就是使用,因为我们是使用在Service中,所以需要在AndroidManifest中添加允许弹窗的权限,不能忘了

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<!--悬浮窗的-->
<uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW"/>

在service中调用:

if (Build.VERSION.SDK_INT >= 23) {
                if (!Settings.canDrawOverlays(this)) {//注意需要判断是否允许了权限
                    Intent intent = new              Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                            Uri.parse("package:" + getPackageName()));
                    startActivity(intent);
                }else {
                    showFloatView();//有权限,去弹出悬浮窗这里
                }
            }

上面是需要判断canDrawOverlays,如下图

只有赋予了权限才可以弹出悬浮窗。,给与权限后,我们就可以看到自己的悬浮窗了

这样只是完成了一半,现在我们要让他可以移动,可以长按点击,就需要OnTouchListener这个了

mView.setOnTouchListener(new View.OnTouchListener() {
            float downX = 0;
            float downY = 0;
            int oddOffsetX = 0;
            int oddOffsetY = 0;
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch(event.getAction()){
                    case MotionEvent.ACTION_DOWN:
                        downX =  event.getX();
                        downY =  event.getY();
                        oddOffsetX = wmParams.x;
                        oddOffsetY = wmParams.y;
                        LogUtil.w("点击按下悬浮框==downX"+downX+";downY="+downY+";oddOffsetX="+oddOffsetX+";oddOffsetY="+oddOffsetY);
    //实现长按悬浮窗点击事件
                        handler.postDelayed(runnable, ViewConfiguration.getLongPressTimeout());
                        break;
                    case MotionEvent.ACTION_MOVE:
                        if(!isMove){//让长摁悬浮窗时不导致他移动
                            break;
                        }
                        float moveX = event.getX();
                        float moveY =  event.getY();
                        //防止拖动太快
                        wmParams.x += (moveX - downX)/3;
                        wmParams.y += (moveY - downY)/3;
                        LogUtil.w("点击“移动”悬浮框==moveX"+moveX+";moveY="+moveY+";wmParams.x="+wmParams.x+";wmParams.y="+wmParams.y);
                        LogUtil.d("移动的距离==x:"+Math.abs(moveX - downX)+";y=="+ Math.abs(moveY - downY));
                        int scaledTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
                        if (Math.abs(moveX - downX) > scaledTouchSlop || Math.abs(moveY - downY) > scaledTouchSlop) { //移动超过了阈值,表示移动了
                            isMove = true; //移除runnable
                            handler.removeCallbacks(runnable);
                            if(mView != null){
                                //更新视图
                                mWindowManager.updateViewLayout(mView,wmParams);
                            }
                        }

                        break;
                    case MotionEvent.ACTION_UP:
                        int newOffsetX = wmParams.x;
                        int newOffsetY = wmParams.y;

                        handler.removeCallbacks(runnable);
                        LogUtil.w("点击“抬起”悬浮框==newOffsetX="+newOffsetX+";newOffsetY="+newOffsetY+";oddOffsetX="+oddOffsetX+";oddOffsetY="+oddOffsetY);
                        LogUtil.d("移动的距离==x:"+Math.abs(newOffsetX - oddOffsetX)+";y=="+Math.abs(newOffsetY - oddOffsetY));
                        if(Math.abs(newOffsetX - oddOffsetX) <=20 || Math.abs(newOffsetY - oddOffsetY) <=20){
                            isMove = true;
                            //添加接口回调
                            if(mOnFloatClickListener != null){
                                if(mView != null){
                                    mWindowManager.updateViewLayout(mView,wmParams);
                                }
                                mOnFloatClickListener.onCancelClick(mView);
                            }
                        }
                        break;
                    case MotionEvent.ACTION_CANCEL:
                        LogUtil.w("取消");
                        break;
                }
                return true;
            }

        });

通过上面的代码,我们就可以实现拖动和长按点击事件了,长按能实现,点击事件很简单,你只需要将取消的事件变成点击事件就行。下图为长摁改变布局

好了,我现在要贴出所有代码来了,

package com.android.ui;

import android.content.Context;
import android.graphics.PixelFormat;
import android.os.Build;
import android.os.Handler;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowManager;
import android.widget.ImageView;

import com.android.util.LogUtil;

public class ScannerFloatView {
    private static Context mContext;
    private static WindowManager mWindowManager;
    private static WindowManager.LayoutParams wmParams;
    private static View mView;
    private static boolean isShow = false;//悬浮框是否已经显示

    private static OnClickListener mListener;//view的点击回调listener
    private static OnFloatClickListener mOnFloatClickListener;
    private ImageView ivLinght;
    private ImageView ivLinghtClose;

    public void setOnClickListener(OnClickListener listener){
        mListener = listener;
    }

    public interface OnClickListener{
        void onClick(View view);
    }

    public interface OnFloatClickListener{
        void onCancelClick(View view);
        void onLongClick();

    }

    public void setOnFloatClickListener(OnFloatClickListener onFloatClickListener){
        mOnFloatClickListener = onFloatClickListener;
    }

    private static ScannerFloatView floatView = null;

    private ScannerFloatView(){

    }

    public static synchronized ScannerFloatView getInstance(){
        if(floatView == null){
            floatView = new ScannerFloatView();
        }
        return floatView;
    }


    /**
     * 是否移动
     */
    private boolean isMove = true;


    /**
     * 显示悬浮框
     */
    public void showFloatView(Context context,int layoutId){
        mContext = context;

        if(isShow){
            return;
        }

        mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        wmParams = new WindowManager.LayoutParams();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {//哭了。。。。我靠8.0又改了
            wmParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
        } else {
            wmParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
        }


        wmParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
        wmParams.gravity = Gravity.CENTER;
        wmParams.format = PixelFormat.RGBA_8888;
        wmParams.x = context.getResources().getDisplayMetrics().widthPixels;
        wmParams.y = 0;

        wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT;

        mView = LayoutInflater.from(context).inflate(layoutId, null);

        ivLinght = mView.findViewById(R.id.iv_lll);
      
        mWindowManager.addView(mView, wmParams);



        mView.setOnTouchListener(new View.OnTouchListener() {
            float downX = 0;
            float downY = 0;
            int oddOffsetX = 0;
            int oddOffsetY = 0;
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch(event.getAction()){
                    case MotionEvent.ACTION_DOWN:
                        downX =  event.getX();
                        downY =  event.getY();
                        oddOffsetX = wmParams.x;
                        oddOffsetY = wmParams.y;
                        LogUtil.w("点击按下悬浮框==downX"+downX+";downY="+downY+";oddOffsetX="+oddOffsetX+";oddOffsetY="+oddOffsetY);
                        handler.postDelayed(runnable, ViewConfiguration.getLongPressTimeout());
                        break;
                    case MotionEvent.ACTION_MOVE:
                        if(!isMove){
                            break;
                        }
                        float moveX = event.getX();
                        float moveY =  event.getY();
                        //不除以3,拖动的view抖动的有点厉害
                        wmParams.x += (moveX - downX)/3;
                        wmParams.y += (moveY - downY)/3;
                        LogUtil.w("点击“移动”悬浮框==moveX"+moveX+";moveY="+moveY+";wmParams.x="+wmParams.x+";wmParams.y="+wmParams.y);
                        LogUtil.d("移动的距离==x:"+Math.abs(moveX - downX)+";y=="+ Math.abs(moveY - downY));
                        int scaledTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
                        if (Math.abs(moveX - downX) > scaledTouchSlop || Math.abs(moveY - downY) > scaledTouchSlop) { //移动超过了阈值,表示移动了
                            isMove = true; //移除runnable
                            handler.removeCallbacks(runnable);
                            if(mView != null){
                          
                                mWindowManager.updateViewLayout(mView,wmParams);
                            }
                        }

                        break;
                    case MotionEvent.ACTION_UP:
                        int newOffsetX = wmParams.x;
                        int newOffsetY = wmParams.y;

                        handler.removeCallbacks(runnable);
                        LogUtil.w("点击“抬起”悬浮框==newOffsetX="+newOffsetX+";newOffsetY="+newOffsetY+";oddOffsetX="+oddOffsetX+";oddOffsetY="+oddOffsetY);
                        LogUtil.d("移动的距离==x:"+Math.abs(newOffsetX - oddOffsetX)+";y=="+Math.abs(newOffsetY - oddOffsetY));
                        if(Math.abs(newOffsetX - oddOffsetX) <=20 || Math.abs(newOffsetY - oddOffsetY) <=20){
                            isMove = true;
                            if(mOnFloatClickListener != null){
                                if(mView != null){
                                   
                                    mWindowManager.updateViewLayout(mView,wmParams);
                                }
                                mOnFloatClickListener.onCancelClick(mView);
                            }
                        }
                        break;
                    case MotionEvent.ACTION_CANCEL:
                        LogUtil.w("取消");
                        break;
                }
                return true;
            }

        });

        isShow = true;
    }

    private Runnable runnable = new Runnable() {
        @Override
        public void run() {
            LogUtil.i("长按了vvvvv");
            isMove = false;
            if(mOnFloatClickListener != null){
             
                mWindowManager.updateViewLayout(mView,wmParams);
                mOnFloatClickListener.onLongClick();
            }
        }
    };

    private Handler handler = new Handler();

    /**
     * 隐藏悬浮窗
     */
    public void hideFloatView(){
        if(mWindowManager != null && isShow){
            mWindowManager.removeView(mView);
            isShow = false;
        }
    }

}

调用

if (Build.VERSION.SDK_INT >= 23) {
                if (!Settings.canDrawOverlays(this)) {
                    Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                            Uri.parse("package:" + getPackageName()));
                    startActivity(intent);
                }else {
                    showFloatView();
                }
            }

展示悬浮框,调用

/**
	 * 显示悬浮窗
	 */
	private void showFloatView() {
		floatView = ScannerFloatView.getInstance();
		floatView.showFloatView(getApplication(), R.layout.layout_float_click);
		floatView.setOnFloatClickListener(new ScannerFloatView.OnFloatClickListener() {
			@Override
			public void onCancelClick(View view) {
                if(SharePreConfig.getScanmode() == 0)
				keySwitchToTigger(TRIGGER_CANCEL_VALUE);
			}

			@Override
			public void onLongClick() {
                keySwitchToTigger(TRIGGER_CANCEL_VALUE);
				keySwitchToTigger(TRIGGER_VALUE);
			}
		});
	}

	/**
	 * 隐藏悬浮窗
	 */
	public void hideFloatView(){
		if(floatView != null){
			floatView.hideFloatView();
		}
	}

猜你喜欢

转载自blog.csdn.net/qq_33796069/article/details/88947145
今日推荐