1. 通过WebView调起继承微信支付的H5页面
// 微信H5 支付 官方测试地址
String url = "http://wxpay.wxutil.com/mch/pay/h5.v2.php";
Map<String, String> extraHeaders = new HashMap<String, String>();
extraHeaders.put("Referer", "http://wxpay.wxutil.com"); // 商户申请H5时提交的授权域名
myWebView.loadUrl(url,extraHeaders); // 加载url
mClient = new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if(url.startsWith("weixin://wap/pay?")) {
try{
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
startActivity(intent);
}catch (ActivityNotFoundException e){
ToastUtil.showShortToast(FoodWebViewActivity.this,"请安装微信最新版!");
}
return true;
} else {
Map<String, String> extraHeaders = new HashMap<String, String>();
extraHeaders.put("Referer", "http://wxpay.wxutil.com"); // 微信公共测试地址
myWebView.loadUrl(url,extraHeaders);
return true;
}
return true;
}
@Override
public void onPageFinished(WebView view, String url) {
if (mProgressDialog != null) {
mProgressDialog.dismiss();
}
super.onPageFinished(view, url);
}
@Override
public void onReceivedError(WebView view, int errorCode,
String description, String failingUrl) {
// 加载失败
if (mProgressDialog != null) {
mProgressDialog.dismiss();
}
super.onReceivedError(view, errorCode, description, failingUrl);
}
};
myWebView.setWebViewClient(mClient);
2. 为什么 WebView 调不起 弹框
myWebView.setWebChromeClient(new WebChromeClient() {
private ValueCallback<Uri> mUploadMessage;
@Override
public void onProgressChanged(WebView view, int newProgress) {
if (newProgress == 100) {
// 网页加载完成
// main_pb_load.setVisibility(View.GONE);
} else {
// 加载中
// if(main_pb_load.getVisibility()==View.GONE){
// main_pb_load.setVisibility(View.VISIBLE);
// }
// main_pb_load.setProgress(newProgress);
}
}
/**
* alert弹框
*
* @return
*/
@Override
public boolean onJsAlert(WebView view, String url, final String message,
JsResult result) {
runOnUiThread(new Runnable() {
@Override
public void run() {
new AlertDialog.Builder(BusWebViewActivity.this)
.setTitle("温馨提示")
.setMessage(message)
.setPositiveButton("确定", null)
.show();
}
});
result.cancel();//这里必须调用,否则页面会阻塞造成假死
return true;
}
@Override
public boolean onJsConfirm(WebView view, String url,
final String message, final JsResult result) {
runOnUiThread(new Runnable() {
@Override
public void run() {
new AlertDialog.Builder(BusWebViewActivity.this)
.setTitle("温馨提示")
.setMessage(message)
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
})
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.cancel();
}
})
.show();
}
});
return true;
}
});
3. H5上传图片,调用起 拍照和 图库
其实上传图片的方式有两种,一种是通过设置WebChromeClient。重写WebChromeClient中关于文件选择的方法,onShowFileChooser和openFileChooser,这种方法其实是有坑的,而且麻烦,还要兼容各个版本,当然我已经实现了,但是出现了个Bug,实在是找不到解决方法,我这边简单描述一下,拍照和图库选择图片我都是可以调起,并且在网页上进行显示,但是当我第一次拍照,显示OK,但是如果我马上又拍了一张,发现图片死活显示不出来。如果我是选择图库,然后在拍照,有可以,只要我是连续拍照就不行。算了,你也看得迷糊,你遇到就会清楚。
现在我在这里介绍第二种,不会存在兼容问题,当然也有兼容问题,下面的例子已经解决,所以是OK的。
第二种:通过 H5 调用 android本地方法,调起 拍照和 图库选择,然后选择好之后,用android 调用 JS 的方法,通过把图片转为base64的字符串传递给H5,这里有个坑,就是base64比较大 在低版本的安卓机上,是传不上去的,所以呢,我们把base64 转为JsonObject 传递给 H5的 JS 方法。同样的还有一个坑,就是 如果用Base64.DEFAULT进行转换base64他会在这个base64后面加上一个换行符,所以不能用这个要用Base64.NO_WRAP 。当然如果你不兼容 低版本,直接用Base64.DEFAULT 的base64字符串直接JS是可以显示图片的,但是如果用 JsonObject 就不行。
以上的坑的解决方法我都是通过上网查找资料找到的,很可惜,没有一段完整的代码来实现这个功能,我这边特别整理了所有的坑,然后完整的代码在这里做个笔记。
当然我没有详细的将这些代码到底是干嘛的,有点基础的会知道的,不知道就百度吧,想要认真的写一篇博客,真的 是很浪费精力。我这边只是做个记录,很多小细节我都没讲,代码也不是全部完整的,但是该有的重点全部有,你照着这个搞一定可以搞定
public class BusJavaScriptMethods extends JavaScriptMethods{
public BusJavaScriptMethods(Context context, WebView webView) {
super(context, webView);
}
@JavascriptInterface
public void openPhoto(int selectImageIndex) {
// 启动本地相册
((BusWebViewActivity) context).selectImageIndex = selectImageIndex;
((BusWebViewActivity) context).openPhoto();
}
}
private WebSettings webSettings;
webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true); // 启用JS脚本
myWebView.addJavascriptInterface(
new BusJavaScriptMethods(this, myWebView), "jsInterface");
// 这个就是 掉起 相册和拍照
public void openPhoto() {
showPhotoAlertDialog();
}
private void showPhotoAlertDialog() {
final AlertDialog alertDialog = new AlertDialog.Builder(this).create();
alertDialog.show();
Window win = alertDialog.getWindow();
win.getDecorView().setPadding(0, 0, 0, 0);
WindowManager.LayoutParams lp = win.getAttributes();
// 设置弹出框的宽高
lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
// 设置弹出框的位置
win.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL);
win.setAttributes(lp);
win.setContentView(R.layout.dialog_user_photo);
RelativeLayout tempRl = (RelativeLayout) win
.findViewById(R.id.rl_takevidio);
tempRl.setVisibility(View.GONE);
// 取消
RelativeLayout cancelRl = (RelativeLayout) win
.findViewById(R.id.rl_cancle_photo);
cancelRl.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
alertDialog.cancel();
}
});
// 拍照
RelativeLayout takephotoRl = (RelativeLayout) win
.findViewById(R.id.rl_takephoto);
takephotoRl.setBackgroundResource(R.drawable.corners_takephoto_bg);
takephotoRl.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
takePhoto();
alertDialog.cancel();
}
});
// 从手机上传
RelativeLayout fromcameraRl = (RelativeLayout) win
.findViewById(R.id.rl_from_camera);
fromcameraRl.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 调用系统相册获取图片 ,未将图片与上传至服务器
pickPhoto();
alertDialog.cancel();
}
});
}
/***
* 使用相册中的图片
*/
public static final int SELECT_PIC_BY_PICK_PHOTO = 2;
private static final int PHOTO_REQUEST_GALLERY = 0;
private Uri photoUri;
/***
* 使用照相机拍照获取图片
*/
public static final int SELECT_PIC_BY_TACK_PHOTO = 1;
/**
* 拍照获取图片
*/
private void takePhoto() {
// 执行拍照前,应该先判断SD卡是否存在
String SDState = Environment.getExternalStorageState();
if (SDState.equals(Environment.MEDIA_MOUNTED)) {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);// "android.media.action.IMAGE_CAPTURE"
/***
* 需要说明一下,以下操作使用照相机拍照,拍照后的图片会存放在相册中的 这里使用的这种方式有一个好处就是获取的图片是拍照后的原图
* 如果不实用ContentValues存放照片路径的话,拍照后获取的图片为缩略图不清晰
*/
ContentValues values = new ContentValues();
photoUri = this.getContentResolver().insert(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, photoUri);
/** ----------------- */
startActivityForResult(intent, SELECT_PIC_BY_TACK_PHOTO);
} else {
MyApplication.mToast.ToastShow("内存卡不存在");
}
}
/***
* 从相册中取图片
*/
private void pickPhoto() {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_PICK);
intent.setData(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);// 使用以上这种模式,并添加以上两句
startActivityForResult(intent, PHOTO_REQUEST_GALLERY);
}
上面是选择图片
下面是通过onActivityResult 拿到选择的图片或者是拍照的图片,然后调用JS的方法进行传递
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK) {
//裁剪图片
if (requestCode == PHOTO_REQUEST_GALLERY) {
if (data == null) {
MyApplication.mToast.ToastShow("选择图片文件出错");
return;
}
photoUri = data.getData();
if (photoUri == null) {
MyApplication.mToast.ToastShow("选择图片文件出错");
return;
}
setImage(getRealFilePath(this, photoUri));
} else if (requestCode == SELECT_PIC_BY_TACK_PHOTO) {
setImage(getRealFilePath(this, photoUri));
}
}
super.onActivityResult(requestCode, resultCode, data);
}
// 这个是 和 JS 约定的 方法 "javascript:setImg('" + jsonObject + "'," + selectImageIndex + ")"
private void setImage(final String path) {
String base64String = getImageBase64(path);
if (!TextUtils.isEmpty(base64String)) {
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("img", "data:image/png;base64," + base64String);
myWebView.loadUrl("javascript:setImg('" + jsonObject + "'," + selectImageIndex + ")");
} catch (JSONException e) {
}
}
}
public String getImageBase64(String path) {
String base64Image = bitmapToBase64String(path);
return base64Image;
}
//计算图片的缩放值
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
// 根据路径获得图片并压缩,返回bitmap用于显示
public static Bitmap getSmallBitmap(String filePath) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, 480, 800);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(filePath, options);
}
//把bitmap转换成String
public static String bitmapToBase64String(String filePath) {
Bitmap bm = getSmallBitmap(filePath);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.JPEG, 40, baos);
byte[] b = baos.toByteArray();
// Base64.NO_WRAP 这个坑 之前提过
return Base64.encodeToString(b, Base64.NO_WRAP);
}
public static String getRealFilePath(final Context context, final Uri uri) {
if (null == uri) return null;
final String scheme = uri.getScheme();
String data = null;
if (scheme == null)
data = uri.getPath();
else if (ContentResolver.SCHEME_FILE.equals(scheme)) {
data = uri.getPath();
} else if (ContentResolver.SCHEME_CONTENT.equals(scheme)) {
Cursor cursor = context.getContentResolver().query(uri, new String[]{MediaStore.Images.ImageColumns.DATA}, null, null, null);
if (null != cursor) {
if (cursor.moveToFirst()) {
int index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
if (index > -1) {
data = cursor.getString(index);
}
}
cursor.close();
}
}
return data;
}
下面是H5 的代码,对照着看吧,你会看懂的
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=0">
<meta content="telephone=no" name="format-detection">
<meta name="msapplication-tap-highlight" content="no">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta content="yes" name="apple-mobile-web-app-capable">
<meta name="apple-touch-fullscreen" content="yes">
<meta name="format-detection" content="telephone=no">
<meta http-equiv="cleartype" content="on">
<title>图片上传测试</title>
<script src="/Public/api/js/buses/zepto.js"></script>
<script src="/Public/api/js/buses/uploadImg.js"></script>
<script src="/Public/api/js/page.js"></script>
<meta name="__hash__" content="f3d10c9b6eeb384d1bfc1ae58e3cbdf9_b585d710b9132dbe319f2475665d42eb" /></head>
<body>
<p>测试 JS调本地相册</p>
<button onclick="openPhotoFromAndroid()">JS调用相册</button>
<p><img id="img" src="" width="100" height="100"/></p>
<p>参数: <span id="param">0</span></p>
<br>
<br>
<button onclick="backApp()">返回</button>
<script type="text/javascript">
var u = navigator.userAgent;
var isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1; //android终端
var isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios终端
console.log('是否是Android:' + isAndroid);
console.log('是否是iOS:' + isiOS);
/**
* 调用安卓打开相册对应的openPhoto方法
*/
function openPhotoFromAndroid() {
//调用Android的方法启动相册/拍照
//if (isAndroid) alert('当前系统为:安卓');
//if (isiOS) alert('当前系统为:IOS');
window.runApp('openPhoto', rnd(1, 2));
// 这边做过封装,想要掉我的android代码,应该这样写
// window.jsInterface.openPhoto(rnd(1, 2));
// 这个jsInterface不是乱取的是和android那边定义好的,具体看myWebView.addJavascriptInterface(new BusJavaScriptMethods(this, myWebView), "jsInterface");
}
/**
* 安卓调用的js方法,将图片路径返回
* @param path
* @param id
*/
function setImg(path, id) {
try {
alert('返回数据类型:' + typeof path);
path = JSON.parse(path);
alert(' 解析后的数据:' + path.img);
//$('#img').attr('src', path[0].img);
//$('#param').text(id);
//readFile();
} catch (err) {
alert('设置图片路径发生错误:' + err.message);
}
}
/**
* 读取文件并转为base64
* @returns {boolean}
*/
function readFile() {
alert('开始读取图片');
var path = $('#img').attr('src');
lrz(path).then(function (rst) {
console.log(rst);
$('#img').attr('src', rst.base64);
}).catch(function (err) {
// 处理失败会执行
alert('读取图片发生错误1:' + err.message + ' 图片路径为:' + path);
}).always(function () {
// 不管是成功失败,都会执行
});
}
/**
* 调用app退出浏览器的方法
*/
function backApp() {
window.runApp('stopWebActivity');// 这边做过封装,想要掉我的android代码,应该这样写window.jsInterface.stopWebActivity();
}
function rnd(min, max) {
return min + Math.floor(Math.random() * (max - min + 1));
}
</script>
</body>
</html>