广告
返回顶部
首页 > 资讯 > 移动开发 >Android悬浮窗视频
  • 302
分享到

Android悬浮窗视频

android悬浮窗Android 2022-06-06 13:06:24 302人浏览 八月长安
摘要

类似微信视频通话需求。 思路: 1.申请悬浮窗权限 2.windowManager实现悬浮窗; 3.moveToback退出全屏,显示悬浮窗;

类似微信视频通话需求。
思路:
1.申请悬浮窗权限
2.windowManager实现悬浮窗;
3.moveToback退出全屏,显示悬浮窗;

当用户正在NewsActivity看文字,视频通话来了,接听(VideoActivity),然后缩至悬浮窗,此时应回到NewsActivity,悬浮窗出现时该如何回到电话前的页面?finish掉VideoActivity吗?finish后自然回退到栈内上一个Activity,页面逻辑上符合需求,但是VideoActivity中写了很多通话逻辑,销毁不太好,怎么办?(有人说,为何会在页面写逻辑,明显是没抽象好嘛,这都是后话,不解决当前版本问题)

于是想到干脆起两个任务栈,一个单独放VideoActivity,另一个放剩余的Activity,然后点击要悬浮时,直接后置VideoActivity所在任务栈即可。
实现两个任务栈也很简单,只要给VideoActivity设置Android:launchMode="singleInstance"即可。
想要退后该任务栈直接调用 moveTaskToBack(true);

然而singleInstance的坑不是一般的多。

一 . 首先 singleInstance会导致onRequestPermissionsResult( ),onActivityResult( )不回调。可是视频必须要申请RECORD_AUDIO,CAMERA,WRITE_EXTERNAL_STORAGE权限,以及悬浮窗ACTION_MANAGE_OVERLAY_PERMISSION。
无奈写个视频的前置页面标准启动模式,专门用来申请相机,录音,读写等权限,全部授权后 再真正进入singleInstance页面。
问题一算是迎刃而解。(躲过去了)

二. 长按home或者菜单键 查看Android最近任务列表时,一个APP竟然两个最近任务,解决方案:需要添加一个属性 保证 不在最近任务列表中显示当前activity所在的应用

android:launchMode= "singleInstance"  //开启新的应用任务栈
android:excludeFromRecents= "true"   //不在最近任务列表中显示当前activity所在的应用

三 选择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;
}

下面是悬浮窗的实现



public class FloatVideowindowservice extends Service {
    private WindowManager mWindowManager;
    private WindowManager.LayoutParams wmParams;
    private View mFloatingLayout;
    private RelativeLayout mPreviewLayout;
    private TutorialsManager mInstance = null;
    private SurfaceView remoteView = null;
    @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();//悬浮框点击事件的处理
        mInstance = TutorialsManager.getInstance(this);
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        int uid = intent.getIntExtra(VideoChatViewActivity.KEY_C_USER_ID, -1);
        if (uid != -1) {
            remoteView = mInstance.getRemoteVideo(uid);
            ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
            remoteView.setLayoutParams(lp);
            mPreviewLayout.addView(remoteView);
        } else {
            stopSelf();
        }
        return super.onStartCommand(intent, flags, startId);
    }
    @Override
    public void onDestroy() {
        if (null != mPreviewLayout) {
            mPreviewLayout.removeAllViews();
        }
        if (mFloatingLayout != null) {
            // 移除悬浮窗口
            mWindowManager.removeView(mFloatingLayout);
        }
    }
    
    private void initWindow() {
        mWindowManager = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
        wmParams = getParams();
        // 悬浮窗默认显示以左上角为起始坐标
        wmParams.gravity = Gravity.RIGHT | Gravity.TOP;
        //悬浮窗的开始位置,因为设置的是从左上角开始,所以屏幕左上角是x=0;y=0
        wmParams.x = DensityUtil.dip2px(10);
        wmParams.y = 110;
        // 获取浮动窗口视图所在布局
        mFloatingLayout = LayoutInflater.from(getApplicationContext()).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.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 = DeviceUtils.getScreenWidth();
        int cropHeight = fullCropWidth * 16 / 9;
        //设置悬浮窗口长宽数据
        wmParams.width = (int) (fullCropWidth * 0.26);
        wmParams.height = (int) (cropHeight * 0.26);
        return wmParams;
    }
    private void initFloating() {
        mPreviewLayout = mFloatingLayout.findViewById(R.id.small_size_preview);
        //悬浮框点击事件
        mPreviewLayout.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(FloatVideoWindowService.this, VideoChatViewActivity.class);
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                startActivity(intent);
                mPreviewLayout.removeView(remoteView);
                stopSelf();
            }
        });
        //悬浮框触摸事件,设置悬浮框可拖动
        mPreviewLayout.setOnTouchListener(new FloatingListener());
    }
    
    private int mTouchStartX;
    private int mTouchStartY;
    
    private int mStartX;
    private int mStartY;
    
    private boolean isMove;
    private class FloatingListener implements View.OnTouchListener {
        int slop = 1;//滑动距离,区分点击
        public FloatingListener() {
            slop = ViewConfiguration.get(getApplicationContext()).getScaledTouchSlop();
        }
        @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) >= slop || Math.abs(mStartY - mStopY) >= slop) {
                        isMove = true;
                    }
                    break;
                default:
                    break;
            }
            //如果是移动事件不触发OnClick事件,防止移动的时候一放手形成点击事件
            return isMove;
        }
    }
}

