在上篇文章Chrome Custom Tab(一)— 自定义款式与基本使用中,介绍了翻开网页、调整视图高度、调整地址栏款式、替换显示隐藏动画等功能。
本文介绍怎么增加功能按钮、菜单项,以及怎么装备预加载。
增加功能按钮
Custom Tab支持在地址栏中增加功能按钮,代码如下:
// 辅佐类
object CustomTabHelper {
private var customTabAvailablePackageName: String = ""
private var customTabsClient: CustomTabsClient? = null
private var customTabsServiceConnection: CustomTabsServiceConnection? = null
fun openCustomTabWithCustomActionButton(context: Context, url: String, actionButtonIcon: Bitmap, actionButtonLabel: String, pendingIntent: PendingIntent) {
val customTabsIntentBuilder = CustomTabsIntent.Builder(customTabsClient?.newSession(null))
// 设置按钮的图标、案牍以及点击图标时触发的PendingIntent
customTabsIntentBuilder.setActionButton(actionButtonIcon, actionButtonLabel, pendingIntent)
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 = "https://go.minigame.vip/"
private val ACTION_ID = "actionId"
private val ACTION_ID_SCAN = 1
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding: LayoutCustomTabActivityBinding = DataBindingUtil.setContentView(this, R.layout.layout_custom_tab_activity)
binding.btnCustomActionButton.setOnClickListener {
checkCustomTabAvailable()
ContextCompat.getDrawable(this, R.drawable.icon_scan_32_black)?.let {
CustomTabHelper.openCustomTabWithCustomActionButton(this, url, toBitmap(it), "Scan", createPendingIntent(ACTION_ID_SCAN))
}
}
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
intent?.run {
when (getIntExtra(ACTION_ID, -1)) {
ACTION_ID_SCAN -> showToast("click scan action button")
else -> showToast("Unknown action id")
}
}
}
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)
}
private fun toBitmap(drawable: Drawable): Bitmap {
val width = DensityUtil.dp2Px(24)
val height = DensityUtil.dp2Px(24)
val oldBounds = Rect(drawable.bounds)
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
drawable.setBounds(0, 0, width, height)
drawable.draw(Canvas(bitmap))
drawable.bounds = oldBounds
return bitmap
}
private fun createPendingIntent(actionId: Int): PendingIntent {
val actionIntent = Intent(applicationContext, CustomTabExampleActivity::class.java).apply {
putExtra(ACTION_ID, actionId)
}
return PendingIntent.getActivity(applicationContext, this.hashCode() + actionId, actionIntent, PendingIntent.FLAG_IMMUTABLE)
}
}
作用如图:
增加菜单项
Custom Tab支持在地址栏的溢出菜单中增加菜单项(至多5个),代码如下:
// 辅佐类
object CustomTabHelper {
fun openCustomTabWithCustomMenu(context: Context, url: String, menuItemParams: Map<String, PendingIntent>) {
val customTabsIntentBuilder = CustomTabsIntent.Builder(customTabsClient?.newSession(null))
menuItemParams.entries.forEach {
// 设置菜单项的案牍,以及点击菜单项时触发的PendingIntent
customTabsIntentBuilder.addMenuItem(it.key, it.value)
}
customTabsIntentBuilder.build().launchUrl(context, url.toUri())
}
}
// 示例Activity
class CustomTabExampleActivity : BaseGestureDetectorActivity() {
private val url = "https://go.minigame.vip/"
private val ACTION_ID = "actionId"
private val ACTION_ID_COPY_LINK = 2
private val ACTION_ID_SEARCH = 3
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding: LayoutCustomTabActivityBinding = DataBindingUtil.setContentView(this, R.layout.layout_custom_tab_activity)
binding.btnCustomMenu.setOnClickListener {
checkCustomTabAvailable()
CustomTabHelper.openCustomTabWithCustomMenu(this, url, mapOf(
Pair("Copy Link", createPendingIntent(ACTION_ID_COPY_LINK)),
Pair("Search In App", createPendingIntent(ACTION_ID_SEARCH))
))
}
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
intent?.run {
when (getIntExtra(ACTION_ID, -1)) {
ACTION_ID_COPY_LINK -> showToast("click copy link menu item")
ACTION_ID_SEARCH -> showToast("click search menu item")
else -> showToast("Unknown action id")
}
}
}
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)
}
private fun toBitmap(drawable: Drawable): Bitmap {
val width = DensityUtil.dp2Px(24)
val height = DensityUtil.dp2Px(24)
val oldBounds = Rect(drawable.bounds)
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
drawable.setBounds(0, 0, width, height)
drawable.draw(Canvas(bitmap))
drawable.bounds = oldBounds
return bitmap
}
private fun createPendingIntent(actionId: Int): PendingIntent {
val actionIntent = Intent(applicationContext, CustomTabExampleActivity::class.java).apply {
putExtra(ACTION_ID, actionId)
}
return PendingIntent.getActivity(applicationContext, this.hashCode() + actionId, actionIntent, PendingIntent.FLAG_IMMUTABLE)
}
}
作用如图:
预加载
Custom Tab支持预热浏览器,装备可能会拜访的网站,能够加快网页的翻开速度,使用户取得更好的体会,代码如下:
// 辅佐类
object CustomTabHelper {
private val mayLaunchUrl = arrayListOf("https://go.minigame.vip/", "https:///", "https://www.baidu.com/")
private var customTabAvailablePackageName: String = ""
private var customTabsClient: CustomTabsClient? = null
private var customTabsSession: CustomTabsSession? = null
private var customTabsServiceConnection: CustomTabsServiceConnection? = null
fun openSimpleCustomTab(context: Context, url: String) {
CustomTabsIntent.Builder(customTabsSession).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) {
// 预热浏览器,返回是否成功
client.warmup(0)
customTabsSession = client.newSession(null)
customTabsSession?.let {
var mainUri: Uri? = null
val otherLinkBundles = ArrayList<Bundle>()
for ((index, url) in mayLaunchUrl.withIndex()) {
if (index == 0) {
mainUri = url.toUri()
} else {
otherLinkBundles.add(Bundle().apply { putParcelable(CustomTabsService.KEY_URL, url.toUri()) })
}
}
// 装备可能会拜访的网站
// 设置一个主链接,能够通过Bundle List传入其他可能会翻开的链接
it.mayLaunchUrl(mainUri, null, otherLinkBundles)
}
customTabsClient = client
}
override fun onServiceDisconnected(name: ComponentName?) {
customTabsClient = null
customTabsSession = null
}
}
customTabsServiceConnection?.let {
CustomTabsClient.bindCustomTabsService(activity, customTabAvailablePackageName, it)
}
}
}
}
fun unbindCustomTabsService(activity: Activity) {
customTabsServiceConnection?.let { activity.unbindService(it) }
customTabsClient = null
customTabsSession = null
customTabsServiceConnection = null
}
}
// 示例Activity
class CustomTabExampleActivity : BaseGestureDetectorActivity() {
private val url = "https://go.minigame.vip/"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding: LayoutCustomTabActivityBinding = DataBindingUtil.setContentView(this, R.layout.layout_custom_tab_activity)
binding.btnOpenSimpleTab.setOnClickListener {
checkCustomTabAvailable()
CustomTabHelper.openSimpleCustomTab(this, url)
}
}
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)
}
}
作用如图:
无预加载 | 预加载 |
---|---|
视频转换为GIF后有点卡顿,可是开启预加载后,网页中的资源加载速度仍是有所提高的。
示例
在示例Demo中增加了相关的演示代码。
ExampleDemo github
ExampleDemo gitee