版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
【移動應(yīng)用開發(fā)技術(shù)】Android如何實現(xiàn)通話最小化懸浮框效果
這篇文章主要介紹Android如何實現(xiàn)通話最小化懸浮框效果,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!一、實現(xiàn)效果(gif效果可能錄制的不是特別好)二、實現(xiàn)思路關(guān)于這個功能的實現(xiàn)其實不難,這里我把實現(xiàn)思路拆分為了兩步:1、視頻通話Activity的最小化。2、視頻通話懸浮框的開啟具體思路是這樣的:當(dāng)用戶點擊最小化按鈕的時候,最小化我們的視頻通話Activity(這時Activity處于后臺狀態(tài)),移除原先在Activity的視頻畫布(因為我用的是網(wǎng)易云信,這里他們只能允許一個視頻畫布存在,這里看情況要不要移除),于此同時,延時個幾百毫秒,開啟懸浮框,新建一個新的視頻畫布然后動態(tài)添加到懸浮框里面去,監(jiān)聽?wèi)腋】虻挠|摸事件,讓懸浮框可以拖拽移動;監(jiān)聽?wèi)腋】虻狞c擊事件,如果用戶點擊了懸浮框,則移除懸浮框里面新建的那個視頻畫布,然后重新調(diào)起我們在后臺的視頻通話Activity,緊接著新建一個新的視頻畫布重新動態(tài)的添加到Activity里面去。關(guān)于視頻畫布的添加移除方法,這里要看一下所接入的第三方SDK,如用的若是網(wǎng)易云信的SDK,他們的方法如下(下面摘自他們的SDK說明文檔),也就是說移除畫布我只需要傳入null就行了。1.Activity是如何實現(xiàn)最小化的?Activity最小化可能你沒有聽過,但是只要姿勢對的話,其實實現(xiàn)起來非常簡單,因為Activity本身就自帶了一個moveTaskToBack(booleannonRoot),如果我們要實現(xiàn)最小化,只需要調(diào)用moveTaskToBack(true)傳入一個true值就可以了,但是這里有一個前提,就是需要設(shè)置Activity的啟動模式為singleInstance模式,兩步搞定。(注:這里先記住一個小知識點,就是activity最小化后重新從后臺回到前臺會回調(diào)onRestart()方法)@Override
public
boolean
moveTaskToBack(boolean
nonRoot)
{
return
super.moveTaskToBack(nonRoot);
}2.懸浮框是如何開啟的?這里我把懸浮框的實現(xiàn)方法寫在一個服務(wù)Service里面,將懸浮框的開啟關(guān)閉與服務(wù)Service的綁定解綁所關(guān)聯(lián)起來,開啟服務(wù)即相當(dāng)于開啟我們的懸浮框,解綁服務(wù)則相當(dāng)于關(guān)閉關(guān)閉的懸浮框,以此來達到更好的控制效果。a.首先我們聲明一個服務(wù)類,取名為FloatVideoWindowService:public
class
FloatVideoWindowService
extends
Service
{
@Nullable
@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();
}
@Override
public
int
onStartCommand(Intent
intent,
int
flags,
int
startId)
{
return
super.onStartCommand(intent,
flags,
startId);
}
@Override
public
void
onDestroy()
{
super.onDestroy();
}
}b.為懸浮框建立一個布局文件alert_float_video_layout,這里根據(jù)需求去寫,如果只是像我上面gif那樣,只需要懸浮框顯示對方的視頻畫布,那么布局文件可以如下所示:(其中懸浮框大小我這里固定為長80dp,高110dp,id為small_size_preview的Linearlayout主要是一個容器,可以動態(tài)的添加view到里面去,也就是我們的視頻畫布)<?xml
version="1.0"
encoding="utf-8"?>
<LinearLayout
xmlns:android="/apk/res/android"
xmlns:app="/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<FrameLayout
android:layout_width="80dp"
android:layout_height="110dp"
android:background="@color/black_1f2d3d">
<LinearLayout
android:id="@+id/small_size_preview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/transparent"
android:orientation="vertical"
/>
</FrameLayout>
</LinearLayout>c.布局定義好后,接下來就要對懸浮框做一些初始化操作了,初始化操作這里我們放在服務(wù)的onCreate()生命周期里面執(zhí)行,因為只需要執(zhí)行一次就行了。這里的初始化主要包括對:懸浮框的基本參數(shù)(位置,寬高等),懸浮框的點擊事件以及懸浮框的觸摸事件(即可拖動范圍)等的設(shè)置,代碼注釋已經(jīng)很清楚,直接看代碼,如下所示:public
class
FloatVideoWindowService
extends
Service
{
private
WindowManager
mWindowManager;
private
WindowManager.LayoutParams
wmParams;
private
LayoutInflater
inflater;
//constant
private
boolean
clickflag;
//view
private
View
mFloatingLayout;
//浮動布局
private
LinearLayout
smallSizePreviewLayout;
//容器父布局
@Nullable
@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();//設(shè)置懸浮窗基本參數(shù)(位置、寬高等)
initFloating();//懸浮框點擊事件的處理
}
@Override
public
int
onStartCommand(Intent
intent,
int
flags,
int
startId)
{
return
super.onStartCommand(intent,
flags,
startId);
}
@Override
public
void
onDestroy()
{
super.onDestroy();
}
/**
*
設(shè)置懸浮框基本參數(shù)(位置、寬高等)
*/
private
void
initWindow()
{
mWindowManager
=
(WindowManager)
getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
wmParams
=
getParams();//設(shè)置好懸浮窗的參數(shù)
//
懸浮窗默認顯示以左上角為起始坐標(biāo)
wmParams.gravity
=
Gravity.LEFT
|
Gravity.TOP;
//懸浮窗的開始位置,因為設(shè)置的是從左上角開始,所以屏幕左上角是x=0;y=0
wmParams.x
=
70;
wmParams.y
=
210;
//得到容器,通過這個inflater來獲得懸浮窗控件
inflater
=
LayoutInflater.from(getApplicationContext());
//
獲取浮動窗口視圖所在布局
mFloatingLayout
=
inflater.inflate(R.layout.alert_float_video_layout,
null);
//
添加懸浮窗的視圖
mWindowManager.addView(mFloatingLayout,
wmParams);
}
private
WindowManager.LayoutParams
getParams()
{
wmParams
=
new
WindowManager.LayoutParams();
//設(shè)置window
type
下面變量2002是在屏幕區(qū)域顯示,2003則可以顯示在狀態(tài)欄之上
wmParams.type
=
WindowManager.LayoutParams.TYPE_TOAST;
//設(shè)置可以顯示在狀態(tài)欄上
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;
//設(shè)置懸浮窗口長寬數(shù)據(jù)
wmParams.width
=
WindowManager.LayoutParams.WRAP_CONTENT;
wmParams.height
=
WindowManager.LayoutParams.WRAP_CONTENT;
return
wmParams;
}
private
void
initFloating()
{
smallSizePreviewLayout
=
mFloatingLayout.findViewById(R.id.small_size_preview);
//懸浮框點擊事件
smallSizePreviewLayout.setOnClickListener(new
View.OnClickListener()
{
@Override
public
void
onClick(View
v)
{
//在這里實現(xiàn)點擊重新回到Activity
}
});
//懸浮框觸摸事件,設(shè)置懸浮框可拖動
smallSizePreviewLayout.setOnTouchListener(new
FloatingListener());
}
//開始觸控的坐標(biāo),移動時的坐標(biāo)(相對于屏幕左上角的坐標(biāo))
private
int
mTouchStartX,
mTouchStartY,
mTouchCurrentX,
mTouchCurrentY;
//開始時的坐標(biāo)和結(jié)束時的坐標(biāo)(相對于自身控件的坐標(biāo))
private
int
mStartX,
mStartY,
mStopX,
mStopY;
//判斷懸浮窗口是否移動,這里做個標(biāo)記,防止移動后松手觸發(fā)了點擊事件
private
boolean
isMove;
private
class
FloatingListener
implements
View.OnTouchListener
{
@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:
mTouchCurrentX
=
(int)
event.getRawX();
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:
mStopX
=
(int)
event.getX();
mStopY
=
(int)
event.getY();
if
(Math.abs(mStartX
-
mStopX)
>=
1
||
Math.abs(mStartY
-
mStopY)
>=
1)
{
isMove
=
true;
}
break;
}
//如果是移動事件不觸發(fā)OnClick事件,防止移動的時候一放手形成點擊事件
return
isMove;
}
}
}d.在懸浮框成功被初始化以及相關(guān)參數(shù)被設(shè)置后,接下來就需要將對方的視頻畫布添加到懸浮框里面去了,這樣我們才能看到對方的視頻畫面嘛,同樣我們是在Service的oncreate這個生命周期完成這個操作的,這里視頻畫布的添加方式使用的網(wǎng)易云信的SDK,具體的添加方式視不同的SDK而定,代碼如下所示:/**
*
初始化預(yù)覽窗口
*/
private
void
initSurface()
{
if
(smallRender
==
null)
{
smallRender
=
new
AVChatSurfaceViewRenderer(getApplicationContext());
}
addIntoSmallSizePreviewLayout(smallRender);
}
/**
*
添加surfaceview到smallSizePreviewLayout
*/
private
void
addIntoSmallSizePreviewLayout(SurfaceView
surfaceView)
{
if
(surfaceView.getParent()
!=
null)
{
((ViewGroup)
surfaceView.getParent()).removeView(surfaceView);
}
smallSizePreviewLayout.addView(surfaceView);
surfaceView.setZOrderMediaOverlay(true);
}e.我們上面說到要將服務(wù)service的綁定與解綁與懸浮框的開啟和關(guān)閉相結(jié)合,所以既然我們在服務(wù)的oncreate()方法中開啟了懸浮框,那么就應(yīng)該在其ondestroy()方法中對懸浮框進行關(guān)閉,關(guān)閉懸浮框的本質(zhì)是將相關(guān)view給移除掉,接著清除我們的視頻畫布,在服務(wù)的ondestroy()方法中執(zhí)行如下代碼:@Override
public
void
onDestroy()
{
super.onDestroy();
if
(mFloatingLayout
!=
null)
{
//
移除懸浮窗口
mWindowManager.removeView(mFloatingLayout);
}
//清除視頻畫布
AVChatManager.getInstance().setupRemoteVideoRender(account,
null,
false,
0);
}f.服務(wù)的綁定方式有bindService和startService兩種,使用不同的綁定方式其生命周期也會不一樣,已知我們需要讓懸浮框在視頻通話activityfinish掉的時候也順便關(guān)掉,那么理所當(dāng)然我們就應(yīng)該采用bind方式來啟動服務(wù),讓他的生命周期跟隨他的開啟者,也即是跟隨開啟它的activity生命周期。intent
=
new
Intent(this,
FloatVideoWindowService.class);//開啟服務(wù)顯示懸浮框
bindService(intent,
mVideoServiceConnection,
Context.BIND_AUTO_CREATE);
ServiceConnection
mVideoServiceConnection
=
new
ServiceConnection()
{
@Override
public
void
onServiceConnected(ComponentName
name,
IBinder
service)
{
//
獲取服務(wù)的操作對象
FloatVideoWindowService.MyBinder
binder
=
(FloatVideoWindowService.MyBinder)
service;
binder.getService();
}
@Override
public
void
onServiceDisconnected(ComponentName
name)
{
}
};三、完整的流程現(xiàn)在我們將上面所說的給串聯(lián)起來,思路會更加清晰一點,假設(shè)現(xiàn)在我正在進行視頻通話,點擊視頻最小化按鈕,我們應(yīng)該按順序執(zhí)
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 汽車模具2025版性能優(yōu)化開發(fā)合同
- 2025年度木材出口合同范本與執(zhí)行細則4篇
- 2025版學(xué)校小賣部與校園周邊商家聯(lián)盟合同3篇
- 2025版建筑設(shè)備安裝工程安全生產(chǎn)消防合同3篇
- 2025版外語教學(xué)機構(gòu)兼職外教招聘合同樣本3篇
- 2025年人力資源服務(wù)合同解除協(xié)議
- 2025年前雇主員工競業(yè)禁止合同樣本模板
- 2025版?zhèn)€人合伙退伙協(xié)議書糾紛處理指南4篇
- 2025年云石打邊蠟水項目投資可行性研究分析報告
- 2025年度駱采與陳鵬的離婚財產(chǎn)分割及子女撫養(yǎng)權(quán)合同4篇
- GB/T 45107-2024表土剝離及其再利用技術(shù)要求
- 2024-2025學(xué)年八年級上學(xué)期1月期末物理試題(含答案)
- 商場電氣設(shè)備維護勞務(wù)合同
- 2023年國家公務(wù)員錄用考試《行測》真題(行政執(zhí)法)及答案解析
- 2024智慧醫(yī)療數(shù)據(jù)字典標(biāo)準值域代碼
- 年產(chǎn)12萬噸裝配式智能鋼結(jié)構(gòu)項目可行性研究報告模板-立項備案
- 【獨家揭秘】2024年企業(yè)微信年費全解析:9大行業(yè)收費標(biāo)準一覽
- 醫(yī)療器械經(jīng)銷商會議
- 《±1100kV特高壓直流換流變壓器使用技術(shù)條件》
- 1-1 擁抱夢想:就這樣埋下一顆種子【2022中考作文最熱8主題押題24道 構(gòu)思點撥+范文點評】
- 《風(fēng)電場項目經(jīng)濟評價規(guī)范》(NB-T 31085-2016)
評論
0/150
提交評論