H5獲取手機相機或相冊圖片兩種方式-Android通過webview傳遞多張照片給H5

需求目的: 手機機通過webView展示H5網頁,在特殊場景下,需要使用相機拍照或者從相冊獲取照片,上傳后臺。

完整流程效果: 如下圖
H5調用相機相冊使用組件庫方式流程

一、H5界面樣例代碼

使用html文件格式,文件直接打開就可以展示布局;一會在andriod webview中直接加載

<!DOCTYPE html>
<html lang="en" xmlns:v-on="http://www.w3.org/1999/xhtml"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><div id="app"><h1>alllalalallalal 默認會被覆蓋</h1>
</div>
<template id="why"><div><h2>{{message}}</h2><h2>{{counter}}</h2><button @click="increment">+1</button><button @click="decrement">-1</button><h1 style="text-align: center;">{{ title }}</h1><div><h2 style="text-align: center;">android選中照片H5展示</h2><!--HTML5提供了<input type="file">元素來實現選取文件的功能,在WebView表現為調用onShowFileChooser--><input accept="image/*" capture="camera" ref="imgFile" type="file" multiple@change="previewFiles"><div id="preview"><img v-for="imgSrc in imageSources" :src="imgSrc" :key="imgSrc"style="max-width: 100px; max-height: 100px; margin: 10px;"></div></div></div>
</template><body>
<!-- 引入 Vue 3 的 CDN 資源網絡加載不了 -->
<!-- <script src="https://unpkg.com/vue@next"></script> -->
<!-- 引入 Vue 3 的 CDN 資源,本地引用 -->
<script src="vue3.2.12global.js"></script>
<script>Vue.createApp({template: '#why',data: function () {return {message: "功能開發中,敬請期待!",counter: 100,pictureSelectorContent: "相機選擇結果:",imageSources: [] // 存儲圖片的數據URL}},// 在你的 Vue 組件中處理 Webview 傳遞的數據mounted() {// 設置全局函數,用于接收 WebView 調用// window.pictureSelectorResult = this.pictureSelectorResult;},methods: {increment() {this.counter++;console.log("點擊了+1");},decrement() {this.counter--;consloe.log("點擊了-1");},startPictureSelector() {window.android.startPictureSelector();},previewFiles() {const files = this.$refs.imgFile.files;this.imageSources = [];for (let i = 0; i < files.length; i++) {const file = files[i];const reader = new FileReader();reader.onload = (e) => {this.imageSources.push(e.target.result);};reader.readAsDataURL(file);}},},}).mount("#app")</script>
<style>h1 {font-size: 80px;font-weight: bold;margin-bottom: 20px;}h2 {font-size: 20px;font-weight: bold;color: #C8EFD4;}h3 {font-size: 10px;font-weight: bold;color: #C8EFD4;}button1 {font-size: 60px;padding: 10px 20px;background-color: #007bff;color: #fff;border: none;border-radius: 4px;cursor: pointer;margin-bottom: 20px;margin-top: 20px;text-align: center;/* 將文字水平居中顯示 */display: flex;/* 將按鈕設置為flex容器 */align-items: center;/* 將文字在垂直方向上居中顯示 */}</style>
</body></html>

上述代碼是前端代碼,使用vue3框架展示一個基礎 加減demo界面(不重要的冗余),以及 一個打開文件的按鈕以及展示圖片
在這里插入圖片描述

其中HTML5提供了元素來實現選取文件的功能,當在WebView表現為調用onShowFileChooser后,回調圖片uri列表一一獲取并展示

二、Android打開相機以及相冊的兩種方式

方式一:android 原生方式

實際效果和流程示圖
在這里插入圖片描述

1.android界面邏輯代碼
這邊使用的是kotlin語言,使用的fragment界面展示,使用binding加載了布局,以及聲明了webview組件,在webview上導入html鏈接,使本地H5界面展示


class VisitorFragment : Fragment() {private lateinit var binding: FragmentVisitorBindinglateinit var mActivity: Activityprivate lateinit var mRoot: Viewcompanion object {const val TAG = "VisitorFragment"}private var mWebViewUrl: String = "file:///android_asset/vue_android_demo.html"var mAppName = MainApplication.instance.configuration.BASE_APP_LOGIN_IDENTITYvar mSystemName = WebViewConstant.DEFAULT_SYSTEM_NAMEprivate var mVisitorAndroidJs: VisitorAndroidJs = VisitorAndroidJs(this)private var mWebView: WebView? = nullvar mAndroidId: String = WebViewConstant.DEFAULT_DEVICE_SIGNvar mApiKey: String = WebViewConstant.DEFAULT_API_KEY//回傳H5時使用的對象private var mUploadCallback: ValueCallback<Array<Uri>>? = null//拍照傳遞的路徑uriprivate var mImageUri: Uri? = null/*** onCreate方法是Activity生命周期的第一個回調方法* ,當Activity被創建時被調用。* 在這個方法中,你可以進行一些初始化的操作,比如設置布局、綁定控件、初始化數據等。** @param savedInstanceState If the fragment is being re-created from* a previous saved state, this is the state.*/override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = FragmentVisitorBinding.inflate(layoutInflater)mRoot = binding.root//記錄val viewModel = ViewModelProvider(requireActivity())[DashboardViewModel::class.java]viewModel.setFragment(this)LogUtils.d(TAG, "onCreate")}/*** onCreateView方法是Fragment生命周期的回調方法,* 當Fragment創建并繪制其用戶界面時被調用。* 在這個方法中,你可以通過返回一個View對象來定義Fragment的用戶界面。* 它常用于加載布局文件、查找和初始化控件等操作。** @return*/override fun onCreateView(inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle?): View {LogUtils.d(TAG, "onCreateView")initView(mRoot, layoutInflater, null)return mRoot}fun initView(parent: View?, inflater: LayoutInflater?, container: ViewGroup?) {//設置當前fragmentmAndroidId = DeviceUtils.getUniqueId(mActivity)initWebView()mWebView?.loadUrl(mWebViewUrl)//彈出展示鏈接showToast("${getString(R.string.current_develop_environment)}$mWebViewUrl")initData()}override fun onAttach(context: Context) {super.onAttach(context)mActivity = context as Activity}/*** @param msg 內容*/fun showToast(msg: String?) {val activity: Activity? = activityactivity?.runOnUiThread {Toast.makeText(activity,msg,Toast.LENGTH_SHORT).show()}}private fun initData() {}override fun onResume() {super.onResume()LogUtils.d(TAG, "onResume")}@SuppressLint("SetJavaScriptEnabled")private fun initWebView() {LogUtils.d(TAG, "initWebView")mWebView = binding.mainWebViewmWebView?.requestFocus()mWebView?.isHorizontalScrollBarEnabled = falsemWebView?.isVerticalScrollBarEnabled = falseval setting = mWebView?.settingssetting?.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW;setting?.javaScriptEnabled = true//用于開啟或禁用其 DOM(文檔對象模型)存儲功能,瀏覽器緩存這些數據setting?.domStorageEnabled = true//允許訪問文件,默認允許setting?.allowFileAccess = true//設置不,會引起webView重新不急,默認NARROW_COLUMNSsetting?.layoutAlgorithm = WebSettings.LayoutAlgorithm.NARROW_COLUMNS//自動縮放setting?.setSupportZoom(true)setting?.builtInZoomControls = true//自適應屏幕setting?.useWideViewPort = truesetting?.loadWithOverviewMode = true//支持多窗口setting?.setSupportMultipleWindows(true)setting?.setAppCacheEnabled(true)setting?.domStorageEnabled = true//定位setting?.setGeolocationEnabled(true)//優先使用緩存數據,在緩存數據不存在的情況下才去獲取網絡數據setting?.cacheMode = WebSettings.LOAD_CACHE_ELSE_NETWORKsetting?.savePassword = false//設置js接口mVisitorAndroidJs.let { mWebView?.addJavascriptInterface(it, "android") }//頁面不跳轉瀏覽器mWebView?.webViewClient = object : WebViewClient() {override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {LogUtils.d(TAG, "url: $url")view.loadUrl(url)return true}override fun shouldInterceptRequest(view: WebView,request: WebResourceRequest): WebResourceResponse? {var uri = request.urlvar path = uri.pathLogUtils.d(TAG, "uri: $uri, path: $path")return super.shouldInterceptRequest(view, request)}}//webView官方打開文件選取方法onShowFileChooser,把網頁回傳文件mWebView?.webChromeClient = object : WebChromeClient() {//API >=21(android 5.0.1)回調此方法override fun onShowFileChooser(webView: WebView?,filePathCallback: ValueCallback<Array<Uri>>?,fileChooserParams: FileChooserParams?): Boolean {mUploadCallback = filePathCallback//使用拍照或者打開文件mImageUri = ChoosePhotoFile.takePhoto(mActivity)//設置false會IllegalStateException: Duplicate showFileChooser resultreturn true}}}fun onActivityResultResponse(requestCode: Int, resultCode: Int, intent: Intent?) {LogUtils.d(TAG,"onActivityResultResponse requestCode $requestCode, resultCode:$resultCode")// 掃描二維碼/條碼回傳if (requestCode == ScanCodeUtils.REQUEST_CODE_SCAN && resultCode == Activity.RESULT_OK) {LogUtils.d(TAG, "onActivityResultResponse ${intent?.extras}")if (intent == null) {//彈出展示鏈接showToast("掃描結果為空")return}//傳遞給js,格式是"scanCodeResult('${data.extras}')",其中單引號很重要,可能導致js script error//codedContent是組件中定義的參數名setEvaluateJavascript("scanCodeResult('${intent.getStringExtra("codedContent")}')")} else if (requestCode == ChoosePhotoFile.REQUEST_CODE) {//拍照,界面跳回后,結果文件的使用ChoosePhotoFile.takeActivityResult(requestCode, intent, mUploadCallback, mImageUri)}}@Overrideoverride fun onDestroy() {//防止更新dialog內存泄漏super.onDestroy()}/*** 登錄成功后跳轉*/open fun loginSuccessJump() {}/*** 給網頁傳值* 傳遞給js,格式是"scanCodeResult('${data.extras}')"* 其中單引號很重要,可能導致js script error*/private fun setEvaluateJavascript(jspMethodAndValue: String) {LogUtils.d(TAG,"setEvaluateJavascript mWebView ${mWebView}, jspMethodAndValue $jspMethodAndValue")mWebView?.evaluateJavascript(jspMethodAndValue, ValueCallback<String>() {LogUtils.d(TAG, "給網頁傳值為: $jspMethodAndValue")})}
}

android layout布局

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><WebViewandroid:id="@+id/main_web_view"android:layout_width="match_parent"android:layout_height="match_parent" /></androidx.constraintlayout.widget.ConstraintLayout>

2.獲取照片的主要思路是兩個方法:

  • webView官方打開文件選取方法onShowFileChooser,把網頁回傳文件
        //webView官方打開文件選取方法onShowFileChooser,把網頁回傳文件mWebView?.webChromeClient = object : WebChromeClient() {//API >=21(android 5.0.1)回調此方法override fun onShowFileChooser(webView: WebView?,filePathCallback: ValueCallback<Array<Uri>>?,fileChooserParams: FileChooserParams?): Boolean {mUploadCallback = filePathCallback//使用拍照或者打開文件mImageUri = ChoosePhotoFile.takePhoto(mActivity)//設置false會IllegalStateException: Duplicate showFileChooser resultreturn true}}
  • 拍照或者相冊選中后界面跳回后,使用ValueCallback<Array>回傳
    fun onActivityResultResponse(requestCode: Int, resultCode: Int, intent: Intent?) {LogUtils.d(TAG,"onActivityResultResponse requestCode $requestCode, resultCode:$resultCode")if (requestCode == ChoosePhotoFile.REQUEST_CODE) {//拍照或者相冊選中后界面跳回后,結果文件的使用ChoosePhotoFile.takeActivityResult(requestCode, intent, mUploadCallback, mImageUri)}}
  • 以上onActivityResultResponse方法需要在actvity onActivityResult方法中使用
    (因為我這里是activity嵌套fragment的,如果直接在activity使用webview就不需我這太麻煩)
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {super.onActivityResult(requestCode, resultCode, data)LogUtils.d(TAG,"onActivityResult requestCode11 $requestCode, resultCode:$resultCode")//設置當前fragmentval fragment = mDashboardViewModel.fragment.valueLog.d(TAG, "fragment: $fragment")Log.d(TAG, "fragment.isResumed: ${fragment?.isResumed}")//界面返回時VisitorFragment還沒有Resumedif (fragment is VisitorFragment) {val visitorFragment: VisitorFragment = fragmentvisitorFragment.onActivityResultResponse(requestCode, resultCode, data)}}

3.打開相機和相冊的工具類

object ChoosePhotoFile {private const val TAG = "ChoosePhotoFile"const val REQUEST_CODE: Int = 12345fun takePhoto(activity: Activity): Uri {//相機可以訪問的公共位置才能存儲,獲取時需要讀取文件權限val file: String =Environment.getExternalStorageDirectory().toString() + File.separator + Environment.DIRECTORY_PICTURES + File.separatorval fileName = "Image_${SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())}.jpg"val realFile = File(file, fileName)val imageUri = Uri.fromFile(realFile)LogUtils.d(TAG, "realFile:$realFile, imageUri: $imageUri")// 拍照后獲取圖片需要文件權限,界面跳轉直接拿文件不需要權限//檢查申請讀文件權限CheckPermissionUtils.requestPermissions(activity,arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.CAMERA))//調起相機,拍一張照片val captureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri)//調起相冊,取一張照片val photoIntent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)//選擇方式,拍照或者相冊val chooserIntent = Intent.createChooser(photoIntent, "Image Chooser")chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, arrayOf<Parcelable>(captureIntent))activity.startActivityForResult(chooserIntent, REQUEST_CODE)return imageUri}/*** H5針對從拍照或者相冊中選中的圖片做處理* @param imageUri 拍照返回的數據,*/fun takeActivityResult(requestCode: Int,intent: Intent?,filePathCallback: ValueCallback<Array<Uri>>?,imageUri: Uri?) {if (requestCode == REQUEST_CODE) {//從相冊獲取,返回的intentif (intent != null && intent.data != null) {var uri: Uri = intent.data as UriLogUtils.d(TAG, "file uri: $uri")filePathCallback?.onReceiveValue(arrayOf<Uri>(uri))} else {//從拍照中獲取圖片,已經返回的imageUriLogUtils.d(TAG, "take photo imageUri: $imageUri")if (imageUri != null) {filePathCallback?.onReceiveValue(arrayOf<Uri>(imageUri))} else {filePathCallback?.onReceiveValue(null)}}}}
}
方式二:使用android 組件庫是實現-朋友圈獲取照片功能

實際效果和流程示圖
在這里插入圖片描述
1.獲取照片的主要思路是兩個方法-替換

  • webView官方打開文件選取方法onShowFileChooser,把網頁回傳文件
        //webView官方打開文件選取方法onShowFileChooser,把網頁回傳文件mWebView?.webChromeClient = object : WebChromeClient() {//API >=21(android 5.0.1)回調此方法override fun onShowFileChooser(webView: WebView?,filePathCallback: ValueCallback<Array<Uri>>?,fileChooserParams: FileChooserParams?): Boolean {mUploadCallback = filePathCallback//使用拍照或者打開文件
//                mImageUri = ChoosePhotoFile.takePhoto(mActivity)//模擬微信朋友圈獲取照片模式LogUtils.d(TAG,"onShowFileChooser")PictureSelectorUtils.startPictureSelector(mActivity)//設置false會IllegalStateException: Duplicate showFileChooser resultreturn true}}
  • 拍照或者相冊選中后界面跳回后,使用ValueCallback<Array>回傳
    fun onActivityResultResponse(requestCode: Int, resultCode: Int, intent: Intent?) {LogUtils.d(TAG,"onActivityResultResponse requestCode $requestCode, resultCode:$resultCode")if (requestCode == PictureSelectorUtils.REQUEST_PICTURE_SELECTOR) {LogUtils.d(TAG, "onActivityResultResponse REQUEST_PICTURE_SELECTOR")PictureSelectorUtils.takeActivityResult(requestCode, intent, mUploadCallback)}}

2.打開相機和相冊的工具類

object PictureSelectorUtils {const val REQUEST_PICTURE_SELECTOR = 10012const val TAG = "PictureSelectorUtils"fun startPictureSelector(activity: Activity) {LogUtils.d(TAG, "startPictureSelector")// 拍照后獲取圖片需要文件權限,界面跳轉直接拿文件不需要權限//檢查申請讀文件權限
//        CheckPermissionUtils.requestPermissions(
//            activity,
//            arrayOf(
//                Manifest.permission.CAMERA,
//                Manifest.permission.READ_EXTERNAL_STORAGE,
//                Manifest.permission.WRITE_EXTERNAL_STORAGE
//            )
//        )//插件里自帶了靜態權限以及權限校驗PictureSelector.create(activity).openGallery(PictureMimeType.ofImage()).imageEngine(GlideEngine) // Please refer to the Demo GlideEngine.java.isWeChatStyle(true) // 是否開啟微信圖片選擇風格.selectionMode(PictureConfig.MULTIPLE).forResult(REQUEST_PICTURE_SELECTOR)}fun getPictures(data: Intent): MutableList<Uri> {val selectList = PictureSelector.obtainMultipleResult(data)LogUtils.d(TAG, "getPicture selectList: $selectList")// 將照片路徑轉換成 Uri 列表val imageUris: MutableList<Uri> = ArrayList()if (selectList.isEmpty()) {LogUtils.d(TAG, "getPicture selectList isEmpty")return imageUris}for (imagePath in selectList) {var path = imagePath.pathLogUtils.d(TAG, "path: $path")val uri = Uri.parse(path)LogUtils.d(TAG, "uri: $uri")imageUris.add(uri)}LogUtils.d(TAG, "imageUris: ${imageUris.toString()}")return imageUris}/*** H5針對從文件鐘選中的圖片做處理*/fun takeActivityResult(requestCode: Int,intent: Intent?,filePathCallback: ValueCallback<Array<Uri>>?,) {if (requestCode == REQUEST_PICTURE_SELECTOR) {val selectList = PictureSelector.obtainMultipleResult(intent)LogUtils.d(TAG, "getPicture selectList: $selectList")// 將照片路徑轉換成 Uri 列表val imageUris: MutableList<Uri> = ArrayList()if (selectList.isEmpty()) {LogUtils.d(TAG, "getPicture selectList isEmpty")filePathCallback?.onReceiveValue(null)return}for (imagePath in selectList) {var path = imagePath.pathLogUtils.d(TAG, "path: $path")val uri = Uri.parse(path)LogUtils.d(TAG, "uri: $uri")imageUris.add(uri)}LogUtils.d(TAG, "imageUris: ${imageUris.toString()}")filePathCallback?.onReceiveValue(imageUris.toTypedArray())}}}

其中使用第三方組件庫-實現類似朋友圈獲取照片的樣式,需要引入一下依賴

    //照片獲取類微信朋友圈implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0'

拍照后獲取圖片需要文件權限,界面跳轉直接拿文件不需要權限,且第三方插件庫里自帶了靜態權限以及權限請求

3.我完整的代碼——類似朋友圈獲取界面邏輯

class VisitorFragment : Fragment() {private lateinit var binding: FragmentVisitorBindinglateinit var mActivity: Activityprivate lateinit var mRoot: Viewcompanion object {const val TAG = "VisitorFragment"}private var mWebViewUrl: String = "file:///android_asset/vue_android_demo.html"var mAppName = MainApplication.instance.configuration.BASE_APP_LOGIN_IDENTITYvar mSystemName = WebViewConstant.DEFAULT_SYSTEM_NAMEprivate var mVisitorAndroidJs: VisitorAndroidJs = VisitorAndroidJs(this)private var mWebView: WebView? = nullvar mAndroidId: String = WebViewConstant.DEFAULT_DEVICE_SIGNvar mApiKey: String = WebViewConstant.DEFAULT_API_KEY//回傳H5時使用的對象private var mUploadCallback: ValueCallback<Array<Uri>>? = null//拍照傳遞的路徑uriprivate var mImageUri: Uri? = null/*** onCreate方法是Activity生命周期的第一個回調方法* ,當Activity被創建時被調用。* 在這個方法中,你可以進行一些初始化的操作,比如設置布局、綁定控件、初始化數據等。** @param savedInstanceState If the fragment is being re-created from* a previous saved state, this is the state.*/override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = FragmentVisitorBinding.inflate(layoutInflater)mRoot = binding.root//記錄val viewModel = ViewModelProvider(requireActivity())[DashboardViewModel::class.java]viewModel.setFragment(this)LogUtils.d(TAG, "onCreate")}/*** onCreateView方法是Fragment生命周期的回調方法,* 當Fragment創建并繪制其用戶界面時被調用。* 在這個方法中,你可以通過返回一個View對象來定義Fragment的用戶界面。* 它常用于加載布局文件、查找和初始化控件等操作。** @return*/override fun onCreateView(inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle?): View {LogUtils.d(TAG, "onCreateView")initView(mRoot, layoutInflater, null)return mRoot}fun initView(parent: View?, inflater: LayoutInflater?, container: ViewGroup?) {//設置當前fragmentmAndroidId = DeviceUtils.getUniqueId(mActivity)initWebView()mWebView?.loadUrl(mWebViewUrl)//彈出展示鏈接showToast("${getString(R.string.current_develop_environment)}$mWebViewUrl")initData()}override fun onAttach(context: Context) {super.onAttach(context)mActivity = context as Activity}/*** @param msg 內容*/fun showToast(msg: String?) {val activity: Activity? = activityactivity?.runOnUiThread {Toast.makeText(activity,msg,Toast.LENGTH_SHORT).show()}}private fun initData() {}override fun onResume() {super.onResume()LogUtils.d(TAG, "onResume")}@SuppressLint("SetJavaScriptEnabled")private fun initWebView() {LogUtils.d(TAG, "initWebView")mWebView = binding.mainWebViewmWebView?.requestFocus()mWebView?.isHorizontalScrollBarEnabled = falsemWebView?.isVerticalScrollBarEnabled = falseval setting = mWebView?.settingssetting?.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW;setting?.javaScriptEnabled = true//用于開啟或禁用其 DOM(文檔對象模型)存儲功能,瀏覽器緩存這些數據setting?.domStorageEnabled = true//允許訪問文件,默認允許setting?.allowFileAccess = true//設置不,會引起webView重新不急,默認NARROW_COLUMNSsetting?.layoutAlgorithm = WebSettings.LayoutAlgorithm.NARROW_COLUMNS//自動縮放setting?.setSupportZoom(true)setting?.builtInZoomControls = true//自適應屏幕setting?.useWideViewPort = truesetting?.loadWithOverviewMode = true//支持多窗口setting?.setSupportMultipleWindows(true)setting?.setAppCacheEnabled(true)setting?.domStorageEnabled = true//定位setting?.setGeolocationEnabled(true)//優先使用緩存數據,在緩存數據不存在的情況下才去獲取網絡數據setting?.cacheMode = WebSettings.LOAD_CACHE_ELSE_NETWORKsetting?.savePassword = false//設置js接口mVisitorAndroidJs.let { mWebView?.addJavascriptInterface(it, "android") }//頁面不跳轉瀏覽器mWebView?.webViewClient = object : WebViewClient() {override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {LogUtils.d(TAG, "url: $url")view.loadUrl(url)return true}override fun shouldInterceptRequest(view: WebView,request: WebResourceRequest): WebResourceResponse? {var uri = request.urlvar path = uri.pathLogUtils.d(TAG, "uri: $uri, path: $path")return super.shouldInterceptRequest(view, request)}}//webView官方打開文件選取方法onShowFileChooser,把網頁回傳文件mWebView?.webChromeClient = object : WebChromeClient() {//API >=21(android 5.0.1)回調此方法override fun onShowFileChooser(webView: WebView?,filePathCallback: ValueCallback<Array<Uri>>?,fileChooserParams: FileChooserParams?): Boolean {mUploadCallback = filePathCallback//使用拍照或者打開文件
//                mImageUri = ChoosePhotoFile.takePhoto(mActivity)//模擬微信朋友圈獲取照片模式LogUtils.d(TAG,"onShowFileChooser")PictureSelectorUtils.startPictureSelector(mActivity)//設置false會IllegalStateException: Duplicate showFileChooser resultreturn true}}}fun onActivityResultResponse(requestCode: Int, resultCode: Int, intent: Intent?) {LogUtils.d(TAG,"onActivityResultResponse requestCode $requestCode, resultCode:$resultCode")// 掃描二維碼/條碼回傳if (requestCode == ScanCodeUtils.REQUEST_CODE_SCAN && resultCode == Activity.RESULT_OK) {LogUtils.d(TAG, "onActivityResultResponse ${intent?.extras}")if (intent == null) {//彈出展示鏈接showToast("掃描結果為空")return}//傳遞給js,格式是"scanCodeResult('${data.extras}')",其中單引號很重要,可能導致js script error//codedContent是組件中定義的參數名setEvaluateJavascript("scanCodeResult('${intent.getStringExtra("codedContent")}')")
//        } else if (requestCode == ChoosePhotoFile.REQUEST_CODE) {
//            //拍照,界面跳回后,結果文件的使用
//            ChoosePhotoFile.takeActivityResult(requestCode, intent, mUploadCallback, mImageUri)} else if (requestCode == PictureSelectorUtils.REQUEST_PICTURE_SELECTOR) {LogUtils.d(TAG, "onActivityResultResponse REQUEST_PICTURE_SELECTOR")PictureSelectorUtils.takeActivityResult(requestCode, intent, mUploadCallback)}}@Overrideoverride fun onDestroy() {//防止更新dialog內存泄漏super.onDestroy()}/*** 登錄成功后跳轉*/open fun loginSuccessJump() {}/*** 給網頁傳值* 傳遞給js,格式是"scanCodeResult('${data.extras}')"* 其中單引號很重要,可能導致js script error*/private fun setEvaluateJavascript(jspMethodAndValue: String) {LogUtils.d(TAG,"setEvaluateJavascript mWebView ${mWebView}, jspMethodAndValue $jspMethodAndValue")mWebView?.evaluateJavascript(jspMethodAndValue, ValueCallback<String>() {LogUtils.d(TAG, "給網頁傳值為: $jspMethodAndValue")})}
}

三、總結一下

  • H5調用公共獲取圖片文件方法,
  • 在android手機端,H5主要依賴Webview,
  • 這邊在webview聲明并重寫該方法onShowFileChooser
  • 使用工具類打開相機或相冊,可以兩種方式安卓原生方式或者利用第三方組件庫方式
  • 選中圖片,返回uri列表給H5
  • H5收到uri照片列表,并且使用前端方式展示

創造價值,樂哉分享!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/697793.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/697793.shtml
英文地址,請注明出處:http://en.pswp.cn/news/697793.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

BGP-OSPF防環機制

一、BGP 防環機制 1、AS內部防環&#xff1a;通過IBGP水平分割&#xff0c;IBGP水平分割的基本思想是 不把從IBGP鄰居學到的路由信息發送給其他IBGP鄰居&#xff1b; 2、AS間的防環&#xff1a;通過屬性AS-PATH來實現&#xff0c; 基本思想是&#xff1a;記錄經過的路徑&…

【每日一題】2583. 二叉樹中的第 K 大層和-2024.2.23

題目: 2583. 二叉樹中的第 K 大層和 給你一棵二叉樹的根節點 root 和一個正整數 k 。 樹中的 層和 是指 同一層 上節點值的總和。 返回樹中第 k 大的層和(不一定不同)。如果樹少于 k 層,則返回 -1 。 注意,如果兩個節點與根節點的距離相同,則認為它們在同一層。 示…

canvas水波紋效果,jquery鼠標水波紋插件

canvas水波紋效果&#xff0c;jquery鼠標水波紋插件 效果展示 jQuery水波紋效果&#xff0c;canvas水波紋插件 HTML代碼片段 <div class"scroll04wrap"><h3>發展歷程</h3><div class"scroll04"><p>不要回頭&#xff0c;一…

前端工程Bem架構及其封裝

文章目錄 簡介語法在vue3項目中引用sass創建bem.scss文件修改vite.config.tsvue文件中使用結果 這是我學習記錄的筆記&#xff0c;如有不正&#xff0c;歡迎補充 簡介 首先認識一下什么是bem架構&#xff1f;BEM的意思就是塊&#xff08;block&#xff09;、元素&#xff08;e…

【DDD】學習筆記-發布者—訂閱者模式

在領域設計模型中引入了領域事件&#xff0c;并不意味著就采用了領域事件建模范式&#xff0c;此時的領域事件僅僅作為一種架構或設計模式而已&#xff0c;屬于領域設計模型的設計要素。在領域設計建模階段&#xff0c;如何選擇和設計領域事件&#xff0c;存在不同的模式&#…

nginx-ingress-controller組件中Nginx的版本升級

參考鏈接&#xff1a;https://blog.csdn.net/qq_22824481/article/details/133761302 https://blog.csdn.net/mengfanshaoxia/article/details/127155020 https://blog.csdn.net/weixin_39961559/article/details/87935873 概要 業務區k…

JAVAEE初階 JVM(一)

JVM的熱門話題 一. JVM中的內存區域劃分1.經典筆試題. 二. JVM的類加載機制 一. JVM中的內存區域劃分 1.經典筆試題. 二. JVM的類加載機制

wondows10用Electron打包threejs的項目記錄

背景 電腦是用的mac&#xff0c;安裝了parallels desktop ,想用electron 想同時打包出 蘋果版本和windows版本。因為是在虛擬機里安裝&#xff0c;它常被我重裝&#xff0c;所以記錄一下打包的整個過程。另外就是node生態太活躍&#xff0c;幾個依賴沒記錄具體版本&#xff0…

lora網關智慧工廠三色燈安燈狀態采集鋇錸技術S281

LoRa網關結合鋇錸技術S281模塊在智慧工廠三色燈安燈狀態采集方面具有廣泛的應用前景。智慧工廠的安全生產管理對于企業生產經營至關重要&#xff0c;而三色燈安燈是工廠安全生產管理的重要指示燈&#xff0c;通過LoRa無線通信技術和鋇錸技術S281模塊&#xff0c;可以實現對三色…

android 使用X264編碼視頻

android 使用X264編碼視頻 源碼剛上傳可能審核 源碼下載地址 X264對應部分API介紹 初始化x264_param_t _x264_param new x264_param_t;/*** preset是編碼速度* 可選項"ultrafast", "superfast", "veryfast", "faster", "fa…

使用 package.json 配置代理解決 React 項目中的跨域請求問題

使用 package.json 配置代理解決 React 項目中的跨域請求問題 當我們在開發前端應用時&#xff0c;經常會遇到跨域請求的問題。為了解決這個問題&#xff0c;我們可以通過配置代理來實現在開發環境中向后端服務器發送請求。 在 React 項目中&#xff0c;我們可以使用 package…

MES系統中的手動排產和自動排產-助力生產效率

企業在排產管理中面臨的問題&#xff1a; 大多數的企業在調度排產過程中&#xff0c;都會遇到以下問題。首先是插單非常的多&#xff0c;計劃調整困難&#xff0c;會經常性的發生原材料、零部件的備貨不足。計劃按MRP或庫存展示計算出需求后將產生大量工單&#xff0c;這些工單…

《劍指Offer》筆記題解思路技巧優化_Part_6

《劍指Offer》筆記&題解&思路&技巧&優化_Part_6 &#x1f60d;&#x1f60d;&#x1f60d; 相知&#x1f64c;&#x1f64c;&#x1f64c; 相識&#x1f622;&#x1f622;&#x1f622; 開始刷題&#x1f7e1;1.LCR 168. 丑數—— 丑數&#x1f7e2;2. LCR 16…

Kubernetes服務網絡Ingress網絡模型分析、安裝和高級用法

文章目錄 1、Ingres簡介2、Ingres網絡模型分析3、安裝Ingress4、使用4.1、搭建測試環境4.2、域名訪問4.3、路徑重寫&#xff08;高級用法&#xff09;4.4、流量限制&#xff08;高級用法&#xff09; 5、總結 1、Ingres簡介 Ingress翻譯過來是“入口”的意思&#xff0c;也就是…

切換分支時候IDEA提示:workspace associated with branch feature has been restored

切換分支時候IDEA提示&#xff1a;workspace associated with branch feature has been restored 這個消息是指與"feature"分支關聯的工作區已經恢復。在Git中&#xff0c;工作區是指你當前正在進行修改和編輯的文件和目錄。當你切換分支時&#xff0c;Git會自動將工…

配置docker 支持GPU方法(Nvidia GPU)

參考官方文檔&#xff1a; https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html 系統版本&#xff1a;ubuntu 23.04 執行腳本如下&#xff1a; 1.Configure the production repository: curl -fsSL https://nvidia.github.io/lib…

怎么把試卷圖片轉換成word?這4種方法一看就會

怎么把試卷圖片轉換成word&#xff1f;在數字化日益盛行的今天&#xff0c;我們常常會面臨將紙質試卷或圖片中的試卷內容轉化為Word文檔的需求。無論是為了對試卷內容進行編輯、修改&#xff0c;還是為了在線共享、遠程教學&#xff0c;將圖片轉換為Word文檔都成為了至關重要的…

集成TinyMCE富文本編輯器

若依的基礎上集成TinyMCE富文本編輯器 前端bootstrap TinyMCE官網鏈接 TinyMCE所需靜態資源下載鏈接 開源項目-若依鏈接 將TinyMCE靜態資源包放入項目中&#xff1b; 代碼引入css&#xff1a; <!-- 引入TinyMCE CSS --><link th:href"{/ajax/libs/tinymce/j…

金田金業: 美聯儲泡沫正在破裂! 崩潰對黃金非常有利

The Great Recession Blog作者大衛哈吉斯表示&#xff0c;美聯儲一直以來都將繼續收緊貨幣政策&#xff0c;直到出現問題&#xff0c;但市場現在已經陷入泡沫。 他指出&#xff0c;泡沫正在破裂&#xff0c;崩潰最終將對黃金非常有利。 正當投資者聚焦美聯儲何時會降息&#xf…

Springboot 使用升級小記-循環依賴

springboot 使用已經非常廣泛了&#xff0c;它的版本迭代速度也比較快&#xff0c;過一段時間版本差異就會比較大。 由于低版本依賴的 spring 版本有漏洞問題&#xff0c;這次我們是從 2.2.6 版本直接升級到 2.7.16&#xff0c;升級 3.0 的話擔心差異更大。 這時直接修改依賴…