其中VideoActivity中代码:

    private int PermissionRequestCode = 10;
    private void onClickFloatBtn() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {
            startActivityForResult(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())), PermissionRequestCode);
        } else {
            showFloatView();
        }
    }
    private void showFloatView() {
        if (mUid != -1) {
            moveTaskToBack(true);
            Intent intent = new Intent(this, FloatVideoWindowService.class);
            intent.putExtra(KEY_C_USER_ID, mUid);
            startService(intent);
        }
    }
    @Override
    protected void onActivityResult(final int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == PermissionRequestCode) {
            if (mHandler == null) {
                mHandler = new Handler(Looper.getMainLooper());
            }
            //此处特意延时500ms,否则回调值不准确,   https://blog.csdn.net/qq_24179679/article/details/84139408
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                        if (Settings.canDrawOverlays(mContext)) {//开启
                            showFloatView();
                        } else {//关闭
                            ToastUtil.show(mContext, "您未授权悬浮窗");
                        }
                    }
                }
            }, 500);
        }
    }

等等,你刚才坑一里不是说,onActivityResult不回调吗?咋还在singleInstance的页面重写这个方法。经测试,onRequestPermissionsResult的确不回调,但是onActivityResult还是有回调的,延时500ms,回调值更准确哦。

是不是很开森,万事大吉了。人生就像登山,往上走即使一小步也有新高度;然而编程就像玩俄罗斯套娃,打破一小层,还有更大层等着你。走不完的套路,爬不完的坑。

问题描述:当singleInstance页面在onResume时,按下home键,再次点击app的桌面icon,进入后,发现不是刚才的singleInstance页面。我太难啦!!!

有人说可以设置:alwaysRetainTaskState = true

咦~ 这是什么属性?来看看谷歌官方文档怎么说的:

android:alwaysRetainTaskState
系统是否始终保持 Activity 所在任务的状态 —“true”表示是,“false”表示允许系统在特定情况下将任务重置到其初始状态。
默认值为“false”。该属性只对任务的根Activity 有意义;所有其他 Activity 均可忽略该属性。
正常情况下,当用户从主屏幕重新选择某个任务时,系统会在特定情况下清除该任务(从根 Activity 上的堆栈中移除所有Activity)。通常,如果用户在一段时间(如 30 分钟)内未访问任务,系统会执行此操作。
不过,如果该属性的值是“true”,则无论用户如何返回任务,该任务始终会显示最后一次的状态。例如,该属性非常适用于网络浏览器这类应用,因为其中存在大量用户不愿丢失的状态(如多个打开的标签)。

