Android仿优酷视频的悬浮窗播放

Android仿优酷视频的悬浮窗播放

之前接了需求要让视频播放时可以像优酷视频那样在悬浮窗里播放,并且悬浮窗和主播放页面之间要实现无缝切换,项目中使用的是自封装的ijkplayer
这个要求就代表不能在悬浮窗中新建视频控件,所以需要在悬浮窗中复用主页面的视频控件,以达到无缝衔接的效果。

主页面对应的视频控件的父view

 <FrameLayout
            android:id="@+id/vw_live"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_centerInParent="true"/>

用FrameLayout作为添加视频控件的ParentView,通过addview方法将新建的播放器控件添加到父控件内部

vw_live = new IjkVideoView(this);
video_frame = findViewById(R.id.vw_live);
video_frame.addView(vw_live);

主播放界面的启动模式

播放主界面的activity的启动模式不能为默认,因为我们要保证播放主界面在显示悬浮窗的时候退到后台,但是整个的应用不能退到后台,所以activity的启动模式改为singleInstance

android:launchMode="singleInstance"

退到后台我们通过moveTaskToBack(true)方法;

moveTaskToBack(true);

可以让播放界面退到后台而整个应用不会退回后台

权限请求

要使用悬浮窗需要申请权限

<uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW" />
if (!Settings.canDrawOverlays(this)) {
            Toast.makeText(this, "当前无权限,请授权", Toast.LENGTH_SHORT);
            startActivityForResult(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())), 2);
        } 

悬浮窗

 @SuppressLint("ClickableViewAccessibility")
    public void showFloatingWindowView(IjkVideoView view) {
        // 悬浮窗显示视图
        LayoutInflater layoutInflater = LayoutInflater.from(activity);
        mShowView = layoutInflater.inflate(R.layout.video_floating_window_layout, null);;
        // 获取系统窗口管理服务
        mWindowManager = (WindowManager) activity.getSystemService(Context.WINDOW_SERVICE);
        // 悬浮窗口参数设置及返回
        mFloatParams = getParams();
        //floatingWindow内部控件实例
        init(view);
        // 设置窗口触摸移动事件
        mShowView.setOnTouchListener(new FloatViewMoveListener());

        // 悬浮窗生成
        mWindowManager.addView(mShowView, mFloatParams);
    }
    private void init(IjkVideoView viewGroup){
        videoLayout = mShowView.findViewById(R.id.floating_video);
        videoLayout.removeAllViews();
        if (viewGroup != null){
            ijkVideoView = viewGroup;
            videoLayout.addView(ijkVideoView,new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT
                    ,ViewGroup.LayoutParams.MATCH_PARENT));
        }

        mBtnCloseFloatingWindow = mShowView.findViewById(R.id.close_floating_view);
        mBtnCloseFloatingWindow.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });
        mBtnBackFloatingWindow = (ImageView)mShowView.findViewById(R.id.back_floating_view);
        mBtnBackFloatingWindow.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });
    }

    private WindowManager.LayoutParams getParams() {
        WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
        //设置悬浮窗口类型
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
        } else {
            layoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
        }
        //设置悬浮窗口属性
        layoutParams.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;
        //设置悬浮窗口透明
        layoutParams.format = PixelFormat.TRANSLUCENT;
        //设置悬浮窗口长宽数据
        layoutParams.width = 500;
        layoutParams.height = 340;
        //设置悬浮窗显示位置
        layoutParams.gravity = Gravity.START | Gravity.TOP;
        layoutParams.x = 100;
        layoutParams.y = 100;
        return layoutParams;
    }

悬浮窗的xml,可通过自定义获得自己想要的效果

<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/floating_video_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <FrameLayout
        android:id="@+id/floating_video"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>


    <ImageView
        android:id="@+id/close_floating_view"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_gravity="end"
        android:padding="10dp"
        android:src="@android:drawable/ic_menu_close_clear_cancel" />

    <ImageView
        android:id="@+id/back_floating_view"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:padding="10dp"
        android:src="@android:drawable/ic_menu_revert" />
</FrameLayout>

悬浮窗的滑动,我们可以通过自定义点击监听实现

