??????Android SDK 提供了Display類,實現(xiàn)在主屏幕之外的擴展屏幕上顯示不同于主屏幕的UI,而擴展屏幕上的UI顯示,實質(zhì)上是顯示了一個系統(tǒng)級別的Dialog,我們可以將自已的View加入到此Dialog中進行顯示。
??????擴展屏可以有一個或超過一個,實際的應(yīng)用中需要底層驅(qū)動的支持。在官方的模擬器中我們可以模擬出一個副屏,進行調(diào)試(當然除了模擬器,我們的手機也可以進行模擬)
??????以Android10官方模擬器為例,來說明如何進行雙屏異顯的調(diào)試。
一、設(shè)置Simulate secondary displays
??????模擬器中打開"設(shè)置-開發(fā)者選項"界面,在列表中找到"Simulate secondary displays"條目。
點擊后,在彈出的對話框中選擇副屏的分辨率:

??????比如我們選中720p,模擬器屏幕的左上角上會立即呈現(xiàn)出副屏的窗口,其顯示內(nèi)容默認為與主屏顯示一致:

??????副屏窗口就是一個Dialog,可以拖動,讓其顯示在合適的位置。
二、實現(xiàn)副屏異顯
??????因為副屏默認顯示與主屏一致,如果要實現(xiàn)雙屏異顯,我們需要在APP中進行開發(fā),將自定義布局加入到副屏中進行顯示。
1、申請權(quán)限
AndroidManifest.xml中添加兩個權(quán)限:
<uses-permission android:name= "android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name= "android.permission.SYSTEM_OVERLAY_WINDOW"/>
因為android 6.0及之后 android.permission.SYSTEM_OVERLAY_WINDOW需要系統(tǒng)動態(tài)授權(quán)才能啟用,所以在APP啟動時加入如下代碼進行動態(tài)授權(quán):
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
//啟動Activity讓用戶授權(quán)
if (!Settings.canDrawOverlays(this)) {
val intent: Intent = Intent(
Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse( "package:$packageName"))
startActivityForResult(intent, 1010)
}
}
系統(tǒng)會彈出授權(quán)窗口,開啟即可:

2、獲取副屏的Display對象
//獲取顯示管理服務(wù)代理對象
val displayManger = activity.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager?
//當前副屏的Display
private var currentDisplay: Display? = null
displayManger?.run {
//取所有副屏對應(yīng)的Display列表
val displays = getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION)
if (!displays.isEmpty()){
val display = displays.find { it.isValid } //取第一個有效的Display
//無副屏到接入新的副屏
if (currentDisplay == null) {
//保存副屏的Display
currentDisplay = display
}
}
}
第一步"設(shè)置Simulate secondary displays"中我們已經(jīng)設(shè)置過副屏了,所以currentDisplay不會為空。
3、初始化Context與WindowManager
//副屏Context
private var presentationContext: Context? = null
//副屏窗口管理器
private var presentationWindowManager:WindowManager? = null
val displayContext: Context = activity.createDisplayContext(display)
val wm = displayContext.getSystemService(WINDOW_SERVICE) as WindowManager
presentationContext = object :ContextThemeWrapper(displayContext, android.R.style.Theme){
override fun getSystemService(name: String): Any {
if (WINDOW_SERVICE == name) {
return(wm)
}
return super.getSystemService(name)
}
}
presentationWindowManager =
(presentationContext as ContextThemeWrapper).getSystemService(Context.WINDOW_SERVICE) as WindowManager
4、向副屏添加View
實現(xiàn)一個只顯示純藍色背景的布局:default.xml
<?xml version="1.0" encoding="utf-8"?>
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_blue_dark"/>
以下是向副屏窗口加入View的參考代碼:
//通過布局id生成View
val inflater = LayoutInflater.from(presentationContext!!)
val view = inflater.inflate(R.layout.default, null)
//最終副屏?xí)@示default.xml的內(nèi)容
presentationWindowManager?.addView(view,buildLayoutParams())
buildLayoutParams函數(shù):
@SuppressLint("InlinedApi")
private fun buildLayoutParams():WindowManager.LayoutParams {
if(Build.VERSION.SDK_INT >= 26){
return WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
0,
0,
TYPE_APPLICATION_OVERLAY, //FIRST_SYSTEM_WINDOW + 38
0, PixelFormat.TRANSPARENT
)
} else {
return WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
0,
0,
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
0, PixelFormat.TRANSPARENT
)
}
}
說明一下:
Android8.0及以后只能使用TYPE_APPLICATION_OVERLAY窗口類型來創(chuàng)建懸浮窗。(其它窗口類型在8.0已經(jīng)被廢棄掉)
看一下最終的效果:

副屏還可以顯示其它更復(fù)雜的布局,也可以顯示圖片與播放視頻,如下是播放視頻的截圖:

最后的問題:
副屏是否支持觸摸?
目前我了解所知,原生并不支持副屏觸摸動作,但是修改源碼是可以達到的(主要是修改觸控事件的向上分發(fā)邏輯,因為分發(fā)時只有一個主屏)。
可以參考:http://m.itdecent.cn/p/572eaa37ddd3
另外Android自帶一個系統(tǒng)組件Presentation,可以簡化多屏異顯,其實質(zhì)也是封裝了上面的過程。