相关文章
【Android WebView】仿微信加载H5页面进度条实现
【Android WebView】Android和JS互调,BridgeWebView的使用
前言
Android
中通过WebView
和H5
页面进行交互,有时候会有其他需求比如H5
页面需要从手机本地文件中选择图片。本篇以BridgeWebView
介绍功能的实现。
1、自定义ProgressWebView
class ProgressWebView(context: Context, attr: AttributeSet) : BridgeWebView(context, attr) {
//xml布局中使用,所以用两个构造参数的构造函数
private var progressBar: ProgressBar? = null
//页面加载完成标志
private var hasComplate: Boolean = false
//选择图片回调Activity监听
private var listener: OnImageSelectorListener? = null
//初始化
init {
//设置ProgressBar是横向
progressBar = ProgressBar(context, null, android.R.attr.progressBarStyleHorizontal)
//设置进度条属性
progressBar!!.progressDrawable = context.resources.getDrawable(R.drawable.webview_hori_progress)
//设置ProgressBar的布局参数
val layoutParams = FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, 6, 0)
//绑定参数
progressBar!!.layoutParams = layoutParams
//将ProgressBar添加到WebView上 默认头部
addView(progressBar)
//设置WebView辅助类WebChromeClient,获取实时加载进度
webChromeClient = object : WebChromeClient() {
//进度改变监听
override fun onProgressChanged(view: WebView?, newProgress: Int) {
super.onProgressChanged(view, newProgress)
//页面加载完成,进度条隐藏,设置完成标志
if (progress == 100) {
hasComplate = true
progressBar!!.visibility = View.GONE
} else {
if (!hasComplate) {
progressBar!!.visibility = View.VISIBLE
//设置进度
progressBar!!.progress = progress
}
//防止页面重定向,进度条重新刷新
hasComplate = false
}
}
//Android 4.1+
fun openFileChooser(valueCallback: android.webkit.ValueCallback<Uri>, acceptType: String, capture: String) {
listener!!.chooseImageFromSys4(valueCallback)
}
//Android 5.0+
override fun onShowFileChooser(webView: WebView?, filePathCallback: android.webkit.ValueCallback<Array<Uri>>?, fileChooserParams: FileChooserParams?): Boolean {
listener!!.chooseImageFromSys(filePathCallback)
return true
}
}
}
interface OnImageSelectorListener {
//Android 5.0+
fun chooseImageFromSys(filePathCallback: ValueCallback<Array<Uri>>?)
//Android 4.1+
fun chooseImageFromSys4(valueCallback: android.webkit.ValueCallback<Uri>)
}
fun setOnSelectListener(listener: OnImageSelectorListener) {
this.listener = listener
}
}
之前有篇博客介绍了仿微信加载H5
进度条的实现,以此为基础,之前在加载csdn
网站时,出现了一个现象,监听页面加载progress
时,已经到了100后,进度又会重新加载一次,具体产生原因不知,猜测是加载页面后网址进行了重定向。所以这里做了一个小优化,加载完成后设置标志位让进度条不再显示。
openFileChooser
和onShowFileChooser
这两个方式是WebView
辅助类WebChromeClient
用来监听Javascript
中input type='file'
事件,事件用来调用系统文件。当监听到后回调到Activity进行选择图片操作。
ValueCallback
,这个类的作用是,将我们选择的图片回调给Javascript
。而回调的类型是图片的Uri
。ValueCallback
提供了一个方法onReceiveValue
。
2、XML声明自定义的View
<com.ho.yyyyy.csdn.ProgressWebView
android:id="@+id/progressWebView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
3、设置WebView,选择图片回调
3-1、配置WebView
这里是WebView
几个重要必要的配置选项,其他配置根据自己情况而定
val setting = progressWebView!!.settings
//允许读取文件
setting!!.allowFileAccess = true
//不禁用js代码
setting.javaScriptEnabled = true
//本地缓存,无从网络拉取
setting.cacheMode = WebSettings.LOAD_CACHE_ELSE_NETWORK
//设置代理人名称,表示是app发起的
setting.userAgentString = setting.userAgentString + "penglaiapp"
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
setting.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
}
//注册Android和JS通信回调
progressWebView!!.setDefaultHandler(MyHadlerCallBack())
//创建内部类,自定义类继承DefaultHandler
private inner class MyHadlerCallBack : DefaultHandler() {
override fun handler(data: String?, function: CallBackFunction?) {
if (function != null) {
Toast.makeText(this@IssueInvoiceActivity, "自定义类继承DefaultHandler:" + data!!, Toast.LENGTH_SHORT).show()
}
}
}
<!-------------------分割线-------------------->
<!--这里是Android和JS回调代码-->
//设置JS需要的参数,具体的值这里不显示了
val params = HashMap<String, Any>()
params["token"] = "token"
params["money"] = invoiceMoney
params["invoiceflag"] = invoiceType
//"invoice"字段 JS也要注册,名字随意,Android传参给JS
progressWebView.callHandler("invoice", Gson().toJson(params)) {
}
//"goBack"字段 JS传递给Android,执行相应方法
progressWebView!!.registerHandler("goBack") { _, _ ->
finish()
}
//加载H5页面-
progressWebView!!.loadUrl(invoiceUrl)
3-2、WebView回调Activity
WebView
回调选择图片操作在Activity
里进行。
progressWebView!!.setOnSelectListener(object : ProgressWebView.OnImageSelectorListener {
//4.0以上系统
override fun chooseImageFromSys4(valueCallback: ValueCallback<Uri>) {
uploadMessage = valueCallback
chooseImageFromSys()
}
//5.0以上系统
override fun chooseImageFromSys(filePathCallback: ValueCallback<Array<Uri>>?) {
uploadMessageAboveL = filePathCallback
chooseImageFromSys()
}
})
这里是自定义的弹窗选择拍照或者相册方式。
private fun chooseImageFromSys() {
SelectorDialog.Builder(this)
.addItem(1, resources.getString(R.string.camera))
.addItem(2, resources.getString(R.string.gallery))
.setCancelable(false).setOnSelectedListener { id, name ->
when (id) {
//选择相机拍照 这里默认已经获取权限
1 ->
onPickFromCameraClicked()
//相册选择
2 ->
onPickFromGaleryClicked()
//ValueCallback对象置空,否则无法再次获取JS事件
-1 -> {
//取消按钮
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
uploadMessageAboveL!!.onReceiveValue(null)
uploadMessageAboveL = null
} else {
uploadMessage!!.onReceiveValue(null)
uploadMessage = null
}
}
}
}.build().show()
}
3-3、相册选择与相机拍照
3-3-1.相册选择图片
//相册选择RequestCode
private val PICK_IMAGE_FROM_GALERY = 0x0002
private fun onPickFromGaleryClicked() {
val intent = Intent(Intent.ACTION_PICK)
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*")
startActivityForResult(intent, PICK_IMAGE_FROM_GALERY)
}
3-3-2、调用相机拍照
private var imageUri: Uri? = null //拍照Uri对象
private var tempCameraFile: File? = null //拍照获取File文件对象
private val PICK_IMAGE_FROM_CAMERA = 0x0001 //拍照RequestCode
private fun openCamera() {
tempCameraFile = File(externalCacheDir, getString(R.string.cache__image, System.currentTimeMillis()))
//7.0以上 FileProvider 获取Uri
imageUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
//通过FileProvider创建一个content类型的Uri
FileProvider.getUriForFile(this, "com.xxx.yyyy.fileProvider", tempCameraFile)
} else {
Uri.fromFile(tempCameraFile)
}
val intent = Intent()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
//添加这一句表示对目标应用临时授权该Uri所代表的文件
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
//设置Action为拍照
intent.action = MediaStore.ACTION_IMAGE_CAPTURE
//将拍取的照片保存到指定URI
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri)
startActivityForResult(intent, PICK_IMAGE_FROM_CAMERA)
}
3-4、选择图片回调
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
//选择图片取消时,要将ValueCallback对象置空,否则无法再次获取JS事件
if (resultCode == RESULT_CANCELED) {
//系统版本5.0+
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
uploadMessageAboveL!!.onReceiveValue(null)
uploadMessageAboveL = null
} else {
uploadMessage!!.onReceiveValue(null)
uploadMessage = null
}
}
when (requestCode) {
//相册选择
PICK_IMAGE_FROM_GALERY -> {
tempCropFile = File(externalCacheDir,
"pl_cover" + System.currentTimeMillis() + ".jpg")
//这里不考虑4.4版本以下
handleImageOnKitKat(data)
}
//相机拍照
PICK_IMAGE_FROM_CAMERA-> {
if (tempCameraFile!!.exists()) {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
uploadMessageAboveL!!.onReceiveValue(arrayOf(Uri.fromFile(tempCameraFile)))
uploadMessageAboveL = null
} else {
uploadMessage!!.onReceiveValue(Uri.fromFile(tempCameraFile))
uploadMessage = null
}
}
}
}
}
//相册选择 获取uri
private fun handleImageOnKitKat(data: Intent?) {
imagePath = null
if (data != null) {
imageUri = data.data
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
uploadMessageAboveL!!.onReceiveValue(arrayOf(imageUri))
uploadMessageAboveL = null
} else {
uploadMessage!!.onReceiveValue(imageUri)
uploadMessage = null
}
}
}
效果图
总结
本篇介绍了WebView
中选择图片功能实现。目前已经测试可用,大家也可以做参考。也有功能需要完善比如图片剪裁压缩等功能等。