Android仿優(yōu)酷視頻的懸浮窗播放效果
之前接了需求要讓視頻播放時(shí)可以像優(yōu)酷視頻那樣在懸浮窗里播放,并且懸浮窗和主播放頁(yè)面之間要實(shí)現(xiàn)無(wú)縫切換,項(xiàng)目中使用的是自封裝的ijkplayer
這個(gè)要求就代表不能在懸浮窗中新建視頻控件,所以需要在懸浮窗中復(fù)用主頁(yè)面的視頻控件,以達(dá)到無(wú)縫銜接的效果。
主頁(yè)面對(duì)應(yīng)的視頻控件的父view
<FrameLayout android:id="@+id/vw_live" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_centerInParent="true"/>
用FrameLayout作為添加視頻控件的ParentView,通過(guò)addview方法將新建的播放器控件添加到父控件內(nèi)部
vw_live = new IjkVideoView(this);
video_frame = findViewById(R.id.vw_live); video_frame.addView(vw_live);
主播放界面的啟動(dòng)模式
播放主界面的activity的啟動(dòng)模式不能為默認(rèn),因?yàn)槲覀円WC播放主界面在顯示懸浮窗的時(shí)候退到后臺(tái),但是整個(gè)的應(yīng)用不能退到后臺(tái),所以activity的啟動(dòng)模式改為singleInstance
android:launchMode="singleInstance"
退到后臺(tái)我們通過(guò)moveTaskToBack(true)方法;
moveTaskToBack(true);
可以讓播放界面退到后臺(tái)而整個(gè)應(yīng)用不會(huì)退回后臺(tái)
權(quán)限請(qǐng)求
要使用懸浮窗需要申請(qǐng)權(quán)限
<uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW" />
if (!Settings.canDrawOverlays(this)) { Toast.makeText(this, "當(dāng)前無(wú)權(quán)限,請(qǐng)授權(quán)", 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);; // 獲取系統(tǒng)窗口管理服務(wù) mWindowManager = (WindowManager) activity.getSystemService(Context.WINDOW_SERVICE); // 懸浮窗口參數(shù)設(shè)置及返回 mFloatParams = getParams(); //floatingWindow內(nèi)部控件實(shí)例 init(view); // 設(shè)置窗口觸摸移動(dòng)事件 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(); //設(shè)置懸浮窗口類型 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; } else { layoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; } //設(shè)置懸浮窗口屬性 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; //設(shè)置懸浮窗口透明 layoutParams.format = PixelFormat.TRANSLUCENT; //設(shè)置懸浮窗口長(zhǎng)寬數(shù)據(jù) layoutParams.width = 500; layoutParams.height = 340; //設(shè)置懸浮窗顯示位置 layoutParams.gravity = Gravity.START | Gravity.TOP; layoutParams.x = 100; layoutParams.y = 100; return layoutParams; }
懸浮窗的xml,可通過(guò)自定義獲得自己想要的效果
<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>
懸浮窗的滑動(dòng),我們可以通過(guò)自定義點(diǎn)擊監(jiān)聽實(shí)現(xiàn)
/** * 浮窗移動(dòng)/點(diǎn)擊監(jiān)聽 */ private class FloatViewMoveListener implements View.OnTouchListener { //開始觸控的坐標(biāo),移動(dòng)時(shí)的坐標(biāo)(相對(duì)于屏幕左上角的坐標(biāo)) private int mTouchStartX; private int mTouchStartY; //開始時(shí)的坐標(biāo)和結(jié)束時(shí)的坐標(biāo)(相對(duì)于自身控件的坐標(biāo)) private int mStartX, mStartY; //判斷懸浮窗口是否移動(dòng),這里做個(gè)標(biāo)記,防止移動(dòng)后松手觸發(fā)了點(diǎn)擊事件 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; } //如果是移動(dòng)事件不觸發(fā)OnClick事件,防止移動(dòng)的時(shí)候一放手形成點(diǎn)擊事件 return isMove; } }
懸浮窗的消失,在這里調(diào)用videoLayout.removeAllViews()是為了將復(fù)用的視頻控件的父View清空,返回主播放activity的時(shí)候調(diào)用addview方法不會(huì)再報(bào) child view has Parent,you have to call removeView()的錯(cuò)
public void dismiss() { if (mWindowManager != null && mShowView != null) { videoLayout.removeAllViews(); if (mShowView.getParent() != null){ mWindowManager.removeView(mShowView); } } }
啟動(dòng)懸浮窗
public videoFloatingWindow(Context context){ super(context); this.activity = context; }
對(duì)于懸浮窗的調(diào)用
用hasBind來(lái)記錄是否調(diào)用了懸浮窗
private void startFloatingWindow(){ if (!Settings.canDrawOverlays(this)) { Toast.makeText(this, "當(dāng)前無(wú)權(quán)限,請(qǐng)授權(quán)", 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啟動(dòng)模式,所以從懸浮窗返回主界面activity時(shí),要添加flag
Intent intent = new Intent(activity, activity.getClass()); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); activity.startActivity(intent);
二.當(dāng)主界面的activity退回后臺(tái),再重新進(jìn)入主界面的時(shí)候,注意,不再調(diào)用onCreate方法,而是調(diào)用onNewIntent,所以重寫onNewIntent方法,重新進(jìn)入主界面,懸浮窗消失
@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; } }
總結(jié)
到此這篇關(guān)于Android仿優(yōu)酷視頻的懸浮窗播放的文章就介紹到這了,更多相關(guān)android 優(yōu)酷視頻懸浮窗播放內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- java、android可用的rtp封包解包h264案例
- Android下拉刷新ListView——RTPullListView(demo)
- Android GSYVideoPlayer視頻播放器功能的實(shí)現(xiàn)
- Android實(shí)現(xiàn)本地Service方法控制音樂(lè)播放
- Android項(xiàng)目實(shí)現(xiàn)視頻播放器
- Android Studio實(shí)現(xiàn)音樂(lè)播放器
- Android Studio實(shí)現(xiàn)簡(jiǎn)單音樂(lè)播放功能的示例代碼
- Android實(shí)現(xiàn)文字滾動(dòng)播放效果的代碼
- Android10.0實(shí)現(xiàn)本地音樂(lè)播放(附源碼下載)
- Android原生視頻播放VideoView的使用
- Android手機(jī)通過(guò)rtp發(fā)送aac數(shù)據(jù)給vlc播放的實(shí)現(xiàn)步驟
相關(guān)文章
Android apk 項(xiàng)目一鍵打包并上傳到蒲公英的實(shí)現(xiàn)方法
這篇文章主要介紹了Android apk 項(xiàng)目一鍵打包并上傳到蒲公英,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-06-06Android實(shí)現(xiàn)上傳圖片至java服務(wù)器
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)上傳圖片至java服務(wù)器的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-08-08使用Thumbnails實(shí)現(xiàn)圖片指定大小壓縮
這篇文章主要為大家詳細(xì)介紹了使用Thumbnails實(shí)現(xiàn)圖片指定大小壓縮,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-08-08Android實(shí)現(xiàn)Service下載文件,Notification顯示下載進(jìn)度的示例
本篇文章主要介紹了Android實(shí)現(xiàn)Service下載文件,Notification顯示下載進(jìn)度,具有一定的參考價(jià)值,有興趣的可以了解一下。2017-01-01Android開發(fā)組件flutter的20個(gè)常用技巧示例總結(jié)
這篇文章主要為大家介紹了Android開發(fā)組件flutter的20個(gè)常用技巧示例總結(jié),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05Android Moveview滑屏移動(dòng)視圖類完整實(shí)例
這篇文章主要介紹了Android Moveview滑屏移動(dòng)視圖類,很有實(shí)用價(jià)值,需要的朋友可以參考下2014-07-07解決android studio 3.0 加載項(xiàng)目過(guò)慢問(wèn)題--maven倉(cāng)庫(kù)選擇
這篇文章主要介紹了android studio 3.0 加載項(xiàng)目過(guò)慢問(wèn)題解決方案---maven倉(cāng)庫(kù)選擇,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-11-11