Android視頻懸浮窗口實(shí)現(xiàn)的示例代碼
前言
本文例子實(shí)現(xiàn)了點(diǎn)擊顯示懸浮窗口,同時(shí)窗口可播放視頻,拖動(dòng)位置,點(diǎn)擊關(guān)閉及返回 APP 頁(yè)面,通過例子來講述懸浮窗口實(shí)現(xiàn)原理及細(xì)節(jié)處理,效果圖如下所示:

懸浮窗口.gif
原理
WindowManager 對(duì) View 視圖進(jìn)行添加、移除、更新處理;
WindowManager.LayoutParams 對(duì)窗口參數(shù)進(jìn)行一系列設(shè)置。
實(shí)現(xiàn)
首先,需要添加相對(duì)應(yīng)懸浮窗權(quán)限:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
在 Activity 頁(yè)面中,需要判斷懸浮窗權(quán)限是否獲取,如果未曾獲取,需要跳轉(zhuǎn)系統(tǒng)頁(yè),進(jìn)行對(duì)應(yīng)的授權(quán)操作:
public boolean requestOverlayPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!Settings.canDrawOverlays(this)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, REQUEST_OVERLAY_CODE);
return true;
} else {
return false;
}
}
return false;
}
獲取權(quán)限后,我們可以首先初始化我們要顯示的視圖,并且設(shè)置相對(duì)應(yīng)的事件,這里我們采用了 VideoView 進(jìn)行簡(jiǎn)單的視頻播放,并且設(shè)置懸浮窗關(guān)閉及返回前臺(tái)的操作:
private View initFloatView() {
View view = View.inflate(this, R.layout.view_floating_window, null);
// 設(shè)置視頻封面
final ImageView mThumb = (ImageView) view.findViewById(R.id.thumb_floating_view);
Glide.with(this).load(R.drawable.thumb).into(mThumb);
// 懸浮窗關(guān)閉
view.findViewById(R.id.close_floating_view).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mFloatingWindow.dismiss();
}
});
// 返回前臺(tái)頁(yè)面
view.findViewById(R.id.back_floating_view).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mFloatingWindow.setTopApp(FloatingWindowActivity.this);
}
});
final VideoView videoView = view.findViewById(R.id.video_view);
//視頻內(nèi)容設(shè)置
videoView.setVideoPath("https://stream7.iqilu.com/10339/article/202002/18/2fca1c77730e54c7b500573c2437003f.mp4");
// 視頻準(zhǔn)備完畢,隱藏正在加載封面,顯示視頻
videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
mThumb.setVisibility(View.GONE);
}
});
// 循環(huán)播放
videoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
videoView.start();
}
});
// 開始播放視頻
videoView.start();
return view;
}
通過獲取窗口管理 WindowManager ,設(shè)置懸浮窗口參數(shù) WindowManager.LayoutParams 后,就可以通過 WindowManager 的 addView 方法,生成對(duì)應(yīng)視圖的懸浮窗口:
public void showFloatingWindowView(Context context, View view) {
// 懸浮窗顯示視圖
mShowView = view;
// 獲取系統(tǒng)窗口管理服務(wù)
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
// 懸浮窗口參數(shù)設(shè)置及返回
mFloatParams = getParams();
// 設(shè)置窗口觸摸移動(dòng)事件
mShowView.setOnTouchListener(new FloatViewMoveListener());
// 懸浮窗生成
mWindowManager.addView(mShowView, mFloatParams);
}
WindowManager.LayoutParams 參數(shù)設(shè)置,主要設(shè)置懸浮窗口類型為 WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY :
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 = 600;
layoutParams.height = 340;
//設(shè)置懸浮窗顯示位置
layoutParams.gravity = Gravity.START | Gravity.TOP;
layoutParams.x = 100;
layoutParams.y = 100;
return layoutParams;
}
窗口觸摸移動(dòng)事件,主要通過獲取觸摸位置,通過 WindowManager 的 updateViewLayout 方法設(shè)置懸浮窗口的參數(shù),進(jì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;
}
}
關(guān)閉懸浮窗,調(diào)用 WindowManager 的 removeView 方法即可:
public void dismiss() {
if (mWindowManager != null && mShowView != null) {
mWindowManager.removeView(mShowView);
}
}
懸浮窗點(diǎn)擊返回前臺(tái)方法:
public void setTopApp(Context context) {
//獲取ActivityManager
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
//獲得當(dāng)前運(yùn)行的task(任務(wù))
List<ActivityManager.RunningTaskInfo> taskInfoList = null;
if (activityManager != null) {
taskInfoList = activityManager.getRunningTasks(100);
}
if (taskInfoList != null) {
for (ActivityManager.RunningTaskInfo taskInfo : taskInfoList) {
//找到本應(yīng)用的 task,并將它切換到前臺(tái)
if (taskInfo.topActivity != null && taskInfo.topActivity.getPackageName().equals(context.getPackageName())) {
activityManager.moveTaskToFront(taskInfo.id, 0);
break;
}
}
}
}
關(guān)于懸浮窗的一些基本操作到這里就基本結(jié)束了,具體的布局內(nèi)容及操作,歡迎查看具體的源碼實(shí)現(xiàn): Github開發(fā)記錄
到此這篇關(guān)于Android視頻懸浮窗口實(shí)現(xiàn)的示例代碼的文章就介紹到這了,更多相關(guān)Android視頻懸浮窗口 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android四種數(shù)據(jù)存儲(chǔ)的應(yīng)用方式
這篇文章主要介紹了Android四種數(shù)據(jù)存儲(chǔ)的應(yīng)用方式的相關(guān)資料,希望通過本文能幫助到大家,讓大家理解掌握Android存儲(chǔ)數(shù)據(jù)的方法,需要的朋友可以參考下2017-10-10
Android系統(tǒng)進(jìn)程間通信(IPC)機(jī)制Binder中的Server啟動(dòng)過程源代碼分析
本文主要介紹Android IPC機(jī)制Binder中的Server啟動(dòng)過程源代碼,這里對(duì)Binder 中Server 啟動(dòng)過程中的源碼做了詳細(xì)的介紹,有研究Android源碼 Binder 通信的小伙伴可以參考下2016-08-08
Flutter手機(jī)權(quán)限檢查與申請(qǐng)實(shí)現(xiàn)方法詳解
使用flutter進(jìn)行app開發(fā),一定會(huì)用到手機(jī)的部分權(quán)限,包括通知推送、定位、相冊(cè)、存儲(chǔ)、相機(jī)、麥克風(fēng)等。而權(quán)限的檢查和獲取,最受歡迎的就是通過permission_handler這個(gè)插件來實(shí)現(xiàn)2022-11-11
Android自定義動(dòng)態(tài)壁紙開發(fā)(時(shí)鐘)
今天小編就為大家分享一篇關(guān)于Android自定義動(dòng)態(tài)壁紙開發(fā)(時(shí)鐘),小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-01-01
android效果TapBarMenu繪制底部導(dǎo)航欄的使用方式示例
本篇文章主要介紹了android效果TapBarMenu繪制底部導(dǎo)航欄的使用方式,具有一定的參考價(jià)值,有興趣的可以了解一下。2017-01-01
Android6.0 固定屏幕功能實(shí)現(xiàn)方法及實(shí)例
這篇文章主要介紹了Android6.0 固定屏幕功能實(shí)現(xiàn)方法及實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-01-01
詳解Android冷啟動(dòng)實(shí)現(xiàn)APP秒開的方法
這篇文章給大家介紹的是Android冷啟動(dòng)實(shí)現(xiàn)APP秒開的方法,對(duì)大家日常開發(fā)APP還是很實(shí)用的,有需要的可以參考借鑒。2016-08-08

