Android 通過Chrome Custom Tab開啟網頁
在日常開發中,偶爾會需要在App中開啟網頁,通常會使用WebView來實現。本文介紹一下另一種實現方式Chrome Custom Tab。
Chrome Custom Tab
Custom Tab是Chrome瀏覽器引入的一個功能,現在市面上大部分安卓裝置的瀏覽器都已經支援此功能。Custom Tab使App原生內容與網頁內容的過渡更加流暢,支援自定義部分樣式,可以保持與App一致的風格,支援預載入。
新增庫
在app module下的build.gradle中新增程式碼,如下:
dependencies {
implementation 'androidx.browser:browser:1.5.0'
}
檢查Custom Tab是否可用
儘管現在市面上大部分安卓裝置的瀏覽器都已支援Custom Tab,但為了確保部分裝置不支援該功能的情況下使用者體驗正常,可以先檢查當前裝置是否支援該功能,不支援的話仍然通過WebView實現。程式碼如下:
fun checkCustomTabAvailable(context: Context): Boolean {
val packageManager = context.packageManager
val browsableIntent = Intent().apply {
action = Intent.ACTION_VIEW
addCategory(Intent.CATEGORY_BROWSABLE)
data = Uri.fromParts("http", "", null)
}
// 獲取所有瀏覽器
val browsableResolverInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
packageManager.queryIntentActivities(browsableIntent, PackageManager.ResolveInfoFlags.of(0))
} else {
packageManager.queryIntentActivities(browsableIntent, 0)
}
val supportingCustomTabResolveInfo = ArrayList<ResolveInfo>()
browsableResolverInfo.forEach {
val serviceIntent = Intent().apply {
action = androidx.browser.customtabs.CustomTabsService.ACTION_CUSTOM_TABS_CONNECTION
setPackage(it.activityInfo.packageName)
}
val customTabServiceResolverInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
packageManager.resolveService(serviceIntent, PackageManager.ResolveInfoFlags.of(0))
} else {
packageManager.resolveService(serviceIntent, 0)
}
// 判斷是否可以處理Custom Tabs service
if (customTabServiceResolverInfo != null) {
supportingCustomTabResolveInfo.add(it)
}
}
return supportingCustomTabResolveInfo.isNotEmpty()
}
開啟網頁
通過Custom Tab開啟網頁程式碼如下:
fun openCustomTab(context: Context, url: String) {
// url 為要開啟的網址
CustomTabsIntent.Builder().build().launchUrl(context, url.toUri())
}
需要注意的是,不能通過此方式開啟Assets下的H5檔案。
調整UI
Custom Tab支援自定義部分樣式。
調整檢視高度
可以使用CustomTabsIntent.Builder
中的setInitialActivityHeightPx
方法來調整開啟的Custom Tab的高度,同時可以使用setToolbarCornerRadiusDp
來設定圓角。具體實現方式有如下兩種:
-
- 連線Custom Tab Service(建議使用此方式)。
``` // 輔助類 object CustomTabHelper {
// Custom Tab 可用的包名
private var customTabAvailablePackageName: String = ""
private var customTabsClient: CustomTabsClient? = null
private var customTabsServiceConnection: CustomTabsServiceConnection? = null
fun openCustomTabWithInitialHeight(context: Context, url: String, activityHeight: Int = 0, radius: Int = 0, adjustable: Boolean = false) {
val customTabsIntentBuilder = CustomTabsIntent.Builder(customTabsClient?.newSession(null))
if (activityHeight != 0) {
// 第二個引數配置預期行為
// ACTIVITY_HEIGHT_ADJUSTABLE 使用者可以手動調整檢視高度
// ACTIVITY_HEIGHT_FIXED 使用者無法手動調整檢視高度
customTabsIntentBuilder.setInitialActivityHeightPx(activityHeight, if (adjustable) CustomTabsIntent.ACTIVITY_HEIGHT_ADJUSTABLE else CustomTabsIntent.ACTIVITY_HEIGHT_FIXED)
if (radius != 0) {
customTabsIntentBuilder.setToolbarCornerRadiusDp(radius)
}
}
customTabsIntentBuilder.build().launchUrl(context, url.toUri())
}
fun checkCustomTabAvailable(context: Context): Boolean {
val packageManager = context.packageManager
val browsableIntent = Intent().apply {
action = Intent.ACTION_VIEW
addCategory(Intent.CATEGORY_BROWSABLE)
data = Uri.fromParts("http", "", null)
}
// 獲取所有瀏覽器
val browsableResolverInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
packageManager.queryIntentActivities(browsableIntent, PackageManager.ResolveInfoFlags.of(0))
} else {
packageManager.queryIntentActivities(browsableIntent, 0)
}
val supportingCustomTabResolveInfo = ArrayList<ResolveInfo>()
browsableResolverInfo.forEach {
val serviceIntent = Intent().apply {
action = androidx.browser.customtabs.CustomTabsService.ACTION_CUSTOM_TABS_CONNECTION
setPackage(it.activityInfo.packageName)
}
val customTabServiceResolverInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
packageManager.resolveService(serviceIntent, PackageManager.ResolveInfoFlags.of(0))
} else {
packageManager.resolveService(serviceIntent, 0)
}
// 判斷是否可以處理Custom Tabs service
if (customTabServiceResolverInfo != null) {
supportingCustomTabResolveInfo.add(it)
}
}
if (supportingCustomTabResolveInfo.isNotEmpty()) {
customTabAvailablePackageName = supportingCustomTabResolveInfo[0].activityInfo.packageName
}
return supportingCustomTabResolveInfo.isNotEmpty()
}
fun bindCustomTabsService(activity: Activity) {
if (checkCustomTabAvailable(activity)) {
if (customTabsClient == null) {
customTabsServiceConnection = object : CustomTabsServiceConnection() {
override fun onCustomTabsServiceConnected(name: ComponentName, client: CustomTabsClient) {
customTabsClient = client
}
override fun onServiceDisconnected(name: ComponentName?) {
customTabsClient = null
}
}
customTabsServiceConnection?.let {
CustomTabsClient.bindCustomTabsService(activity, customTabAvailablePackageName, it)
}
}
}
}
fun unbindCustomTabsService(activity: Activity) {
customTabsServiceConnection?.let { activity.unbindService(it) }
customTabsClient = null
customTabsServiceConnection = null
}
}
// 示例Activity class CustomTabExampleActivity : BaseGestureDetectorActivity() {
private val url = "http://go.minigame.vip/"
private var activityHeight = 0
private var topRadius = 16
private var changeHeightAdjustable = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding: LayoutCustomTabActivityBinding = DataBindingUtil.setContentView(this, R.layout.layout_custom_tab_activity)
activityHeight = (resources.displayMetrics.heightPixels * 0.8).toInt()
binding.btnChangeHeightFixed.setOnClickListener {
checkCustomTabAvailable()
changeHeightAdjustable = false
CustomTabHelper.openCustomTabWithInitialHeight(this, url, activityHeight, topRadius, changeHeightAdjustable)
}
binding.btnChangeHeightAdjustable.setOnClickListener {
checkCustomTabAvailable()
changeHeightAdjustable = true
CustomTabHelper.openCustomTabWithInitialHeight(this, url, activityHeight, topRadius, changeHeightAdjustable)
}
}
private fun checkCustomTabAvailable() {
if (!CustomTabHelper.checkCustomTabAvailable(this)) {
startActivity(Intent(this, WebViewActivity::class.java).apply { putExtra(PARAMS_LINK_URL, url) })
return
}
}
override fun onStart() {
super.onStart()
CustomTabHelper.bindCustomTabsService(this)
}
override fun onDestroy() {
super.onDestroy()
CustomTabHelper.unbindCustomTabsService(this)
}
} ```
-
- 使用startActivityForResult
``` class CustomTabExampleActivity : BaseGestureDetectorActivity() {
private val url = "http://go.minigame.vip/"
private var activityHeight = 0
private var topRadius = 16
private var changeHeightAdjustable = false
private val customTabLauncher = registerForActivityResult(object : ActivityResultContract<String, Int>() {
override fun createIntent(context: Context, input: String): Intent {
val customTabsIntentBuilder = CustomTabsIntent.Builder()
.setInitialActivityHeightPx(activityHeight, if (changeHeightAdjustable) CustomTabsIntent.ACTIVITY_HEIGHT_ADJUSTABLE else CustomTabsIntent.ACTIVITY_HEIGHT_FIXED)
.setToolbarCornerRadiusDp(topRadius)
return customTabsIntentBuilder.build().intent.apply {
data = input.toUri()
}
}
override fun parseResult(resultCode: Int, intent: Intent?): Int {
return resultCode
}
}) {
// 頁面返回回撥
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding: LayoutCustomTabActivityBinding = DataBindingUtil.setContentView(this, R.layout.layout_custom_tab_activity)
activityHeight = (resources.displayMetrics.heightPixels * 0.8).toInt()
binding.btnChangeHeightFixed.setOnClickListener {
checkCustomTabAvailable()
changeHeightAdjustable = false
customTabLauncher.launch(url)
}
binding.btnChangeHeightAdjustable.setOnClickListener {
checkCustomTabAvailable()
changeHeightAdjustable = true
customTabLauncher.launch(url)
}
}
private fun checkCustomTabAvailable() {
if (!CustomTabHelper.checkCustomTabAvailable(this)) {
startActivity(Intent(this, WebViewActivity::class.java).apply { putExtra(PARAMS_LINK_URL, url) })
return
}
}
} ```
效果如圖:
調整位址列
可以對Custom Tab的位址列進行一些配置,程式碼如下:
``` // 輔助類 object CustomTabHelper {
fun openCustomTabWithCustomUI(context: Context, url: String, @ColorInt color: Int = 0, showTitle: Boolean = false, autoHide: Boolean = false, backIconPosition: Int = CustomTabsIntent.CLOSE_BUTTON_POSITION_START) {
val customTabsIntentBuilder = CustomTabsIntent.Builder(customTabsClient?.newSession(null))
if (color != 0) {
// 設定背景顏色
customTabsIntentBuilder.setDefaultColorSchemeParams(CustomTabColorSchemeParams.Builder()
.setToolbarColor(color)
.build())
}
// 是否顯示標題
customTabsIntentBuilder.setShowTitle(showTitle)
// 位址列是否自動隱藏 ,此配置僅在Custom Tab全屏顯示時生效
customTabsIntentBuilder.setUrlBarHidingEnabled(autoHide)
// 調整關閉按鈕的位置
// CustomTabsIntent.CLOSE_BUTTON_POSITION_START 在位址列的左側
// CustomTabsIntent.CLOSE_BUTTON_POSITION_END 在位址列的右側
customTabsIntentBuilder.setCloseButtonPosition(backIconPosition)
customTabsIntentBuilder.build().launchUrl(context, url.toUri())
}
}
// 示例Activity class CustomTabExampleActivity : BaseGestureDetectorActivity() {
private val url = "http://go.minigame.vip/"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding: LayoutCustomTabActivityBinding = DataBindingUtil.setContentView(this, R.layout.layout_custom_tab_activity)
binding.btnCustomUi.setOnClickListener {
checkCustomTabAvailable()
CustomTabHelper.openCustomTabWithCustomUI(this, url, ContextCompat.getColor(this, R.color.color_FF2600), showTitle = true, autoHide = true, CustomTabsIntent.CLOSE_BUTTON_POSITION_END)
}
}
} ```
效果如圖:
調整顯示隱藏動畫
當Custom Tab為全屏顯示時,可以調整顯示與隱藏時的動畫,程式碼如下:
```
// slide_in_right
// slide_out_left
// 輔助類 object CustomTabHelper { fun openCustomTabWithCustomAnimations(context: Context, url: String) { val customTabsIntentBuilder = CustomTabsIntent.Builder(customTabsClient?.newSession(null)) // 自定義動畫 customTabsIntentBuilder.setStartAnimations(context, R.anim.slide_in_right, R.anim.slide_out_left) // 系統動畫 customTabsIntentBuilder.setExitAnimations(context, android.R.anim.slide_in_left, android.R.anim.slide_out_right) customTabsIntentBuilder.build().launchUrl(context, url.toUri()) } }
// 示例Activity class CustomTabExampleActivity : BaseGestureDetectorActivity() {
private val url = "http://go.minigame.vip/"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding: LayoutCustomTabActivityBinding = DataBindingUtil.setContentView(this, R.layout.layout_custom_tab_activity)
binding.btnCustomAnimations.setOnClickListener {
checkCustomTabAvailable()
CustomTabHelper.openCustomTabWithCustomAnimations(this, url)
}
}
} ```
效果如圖:
示例Demo
在示例Demo中添加了相關的演示程式碼。
- Android 通過Chrome Custom Tab開啟網頁
- Android 通過MotionLayot實現點贊動畫
- Android FCM接入
- Android 接入Google Tag Manager
- Android 一種點贊動畫的實現
- Android 搜尋框架使用
- Android WebView JS互動 傳Json格式引數問題
- 以往專案中的壓縮apk經驗
- Android 字型下載
- Android 全屏顯示和沉浸式顯示
- Android Google支付接入
- Android 自定義Gradle外掛(八):檢查Manifest中的許可權
- Android 自定義View ——漸變色折線圖
- Android Activity Result API (二) :拍照與選擇照片
- Android 自定義Gradle外掛(七):關於多渠道打包
- Android 自定義Gradle外掛(六):打包時修改assets中的檔案
- Android 自定義Gradle外掛(四):在專案的資料夾中建立檔案
- 使用Nexus搭建maven私有庫
- 記windows下 AndroidStudio 安裝配置過程,以及最新版AndroidStuido遇到的一個問題