布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ProgressBar
android:id="@+id/progressbar"
style="@android:style/Widget.DeviceDefault.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="5dp"
android:layout_marginTop="-1dp"
android:background="#F5F5F5" />
</FrameLayout>
</LinearLayout>
java activity实现:
package com.example.webviewupload;
import android.Manifest;
import android.annotation.TargetApi;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.util.Log;
import android.util.Patterns;
import android.view.KeyEvent;
import android.view.View;
import android.view.WindowManager;
import android.webkit.DownloadListener;
import android.webkit.JsResult;
import android.webkit.PermissionRequest;
import android.webkit.URLUtil;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceError;
import android.webkit.WebResourceRequest;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ProgressBar;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.FileProvider;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.util.Arrays;
public class WebViewActivity extends AppCompatActivity {
private final String TAG = this.getClass().getName();
private ValueCallback<Uri[]> mValueCallback;
private WebChromeClient.FileChooserParams mFileChooserParams;
private final static int FILE_CHOOSER_RESULT_CODE = 1;
private static final int CAMERA_REQUEST_CODE = 2;
private WebView webview;
private ProgressBar progressBar;
private File mImageFile = null;
private Uri mImageUri = null;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_web);
// 解决H5页面全屏时软键盘弹起遮挡问题
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE | WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
AndroidBug5497Workaround.assistActivity(this);
initMyView();
}
@Override
protected void onDestroy() {
super.onDestroy();
}
private void initMyView() {
String url = "https://kefu.easemob.com/webim/im.html?configId=982d8f2d-06db-49ab-9e14-673c151172b6";//测试地址
webview = findViewById(R.id.webview);
progressBar = findViewById(R.id.progressbar);
WebSettings webSettings = webview.getSettings();
webSettings.setJavaScriptEnabled(true); // 支持JS
webSettings.setAllowFileAccess(true); // 可以访问文件
webSettings.setAllowContentAccess(true);
webSettings.setJavaScriptCanOpenWindowsAutomatically(true); // 支持JS打开新窗口
webSettings.setLoadsImagesAutomatically(true); // 支持自动加载图片
webSettings.setDomStorageEnabled(true);
webSettings.setLoadWithOverviewMode(true);
webSettings.setUseWideViewPort(true); // 将图片调整到适合webView的大小
webSettings.setSupportZoom(true);
webSettings.setBuiltInZoomControls(true);
webSettings.setDisplayZoomControls(false);
// 修改配置,支持http路径的内容,如内嵌的视频
webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);
webview.loadUrl(url);
webview.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
//解决重定向问题
if (!TextUtils.isEmpty(request.getUrl().getPath()) && view.getHitTestResult() == null) {
view.loadUrl(request.getUrl().getPath());
return true;
}
return super.shouldOverrideUrlLoading(view, request);
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
//解决重定向问题
if (!TextUtils.isEmpty(url) && view.getHitTestResult() == null) {
view.loadUrl(url);
return true;
}
return super.shouldOverrideUrlLoading(view, url);
}
@RequiresApi(api = Build.VERSION_CODES.M)
@Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
super.onReceivedError(view, request, error);
Log.d(TAG, "onReceivedError=error=" + error.getDescription() + ",errorCode=" + error.getErrorCode() + ",failingUrl=" + request.getUrl());
}
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
Log.d(TAG, "onReceivedError=description=" + description + ",errorCode=" + errorCode + ",failingUrl=" + failingUrl);
}
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
handler.proceed();//网页证书有问题时,继续加载网页
super.onReceivedSslError(view, handler, error);
}
});
webview.setDownloadListener(new DownloadListener() {
@Override
public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {
Log.d(TAG, "onDownloadStart=url=" + url + ",userAgent=" + userAgent +
",contentDisposition=" + contentDisposition + ",mimetype=" + mimetype + ",contentLength=" + contentLength);
//实现下载
if (URLUtil.isValidUrl(url) && Patterns.WEB_URL.matcher(url).matches()) {
Uri uri = Uri.parse(url);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.addCategory(Intent.CATEGORY_BROWSABLE);
startActivity(intent);
} else {//有可能是 bolb 类型
Log.d(TAG, "不支持此类型链接:" + url);
}
}
});
webview.setWebChromeClient(new WebChromeClient() {
@Override
public void onPermissionRequest(final PermissionRequest request) {
Log.d(TAG, "onPermissionRequest=" + request.getResources());
WebViewActivity.this.runOnUiThread(new Runnable() {
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void run() {
request.grant(request.getResources());
}
});
}
@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
}
@Override
public void onProgressChanged(WebView view, int newProgress) {
progressBar.setProgress(newProgress);
if (newProgress == 100) {
progressBar.postDelayed(() -> {
progressBar.setVisibility(View.GONE);
}, 200);
} else {
progressBar.setVisibility(View.VISIBLE);
}
}
//扩展支持alert事件
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
return true;
}
// For Android > 5.0
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> valueCallback, FileChooserParams fileChooserParams) {
Log.d(TAG, "onShowFileChooser=" + Arrays.toString(fileChooserParams.getAcceptTypes()));
mValueCallback = valueCallback;
mFileChooserParams = fileChooserParams;
if (ActivityCompat.checkSelfPermission(WebViewActivity.this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
openFileChooser();
} else {
ActivityCompat.requestPermissions(WebViewActivity.this, new String[]{Manifest.permission.CAMERA}, CAMERA_REQUEST_CODE);
}
return true;
}
});
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull @NotNull String[] permissions, @NonNull @NotNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
Log.d(TAG, "onRequestPermissionsResult=ps=" + Arrays.toString(permissions) + ",gs=" + Arrays.toString(grantResults));
//相机权限请求结果处理
if (requestCode == CAMERA_REQUEST_CODE) {
openFileChooser();
}
}
/**
* 选择文件 或 打开相机
*/
private void openFileChooser() {
//at=image/jpgimage/gifimage/jpegimage/png,m=0,h=null 或 at=image/*,m=0,h=null //图片
//at=,m=0,h=null 或 at=*/*,m=0,h=null //附件
//at=video/*,m=0,h=null //视频
String at = Arrays.toString(mFileChooserParams.getAcceptTypes());
String dmsg = "at=" + at + ",m=" + mFileChooserParams.getMode() + ",h=" + mFileChooserParams.getFilenameHint();
Log.d(TAG, "dmsg=" + dmsg);
Intent fileIntent = new Intent(Intent.ACTION_GET_CONTENT);
fileIntent.addCategory(Intent.CATEGORY_OPENABLE);
fileIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, mFileChooserParams.getMode() == WebChromeClient.FileChooserParams.MODE_OPEN_MULTIPLE);
Intent imageCaptureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);// 启动系统相机-拍图
mImageFile = new File(getFilesDir().getPath() + "/" + System.currentTimeMillis() + ".jpg");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
mImageUri = FileProvider.getUriForFile(this, getApplicationInfo().packageName + ".fileProvider", mImageFile);
} else {
mImageUri = Uri.fromFile(mImageFile); //传递路径
}
imageCaptureIntent.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri);
Intent videoCaptureIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);// 启动系统相机-拍视频
Intent[] intentArray = null;
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
if (at.contains("image/")) {//图片
fileIntent.setType("image/*");
if (ActivityCompat.checkSelfPermission(WebViewActivity.this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
intentArray = new Intent[]{imageCaptureIntent};
}
} else if (at.contains("video/")) {//视频
fileIntent.setType("video/*");
if (ActivityCompat.checkSelfPermission(WebViewActivity.this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
intentArray = new Intent[]{videoCaptureIntent};
}
} else {//文件
fileIntent.setType("*/*");
if (ActivityCompat.checkSelfPermission(WebViewActivity.this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
intentArray = new Intent[]{imageCaptureIntent, videoCaptureIntent};
}
}
chooserIntent.putExtra(Intent.EXTRA_INTENT, fileIntent);
chooserIntent.putExtra(Intent.EXTRA_TITLE, "选择操作");
if (intentArray != null) {
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
}
startActivityForResult(chooserIntent, FILE_CHOOSER_RESULT_CODE);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (webview.canGoBack() && event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
webview.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
Log.d(TAG, "onActivityResult-requestCode=" + requestCode + ",resultCode=" + resultCode + ",intent=" + intent);
//选择器返回结果
if (mValueCallback != null && requestCode == FILE_CHOOSER_RESULT_CODE) {
if (mImageFile != null && mImageFile.length() > 10) {//如果图片文件大小 大于 10 byte ,则说明已被写入
mValueCallback.onReceiveValue(new Uri[]{mImageUri});
} else if (intent.getData() != null) {//单选文件、图片、视频
mValueCallback.onReceiveValue(new Uri[]{intent.getData()});
} else if (intent.getClipData() != null) {//多选文件、图片、视频
Uri[] uris = new Uri[intent.getClipData().getItemCount()];
for (int i = 0; i < intent.getClipData().getItemCount(); i++) {
uris[i] = intent.getClipData().getItemAt(i).getUri();
}
mValueCallback.onReceiveValue(uris);
} else {
mValueCallback.onReceiveValue(new Uri[]{});//onShowFileChooser return true 必须调用这个方法
}
mValueCallback = null;
mFileChooserParams = null;
}
}
}
kotlin activity 实现:
package com.example.webviewupload
import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.MediaStore
import android.text.TextUtils
import android.util.Log
import android.util.Patterns
import android.view.View
import android.view.WindowManager
import android.webkit.*
import android.widget.ProgressBar
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.FileProvider
import java.io.File
import java.util.*
class WebViewActivityK : AppCompatActivity() {
private val TAG: String = javaClass.simpleName
private var mFilePathCallback: ValueCallback<Array<Uri>>? = null
private val CAMERA_REQUEST_CODE: Int = 1
private val FILE_CHOOSER_REQUEST_CODE: Int = 2
private var mFileChooserParams: WebChromeClient.FileChooserParams? = null
private var progressBar: ProgressBar? = null
private var webview: WebView? = null
private var mImageFile: File? = null
private var mImageUri: Uri? = null
companion object {
private val HX_KEY_URL: String = "hx_key_url"
fun startActivity(context: Context, url: String) {
val intent = Intent(context, WebViewActivityK::class.java)
intent.putExtra(HX_KEY_URL, url)
context.startActivity(intent)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_web)
//防止软键盘遮挡 输入框
window.setFlags(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE,WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN)
AndroidBug5497Workaround.assistActivity(this)
initHxView()
}
/**
* 初始化 webview
*/
@SuppressLint("SetJavaScriptEnabled")
private fun initHxView() {
//val hxUrl = intent?.getStringExtra(HX_KEY_URL)
val hxUrl = "https://kefu.easemob.com/webim/im.html?configId=982d8f2d-06db-49ab-9e14-673c151172b6"//测试地址
webview = findViewById(R.id.webview)
progressBar = findViewById(R.id.progressbar)
val webSettings = webview?.settings
webSettings?.javaScriptEnabled = true // 支持JS
webSettings?.allowFileAccess = true // 可以访问文件
webSettings?.allowContentAccess = true
webSettings?.javaScriptCanOpenWindowsAutomatically = true // 支持JS打开新窗口
webSettings?.loadsImagesAutomatically = true // 支持自动加载图片
webSettings?.domStorageEnabled = true
webSettings?.loadWithOverviewMode = true
webSettings?.useWideViewPort = true // 将图片调整到适合webView的大小
webSettings?.setSupportZoom(true)
webSettings?.builtInZoomControls = true
webSettings?.displayZoomControls = false
webSettings?.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
webSettings?.cacheMode = WebSettings.LOAD_NO_CACHE
hxUrl.let {
webview?.loadUrl(it)
}
webview?.webViewClient = (object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView?, urlString: String?): Boolean {
//解决重定向问题
if (!TextUtils.isEmpty(urlString) && view?.hitTestResult == null) {
Log.d(TAG, "shouldOverrideUrlLoading-let=urlString=" + urlString)
view?.loadUrl(urlString!!)
}
Log.d(TAG, "shouldOverrideUrlLoading-url=" + urlString + ",hitTestResult=" + view?.hitTestResult)
return super.shouldOverrideUrlLoading(view, urlString)
}
@RequiresApi(Build.VERSION_CODES.N)
override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
//解决重定向问题
if (!TextUtils.isEmpty(request?.url?.path) && view?.hitTestResult == null) {
view?.loadUrl(request?.url?.path!!)
Log.d(TAG, "shouldOverrideUrlLoading-N-urlString=" + request?.url + ",Redirect=" + request?.isRedirect)
return true
}
Log.d(TAG, "shouldOverrideUrlLoading-N-url=" + request?.url + ",hitTestResult=" + view?.hitTestResult + ",Redirect=" + request?.isRedirect)
return super.shouldOverrideUrlLoading(view, request)
}
override fun onReceivedError(view: WebView?, errorCode: Int, description: String?, failingUrl: String?) {
super.onReceivedError(view, errorCode, description, failingUrl)
Log.d(TAG, "onReceivedError-errorCode=$errorCode,description=$description,failingUrl=$failingUrl")
}
@RequiresApi(Build.VERSION_CODES.M)
override fun onReceivedError(view: WebView?, request: WebResourceRequest?, error: WebResourceError?) {
super.onReceivedError(view, request, error)
Log.d(TAG, "onReceivedError-M-errorCode=" + error?.errorCode + ",description=" + error?.description + ",failingUrl=" + request?.url)
}
})
//文件下载监听
webview?.setDownloadListener(object : DownloadListener {
override fun onDownloadStart(url: String?, userAgent: String?, contentDisposition: String?, mimetype: String?, contentLength: Long) {
Log.d(TAG, "onDownloadStart=$url,userAgent=$userAgent,contentDisposition=$contentDisposition,mimeType=$mimetype,contentLength=$contentLength")
//判断url是否有效,且打开默认浏览器下载文件
if (URLUtil.isValidUrl(url) || Patterns.WEB_URL.matcher(url).matches()) {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
intent.addCategory(Intent.CATEGORY_BROWSABLE)
startActivity(intent)
} else {
Log.d(TAG, "onDownloadStart=无效url=$url")
}
}
})
webview?.webChromeClient = (object : WebChromeClient() {
override fun onPermissionRequest(request: PermissionRequest?) {
Log.d(TAG, "onPermissionRequest=" + request?.resources)
//授权资源读取
runOnUiThread(object : Runnable {
override fun run() {
request?.grant(request.resources)
}
})
}
override fun onProgressChanged(view: WebView?, newProgress: Int) {
//顶部进度条控制
progressBar?.progress = newProgress
if (newProgress == 100) {
progressBar?.postDelayed(object : Runnable {
override fun run() {
progressBar?.visibility = View.GONE
}
}, 200)
} else {
progressBar?.visibility = View.VISIBLE
}
}
override fun onJsAlert(view: WebView?, url: String?, message: String?, result: JsResult?): Boolean {
return true //网页弹框
}
override fun onShowFileChooser(webView: WebView?, filePathCallback: ValueCallback<Array<Uri>>?, fileChooserParams: FileChooserParams?): Boolean {
mFilePathCallback = filePathCallback
mFileChooserParams = fileChooserParams
//打开文件选择器
if (ActivityCompat.checkSelfPermission(this@WebViewActivityK, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
openFileChooser()
} else {//请求相机权限
ActivityCompat.requestPermissions(this@WebViewActivityK, arrayOf(Manifest.permission.CAMERA), CAMERA_REQUEST_CODE)
}
Log.d(TAG, "onShowFileChooser=atStr=" + Arrays.toString(fileChooserParams?.acceptTypes) + ",mode=" + fileChooserParams?.mode)
return true
}
})
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
Log.d(TAG, "onRequestPermissionsResult=requestCode=$requestCode")
//打开文件选择器
if (requestCode == CAMERA_REQUEST_CODE) {
openFileChooser()
}
}
/**
* 打开文件选择器
*/
private fun openFileChooser() {
val atStr = Arrays.toString(mFileChooserParams?.acceptTypes)
//文件获取
val contentIntent = Intent(Intent.ACTION_GET_CONTENT)
contentIntent.addCategory(Intent.CATEGORY_OPENABLE)
contentIntent.putExtra(
Intent.EXTRA_ALLOW_MULTIPLE,
mFileChooserParams?.mode == WebChromeClient.FileChooserParams.MODE_OPEN_MULTIPLE
)
val imageCaptureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)//系统相机-拍照
//设置图片输出路径
mImageFile = File(filesDir.path, System.currentTimeMillis().toString() + ".jpg")
mImageUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
FileProvider.getUriForFile(this, applicationInfo.packageName + ".fileProvider", mImageFile!!)
} else {
Uri.fromFile(mImageFile)
}
imageCaptureIntent.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri)
val videoCaptureIntent = Intent(MediaStore.ACTION_VIDEO_CAPTURE)//系统相机-拍视频
val chooserIntent = Intent(Intent.ACTION_CHOOSER)
var intentArr: Array<Intent>? = null
if ("image/" in atStr) {//图片
contentIntent.type = "image/*"
if (ActivityCompat.checkSelfPermission(this,Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
intentArr = arrayOf(imageCaptureIntent)
}
} else if ("video/" in atStr) {//视频
contentIntent.type = "video/*"
if (ActivityCompat.checkSelfPermission(this,Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
intentArr = arrayOf(videoCaptureIntent)
}
} else {//所有文件
contentIntent.type = "*/*"
if (ActivityCompat.checkSelfPermission(this,Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
intentArr = arrayOf(imageCaptureIntent, videoCaptureIntent)
}
}
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentIntent)
chooserIntent.putExtra(Intent.EXTRA_TITLE, "选择操作")
intentArr?.let {
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, it)
}
startActivityForResult(chooserIntent, FILE_CHOOSER_REQUEST_CODE)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
Log.d(TAG, "onActivityResult=requestCode=$requestCode,resultCode=$resultCode,data=$data")
//选择器返回结果处理
if (mFilePathCallback != null && requestCode == FILE_CHOOSER_REQUEST_CODE) {
val uris = when {
(mImageFile?.length()!! > 10) -> {//拍照返回图片文件大小 > 10 byte 则使用
arrayOf(mImageUri!!)
}
(data?.data != null) -> {//单选文件
arrayOf(data.data!!)
}
(data?.clipData != null) -> {//多选文件
val uriArr = arrayListOf<Uri>()
for (i in 0 until data.clipData?.itemCount!!) {
val itemAt = data.clipData!!.getItemAt(i).uri
uriArr.add(itemAt)
}
uriArr.toTypedArray()
}
else -> {
arrayOf()
}
}
mFilePathCallback?.onReceiveValue(uris)
}
}
}