最近在用compose重构公司的主要APP,由于compose的一些独特设计理念,开发中也遇到很多问题,大多数甚至网上找不到相关资料,只好自己查阅有些晦涩难懂的官方文档,摸索着解决,并记录下来,有需要的可以提供参考。
以前实现webView文件选择都是用Activity.startActivityForResult 和 onActivityResult回调,但是这两个方法由于存在安全问题,现在官方准备弃用,不推荐使用了,推荐使用registerForActivityResult 和 ActivityResultLauncher.lauch()方法来实现
一、compose中使用webView
这部分内容不是本篇文章重点,就不展开介绍了,可以参考网上其他文章,例如:Jetpack Compose - WebView 使用方法,或者直接看wanAndroid大佬的compose版wanAndroid的源码,文章地址:Compose+MVI+Navigation实现wanAndroid客户端,结构非常清楚
二、配置webSettings
注意要开启javaScriptEnabled
@SuppressLint("SetJavaScriptEnabled")
private fun setWebSettings() {
val webSettings = webView.settings
//如果访问的页面中要与Javascript交互,则webview必须设置支持Javascript
webSettings.javaScriptEnabled = true
//设置自适应屏幕,两者合用
webSettings.useWideViewPort = true //将图片调整到适合webview的大小
webSettings.loadWithOverviewMode = true // 缩放至屏幕的大小
//缩放操作
webSettings.setSupportZoom(true) //支持缩放,默认为true。是下面那个的前提。
webSettings.builtInZoomControls = true //设置内置的缩放控件。若为false,则该WebView不可缩放
webSettings.displayZoomControls = false //隐藏原生的缩放控件
webSettings.domStorageEnabled = true // 开启 DOM storage API 功能
//其他细节操作
webSettings.cacheMode = WebSettings.LOAD_CACHE_ELSE_NETWORK //关闭webview中缓存
webSettings.allowFileAccess = true //设置可以访问文件
webSettings.javaScriptCanOpenWindowsAutomatically = true //支持通过JS打开新窗口
webSettings.loadsImagesAutomatically = true //支持自动加载图片
webSettings.defaultTextEncodingName = "UTF-8"//设置编码格式
}
三、配置WebViewChromeClient
注意:重写onShowFileChooser()方法,该方法在Android5.0以前还有两个老版本,但是我项目minSDK > 23所以就重写这一个版本就好了
private var uploadMessageAboveL: ValueCallback<Array<Uri>>? = null
inner class ProgressWebViewChromeClient : WebChromeClient() {
override fun onProgressChanged(view: WebView?, newProgress: Int) {
super.onProgressChanged(view, newProgress)
progressBar.progress = newProgress
}
override fun onReceivedTitle(view: WebView?, title: String?) {
super.onReceivedTitle(view, title)
}
override fun onShowFileChooser(
webView: WebView?,
filePathCallback: ValueCallback<Array<Uri>>?,
fileChooserParams: FileChooserParams?
): Boolean {
uploadMessageAboveL = filePathCallback
MyApp.imageResultLauncher.launch("image/*")
return true
}
}
四、注册ResultLauncher
我是在Application中创建和持有,在Activity的onCreate中注册
由于框架结构性能等原因,compose推荐只有一个Activity,我的项目中因为需要集成微信登陆,所以只有一个WxEntryActivity作为主Activity
@HiltAndroidApp
class MyApp: Application() {
companion object {
@SuppressLint("StaticFieldLeak")
lateinit var CONTEXT: Context
//创建ActivityResultLauncher,用于接收onActivityResult的回调信息
lateinit var imageResultLauncher: ActivityResultLauncher<String>
//将ActivityResultLauncher的回调信息传递给MutableLiveData,并在web组件中监听该LiveData
val imageResult: MutableLiveData<List<Uri>> = MutableLiveData()
}
override fun onCreate() {
super.onCreate()
CONTEXT = this
}
}
@AndroidEntryPoint
class WXEntryActivity : ComponentActivity() {
companion object {
const val TAG = "WXEntryActivity"
//因为只有这一个Activity,所以可以作为全局的lifeCycleOwner
lateinit var instant: WXEntryActivity
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
...
}
instant = this
//注册图片选择器
imageResultLauncher = registerForActivityResult(ActivityResultContracts.GetMultipleContents()) {
//获取到了图片,传递给LiveData
LogUtils.i("WXEntryActivity", "resultLauncher: $it")
imageResult.value = it
}
}
}
五、创建result的LiveData或者flow来设置(发送)和观察(接受)数据
在上一步代码中可以看到,我也是在Application中创建和持有imageResult这个LiveData
然后在注册的图片选择器结果回调中更新数据
最后在webViewCtrl中监听数据变化,并调用uploadMessageAboveL 的 onReceiveValue 回调。其中uploadMessageAboveL 是啥不清楚的见第三步,在onShowFileChooser中将filePathCallBack引用给它
private fun initObserver() {
LogUtils.d("WebViewCtrl", "initObserver")
MyApp.imageResult.observe(WXEntryActivity.instant){
LogUtils.d("WebViewCtrl", "observe: $it")
uploadMessageAboveL?.onReceiveValue(it.toTypedArray())
}
}