android视频通话悬浮窗的适配

前序

按项目交互要求,需要把视频通话界面,缩小至悬浮窗显示,基本实现思路这个比较好想,就是启用一个service,在里面用WindowManager去addView来展示悬浮窗画面。基本效果是有了,但填坑之路才刚开始。。

坑一:WindowManager.LayoutParam.type的选取

选择TYPE_TOAST,如果期间有toast弹出,在android7.1.1会崩溃

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

注意:在android9.0以上就算按照上面方法设置,如果不提前授予悬浮窗权限,也会崩溃

坑二:SingleInstance和onActivityResult搭配问题

当处于悬浮窗口时,此时通话界面会推到后台,要调用moveTaskToBack(true),如果通话的Activity的启动模式不设置为SingleInstance,就会导致整个应用退到后台,不是我们想要的效果,为了只把VIdeoCallActivity(通话界面)退到后台,那么此界面就必须得在单独的一个栈里,所以要设置为SingleInstance。
用一个启动模式为SingleInstance的Activity去打开一个界面,在onActivityResult是拿不到结果的,所以只能用startActivity,然后再VIdeoCallActivity判断悬浮是否已打开,如果打开,则悬浮显示。

坑三:跳转开启悬浮窗设置界面的机型适配问题

适配了市面上大部分厂商机型,小米、华为、魅族、oppo、vivo、360、锤子
跳转开启权限工具类

悬浮窗服务FloatVideoWindowService

 FloatVideoWindowService extends Service {

    private static final String TAG = "FloatVideoWindowService";

    private WindowManager mWindowManager;
    private WindowManager.LayoutParams wmParams;

    private View mFloatingLayout;
    private RelativeLayout smallSizePreviewLayout;

    private BaseVCManager mVCManager;

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

    public class MyBinder extends Binder {
        public FloatVideoWindowService getService() {
            return FloatVideoWindowService.this;
        }
    }

    @Override
    public void onCreate() {
        super.onCreate();
        initWindow();//设置悬浮窗基本参数(位置、宽高等)
        initFloating();//悬浮框点击事件的处理
        mVCManager = VideoChatStateManager.getInstance().getManager();
        initSurface();
    }

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


    @Override
    public void onDestroy() {
        LogUtil.d(TAG, "onDestroy");
        if (mFloatingLayout != null) {
            // 移除悬浮窗口
            mWindowManager.removeView(mFloatingLayout);
        }
    }

    /**
     * 初始化预览窗口
     */
    private synchronized void initSurface() {
        if (mVCManager != null) {
            mVCManager.switchToFloatWindow(this, smallSizePreviewLayout);
        }
    }

    /**
     * 设置悬浮框基本参数(位置、宽高等)
     */
    private void initWindow() {
        mWindowManager = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
        wmParams = getParams();
        // 悬浮窗默认显示以左上角为起始坐标
        wmParams.gravity = Gravity.RIGHT | Gravity.TOP;
        //悬浮窗的开始位置,因为设置的是从左上角开始,所以屏幕左上角是x=0;y=0
        wmParams.x = AndroidUtil.dpToPx(this, 10);
        wmParams.y = 110;
        //得到容器,通过这个inflater来获得悬浮窗控件
        LayoutInflater inflater = LayoutInflater.from(getApplicationContext());
        // 获取浮动窗口视图所在布局
        mFloatingLayout = inflater.inflate(R.layout.view_videochat_services_float_layout, null);
        // 添加悬浮窗的视图
        mWindowManager.addView(mFloatingLayout, wmParams);
    }


    private WindowManager.LayoutParams getParams() {
        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_PHONE;
        }
        //wmParams.windowAnimations = R.style.default_style;
        //设置可以显示在状态栏上
        wmParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams
                .FLAG_NOT_TOUCH_MODAL |
                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR |
                WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;

        int fullCropWidth = ScreenUtil.getPxWidth(this);
        int cropHeight = fullCropWidth * 4 / 3;

        //设置悬浮窗口长宽数据
        wmParams.width = (int) (fullCropWidth * 0.26);
        wmParams.height = (int) (cropHeight * 0.26);
        return wmParams;
    }

    private void initFloating() {
        smallSizePreviewLayout = mFloatingLayout.findViewById(R.id.small_size_preview);

        //悬浮框点击事件
        smallSizePreviewLayout.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //HomeApi.startHomeActivity(FloatVideoWindowService.this);
                Intent intent = new Intent(new Intent(FloatVideoWindowService.this, VideoChatActivity.class));
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                startActivity(intent);
            }
        });

        //悬浮框触摸事件,设置悬浮框可拖动
        smallSizePreviewLayout.setOnTouchListener(new FloatingListener());
    }

    /**
     * 开始触控的坐标,移动时的坐标(相对于屏幕左上角的坐标)
     */
    private int mTouchStartX;
    private int mTouchStartY;
    /**
     * 开始时的坐标和结束时的坐标(相对于自身控件的坐标)
     */
    private int mStartX;
    private int mStartY;
    /**
     * 判断悬浮窗口是否移动,这里做个标记,防止移动后松手触发了点击事件
     */
    private boolean isMove;

    private class FloatingListener implements View.OnTouchListener {

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            int action = event.getAction();
            switch (action) {
                case MotionEvent.ACTION_DOWN:
                    isMove = false;
                    mTouchStartX = (int) event.getRawX();
                    mTouchStartY = (int) event.getRawY();
                    mStartX = (int) event.getX();
                    mStartY = (int) event.getY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    int mTouchCurrentX = (int) event.getRawX();
                    int mTouchCurrentY = (int) event.getRawY();
                    wmParams.x -= mTouchCurrentX - mTouchStartX;
                    wmParams.y += mTouchCurrentY - mTouchStartY;
                    mWindowManager.updateViewLayout(mFloatingLayout, wmParams);

                    mTouchStartX = mTouchCurrentX;
                    mTouchStartY = mTouchCurrentY;
                    break;
                case MotionEvent.ACTION_UP:
                    int mStopX = (int) event.getX();
                    int mStopY = (int) event.getY();
                    if (Math.abs(mStartX - mStopX) >= 1 || Math.abs(mStartY - mStopY) >= 1) {
                        isMove = true;
                    }
                    break;
                default:
                    LogUtil.d(TAG, "onTouch: other");
            }

            //如果是移动事件不触发OnClick事件,防止移动的时候一放手形成点击事件
            return isMove;
        }
    }
}

你可能感兴趣的:(android视频通话悬浮窗的适配)