看完最后一句,“该属性非常适用于网络浏览器这类应用,因为其中存在大量用户不愿丢失的状态(如多个打开的标签)”,好像有点那个意思哦,赶紧加上试试,RUN。。。。。满怀期待。。。。虔诚等待。。。。

等了那么久,然并卵。显然是singleInstance在作祟,烦屎了。。。。

剩下我知识储量的最后一招了,纯属无奈之举!
监听app从后台切回前台时机,切回前台时,如果VideoActivity尚在且在通话中,二话不说,直接startActivity之。
怎么监听APP从后台切到前台了呢?注册Application.ActivityLifecycleCallbacks
记得在MyApplication的onCreate中调用注册方法

reGISterActivityLifecycleCallback(new AppActivityLifecycleCallbacks());

看看AppActivityLifecycleCallbacks实现类的代码:

    @Override
    public void onActivityStarted(Activity activity) {
            //视频面试时APP切换至后台,然后由后台切换至前台,视频面试中,且不是悬浮窗显示时 要回到视频面试页面
            if (onActivityStoppedFlag && BackgroundUtils.isForeground(activity)
                    && ActivityStackHelper.isActivityRunning(VideoChatViewActivity.class)
                    && !SystemUtils.isServiceRunning(activity, FloatVideoWindowService.class.getName())) {
                activity.startActivity(new Intent(activity, VideoChatViewActivity.class));
            }
            onActivityStoppedFlag = !BackgroundUtils.isForeground(activity);
    }
    
    private boolean onActivityStoppedFlag = false;
    @Override
    public void onActivityStopped(Activity activity) {
        onActivityStoppedFlag = !BackgroundUtils.isForeground(activity);
    }

功能算是“顺利”实现了,但是监听app后台切前台纯属临时方案;恳请有爱的大神帮忙支招,在我的知识盲区指津。

参考文献:
Https://www.jianshu.com/p/3786653f9c9b
https://blog.csdn.net/qq_24179679/article/details/84139408


作者:iblade


--结束END--

本文标题: Android悬浮窗视频

