软件编程
位置:首页>> 软件编程>> Android编程>> Android仿优酷视频的悬浮窗播放效果

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

作者:三非_程序猿  发布时间:2022-04-30 20:44:51 

标签: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;
   }
 }

来源:https://blog.csdn.net/why931022/article/details/107229849

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com