美文网首页
android视频通话悬浮窗的适配

android视频通话悬浮窗的适配

作者: 木棉_8d64 | 来源:发表于2019-06-01 14:23 被阅读0次

    前序

    按项目交互要求,需要把视频通话界面,缩小至悬浮窗显示,基本实现思路这个比较好想,就是启用一个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视频通话悬浮窗的适配

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