本文链接: https://www.lsjlt.com/news/28956.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Android悬浮窗视频
    类似微信视频通话需求。 思路: 1.申请悬浮窗权限 2.windowManager实现悬浮窗; 3.moveToback退出全屏,显示悬浮窗; ...
    99+
    2022-06-06
    android悬浮窗 Android
  • Android仿腾讯视频实现悬浮窗效果
    前言 相信大家对Android悬浮窗应该是很熟悉了,比如说腾讯视频、爱奇艺等APP都有悬浮窗功能。在你打游戏的同时还可以看视频,充分利用屏幕空间。还有微信,360手机卫士等APP也有...
    99+
    2022-11-12
  • Android视频悬浮窗口实现的示例代码
    前言 本文例子实现了点击显示悬浮窗口,同时窗口可播放视频,拖动位置,点击关闭及返回 APP 页面,通过例子来讲述悬浮窗口实现原理及细节处理,效果图如下所示: 悬浮窗口.gif...
    99+
    2022-06-06
    悬浮窗口 示例 Android
  • Android悬浮窗如何实现
    小编给大家分享一下Android悬浮窗如何实现,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!效果如下:显示浮窗原生ViewManager接口提供了向窗口添加并操纵...
    99+
    2023-06-04
  • Android 应用弹出悬浮窗
    Android开发者经常遇到应用想弹出悬浮窗的操作,而且有可能还想要高层级弹窗,就像ipone的浮标touch一样。android当然也有类似的悬浮图标,比如前些年我们的流量监控提醒。  这里我们忽略UI美学,简单记录一下: 1、基本使用...
    99+
    2023-09-01
    android view
  • Android实现悬浮窗效果
    本文实例为大家分享了Android实现悬浮窗效果的具体代码,供大家参考,具体内容如下 一、权限: <uses-permission android:name="android....
    99+
    2022-11-13
  • Android悬浮窗效果怎么实现
    要实现Android的悬浮窗效果,可以采用以下几种方法: 使用系统提供的WindowManager类来创建一个悬浮窗口。可以通过...
    99+
    2023-10-22
    Android
  • Android仿IOS系统悬浮窗效果
    在一些场合里,我们使用悬浮窗会有很大的便利,比如IOS系统的悬浮窗,360或者其他手机卫士的悬浮窗等等。 本篇博客,我们创造出两个悬浮窗,通过点击小悬浮窗打开或者关闭大悬浮窗(一个播...
    99+
    2022-11-12
  • Android手机悬浮窗口小案例
    本文实例为大家分享了Android九宫格图片展示的具体代码,供大家参考,具体内容如下 –主页面——– //布局中就一个Button public class MainActi...
    99+
    2022-06-06
    悬浮窗口 Android
  • Android实现圆形菜单悬浮窗
    序言 Android悬浮窗的实现,主要有四个步骤: 1. 声明及申请权限2. 构建悬浮窗需要的控件3. 将控件添加到WindowManager4. 必要时更新WindowManage...
    99+
    2022-11-12
  • Android开发悬浮窗踩坑解决
    目录正文1、悬浮窗中EditText无法获得弹出键盘2、悬浮窗无法录音正文 最近在做一个全局悬浮窗的基于ChatGPT应用快Ai,需要悬浮于其他应用上面,方便从悬浮窗ChatGPT...
    99+
    2023-03-19
    Android悬浮窗 Android开发
  • Android悬浮窗屏蔽悬浮窗外部所有的点击事件的实例代码
    Android可以在所有应用上方添加View,就是给WindowManager添加一个View,在创建的View的时候可以给这个View设置LayoutParams(andro...
    99+
    2022-06-06
    事件 android悬浮窗 Android
  • Android创建悬浮窗的完整步骤
    在Android中想要创建悬浮窗分为三步 1.申请权限 2.使用服务启动悬浮窗 3.设置悬浮窗参数并添加进WindowManager 下面话不多说了,来一起看看详细的实现过程 申请权...
    99+
    2022-11-12
  • Android超简单悬浮窗使用教程
    完全自定义悬浮窗,保证100%学会的超简单悬浮窗 先看看效果图:                 图...
    99+
    2022-11-12
  • Kotlin实现Android系统悬浮窗详解
    目录Android 弹窗浅谈系统悬浮窗具体实现权限申请代码设计具体实现FloatWindowService 类FloatWindowManager 类FloatWindowManag...
    99+
    2022-11-12
  • Android WindowManger实现桌面悬浮窗功能
    目录效果图使用WindowManager实现分析问题参考如果想实现一个在桌面显示的悬浮窗,用Dialog、PopupWindow、Toast等已经不能实现了,他们基本都是在Activ...
    99+
    2023-05-18
    Android桌面悬浮窗 Android WindowManger悬浮窗
  • Kotlin如何实现Android系统悬浮窗
    本篇内容介绍了“Kotlin如何实现Android系统悬浮窗”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!Android 弹窗浅谈我们知道 ...
    99+
    2023-06-22
  • Android可拖动悬浮窗怎么实现
    要实现在Android中可拖动的悬浮窗,可以按照以下步骤进行:1. 创建一个自定义的`FloatingView`类来实现悬浮窗的视图...
    99+
    2023-08-16
    Android
  • android编程实现悬浮窗体的方法
    本文实例讲述了android编程实现悬浮窗体的方法。分享给大家供大家参考,具体如下: 突然对悬浮窗体感兴趣,查资料做了个小Demo,效果是点击按钮后,关闭当前Activity,...
    99+
    2022-06-06
    方法 窗体 Android
  • Android应用内悬浮窗Activity如何实现
    这篇文章主要介绍Android应用内悬浮窗Activity如何实现,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!缩放方法缩放activity需要使用WindowManager.LayoutParams,控制windo...
    99+
    2023-06-22
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作