/**
     * 浮窗移动/点击监听
     */
    private class FloatViewMoveListener implements View.OnTouchListener {

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

        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {
            int action = motionEvent.getAction();
            int x = (int) motionEvent.getX();
            int y = (int) motionEvent.getY();
            switch (action) {
                case MotionEvent.ACTION_DOWN:
                    isMove = false;
                    mTouchStartX = (int) motionEvent.getRawX();
                    mTouchStartY = (int) motionEvent.getRawY();
                    mStartX = x;
                    mStartY = y;
                    break;
                case MotionEvent.ACTION_MOVE:
                    int mTouchCurrentX = (int) motionEvent.getRawX();
                    int mTouchCurrentY = (int) motionEvent.getRawY();
                    mFloatParams.x += mTouchCurrentX - mTouchStartX;
                    mFloatParams.y += mTouchCurrentY - mTouchStartY;
                    mWindowManager.updateViewLayout(mShowView, mFloatParams);
                    mTouchStartX = mTouchCurrentX;
                    mTouchStartY = mTouchCurrentY;
                    float deltaX = x - mStartX;
                    float deltaY = y - mStartY;
                    if (Math.abs(deltaX) >= 5 || Math.abs(deltaY) >= 5) {
                        isMove = true;
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    break;
                default:
                    break;
            }
            //如果是移动事件不触发OnClick事件,防止移动的时候一放手形成点击事件
            return isMove;
        }
    }

悬浮窗的消失,在这里调用videoLayout.removeAllViews()是为了将复用的视频控件的父View清空,返回主播放activity的时候调用addview方法不会再报 child view has Parent,you have to call removeView()的错

public void dismiss() {
        if (mWindowManager != null && mShowView != null) {
            videoLayout.removeAllViews();
            if (mShowView.getParent() != null){
                mWindowManager.removeView(mShowView);
            }
        }
    }

启动悬浮窗

 public videoFloatingWindow(Context context){
        super(context);
        this.activity = context;
    }

对于悬浮窗的调用

用hasBind来记录是否调用了悬浮窗

   private void startFloatingWindow(){
        if (!Settings.canDrawOverlays(this)) {
            Toast.makeText(this, "当前无权限,请授权", Toast.LENGTH_SHORT);
            startActivityForResult(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())), 2);
        } else {
            video_frame.removeView(vw_live);
            videoFloatingWindow.getInstance(this).showFloatingWindowView(vw_live);
            hasBind = true;
            moveTaskToBack(true);
        }
    }

注意

一.由于主界面activity使用了singleInstance启动模式,所以从悬浮窗返回主界面activity时,要添加flag

                Intent intent = new Intent(activity, activity.getClass());
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                activity.startActivity(intent);

二.当主界面的activity退回后台,再重新进入主界面的时候,注意,不再调用onCreate方法,而是调用onNewIntent,所以重写onNewIntent方法,重新进入主界面,悬浮窗消失

  @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        Log.d("RemoteView", "重新显示了");
        //不显示悬浮框
        if (hasBind){
            videoFloatingWindow.getInstance(this).dismiss();
            video_frame.removeAllViews();
            if (vw_live != null){
                video_frame.addView(vw_live);
            }
            hasBind = false;
        }
    }
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
您!要实现仿微信视频通话悬浮窗功能,您可以按照以下步骤进行操作: 1. 创建悬浮窗权限:在 AndroidManifest.xml 文件中添加悬浮窗权限声明,如下所示: ```xml <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> ``` 2. 创建悬浮窗服务:创建一个继承自 Service 的类,用于管理悬浮窗的创建、更新和移除等操作。在该类中,您可以使用 WindowManager 来显示悬浮窗,并监听触摸事件来实现拖拽和缩放等操作。 3. 设计悬浮窗布局:创建一个 XML 布局文件,用于定义悬浮窗的界面。您可以自定义界面元素,如视频画面、控制按钮等。 4. 实现悬浮窗功能:在悬浮窗服务类中,根据需要添加相应的逻辑。例如,您可以使用 MediaProjection API 来捕获屏幕内容,并将其显示在悬浮窗中;您还可以使用 Camera API 来获取摄像头数据,并实现视频通话功能。 5. 添加权限申请逻辑:在需要使用悬浮窗功能的地方,添加权限申请逻辑,以确保用户在安装应用时授予悬浮窗权限。 请注意,实现仿微信视频通话悬浮窗功能需要一定的 Android 开发知识和经验。如果您对 Android 开发不太熟悉,建议您先学习相关的基础知识,然后再尝试实现该功能。同时,还要注意遵守 Android 官方的开发规范和安全要求。希望对您有所帮助!如果您有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值