快速开发时代,很多原生开发让位于套壳混合开发,webview这个之前没过多关注的控件成了核心,当然webview没想的那么简单,链接一放就完事了?
No! 首先要说下整体的框架,很多的项目都是fragement + webview 实现的 ,比如 首页 菜单 购物车 个人中心 四个 强烈建议还是用一个webview去处理 不要 每个fragement都new一个 webview 不然后期很麻烦的,当然如果加载的url 已经包含有底部导航栏了,那更好。下面开始遇到的问题:
首先就是对JavaScript的支持了:
WebSettings websetting = webview.getSettings();
websetting.setJavaScriptEnabled(true); //与JS交互
紧接着就是 back返回按键,这里需要重写onkeydown方法了
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && webview.canGoBack()) {
webview.goBack();// 返回前一个页面
return true;
} else {
//提示退出应用
AlertDialog.Builder isExit = new AlertDialog.Builder(this);
//设置对话框标题
isExit.setTitle("应用提醒");
//设置对话框消息
isExit.setMessage("确定要退出吗");
// 添加选择按钮并注册监听
isExit.setPositiveButton("确定", diaListener); //diaListener 是实例化Dialog的对象
isExit.setNegativeButton("取消", diaListener);
//对话框显示
isExit.show();
}
return super.onKeyDown(keyCode, event);
}
这个是webview 在activity里的可以直接重写onkeydown方法,但有的webview是在fragement里的 那就需要:
webview.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View view, int i, KeyEvent keyEvent) {
if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
if (i == KeyEvent.KEYCODE_BACK && webview.canGoBack()) {
//这里处理返回键事件
Log.d(" FRAGEMENT ","CAN GOBACK = " + webview.canGoBack());
webview_fuwu.goBack();
return true;
}
}
return false;
}
});
当遇到第三方登录 如跳到 支付宝 QQ 微信 等 再按back键时,那么成功进入又一个webview的问题点 重定向问题 ,你back会变成刷新一样,出不去了,这里我是在对webview的重写方法
shouldOverrideUrlLoading
中处理
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// TODO Auto-generated method stub
Log.d(TAG, "加载的url==" + url);
//view.loadUrl(url);
return false ;
});
把view.loadUrl(url); 注释掉
有时当反复点击了back键还是没返回,log信息报 UnimplementedWebViewApi: Unimplemented WebView method onKeyDown called from: android.webkit.WebView.onKeyDown(WebView.java:2178)
的,也可以用上面的方法 解决。
对一些加载失败的连接处理时:
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
Log.e("MianActivity", "ERROR == " + errorCode + "--->" + description + "---->" + failingUrl);
//用javascript隐藏系统定义的404页面信息
String data = "";
view.loadUrl("javascript:document.body.innerHTML=\"" + data + "\"");
if (errorCode == -10 && description.equals("net::ERR_UNKNOWN_URL_SCHEME") && failingUrl.equals("****")) {
//也可以截获错误信息在这个做自定义的处理
// Callphone_showDialog();
} else if (errorCode == -10 && description.equals("net::ERR_UNKNOWN_URL_SCHEME") && failingUrl.equals("***")) {
//自定义处理
}
}
webview开发加载的毕竟是网页啊,要是断网了呢,空白啊,所以还要做缓存处理,这个方法在webview的
webview.setWebViewClient(new WebViewClient()之前
/**
* 设置webview 缓存
*/
private void WebView_Set() {
WebSettings websetting = webview.getSettings();
websetting.setJavaScriptEnabled(true); //与JS交互
websetting.setDomStorageEnabled(true); //开启DOM形式存储
websetting.setDatabaseEnabled(true); //开启数据库形式存储
String appCacheDir = this.getDir("cache", Context.MODE_PRIVATE).getPath(); //缓存数据的存储地址
websetting.setAppCachePath(appCacheDir);
websetting.setAppCacheEnabled(true); //开启缓存功能
websetting.setCacheMode(WebSettings.LOAD_DEFAULT); //缓存模式
websetting.setAllowFileAccess(true);// 设置允许访问文件数据
websetting.setAppCacheMaxSize(1024 * 1024 * 8); //设置缓存文件大小
}
网上也说缓存模式设置这样:
websetting.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); //缓存模式
两者的区别为:
LOAD_DEFAULT,根据cache-control决定是否从网络上取数据
LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据
使用LOAD_CACHE_ELSE_NETWORK会出现,比如你加购物车了,但到购物车去看没有的现象
webview除了加载url的setWebViewClient 外还有一个专用辅助方法
webview.setWebChromeClient(new WebChromeClient() {
});
用于处理弹框 警示信息 图片 文件选择处理等, 因为js里面的alert confirm 事件即使有setjavascriptenabled(true) 但仍是不识别的,同样需要在setWebChromeClient里面重写,用android的方法显示:
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
Log.d(TAG, "url =" + url + "----message=" + message + "-----result==" + result);
final JsResult jr = result;
if (message.contains("*******")) {
// SweetAlertDialog 自定义的一个dialog
SweetAlertDialog sd = new SweetAlertDialog(MainActivity.this, SweetAlertDialog.CUSTOM_IMAGE_TYPE);
sd.setTitleText("***********");
sd.setConfirmText("确定");
sd.setCancelable(false);
sd.setCanceledOnTouchOutside(false);
sd.setConfirmClickListener(new SweetAlertDialog.OnSweetClickListener() {
@Override
public void onClick(SweetAlertDialog sDialog) {
jr.confirm();
sDialog.dismiss();
}
});
sd.show();
}else {
//处理其他alert事件
}
@Override
public boolean onJsConfirm(final WebView view, String url, String message, JsResult result) {
Log.d(TAG, "url =" + url + "----message=" + message + "-----result==" + result);
final JsResult jr = result;
if (message.contains("***********")) {
// SweetAlertDialog 是我自定义的一个dialog
SweetAlertDialog sd = new SweetAlertDialog(MainActivity.this, SweetAlertDialog.WARNING_TYPE);
sd.setTitleText("************?");
// .setContentText("Won't be able to recover this file!")
sd.setCancelText("取消");
sd.setConfirmText("确定");
sd.showCancelButton(true);
sd.setCancelable(false);
sd.setCanceledOnTouchOutside(false);
sd.setCancelClickListener(new SweetAlertDialog.OnSweetClickListener() {
@Override
public void onClick(SweetAlertDialog sDialog) {
// reuse previous dialog instance, keep widget user state, reset them if you need
sDialog.dismiss();
jr.cancel();
}
});
sd.setConfirmClickListener(new SweetAlertDialog.OnSweetClickListener() {
@Override
public void onClick(SweetAlertDialog sDialog) {
jr.confirm();
Toast.makeText(MainActivity.this, "移出成功", Toast.LENGTH_SHORT).show();
sDialog.dismiss();
}
});
sd.show();
return true;
}
对于webview上传文件图片同样在setWebViewChromeClient中写入方法:
//3.0++版本
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
Log.d(TAG, "3.0++");
openFileChooserImpl(uploadMsg);
}
//3.0--版本
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
Log.d(TAG, "3.0");
openFileChooserImpl(uploadMsg);
}
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
Log.d(TAG, "4.1");
openFileChooserImpl(uploadMsg);
}
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
Log.d(TAG, "5.0++");
onenFileChooseImpleForAndroid(filePathCallback);
return true;
}
android在5.0之后才将webview打开文件图片方法固定,所以会有两个不同的方法,android会自己根据版本选择 下面代码会给出
这里重点说下android4.4版本的上传路径的重新获取和android4.4.4打开文件或图片取消的情况
getRealPathFromURL( ) 是对选中的图片或文件路径的重新修改,不然上传会卡在哪里,
onActivityResult( ) 是当打开文件或图片列表时,用户按back键取消返回时,传入intent 为空,所以将mUploadMessage.onReceiveValue(null) 也赋予null值,才可正常返回,
public ValueCallback<Uri> mUploadMessage;
private void openFileChooserImpl(ValueCallback<Uri> uploadMsg) {
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
}
public ValueCallback<Uri[]> mUploadMessageForAndroid5;
private void onenFileChooseImpleForAndroid(ValueCallback<Uri[]> filePathCallback) {
mUploadMessageForAndroid5 = filePathCallback;
Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
contentSelectionIntent.setType("image/*");
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser");
startActivityForResult(chooserIntent, FILECHOOSER_RESULTCODE_FOR_ANDROID_5);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (requestCode == FILECHOOSER_RESULTCODE) {
if (null == mUploadMessage)
return;
if (intent == null) {
Toast.makeText(MainActivity.this, "已取消", Toast.LENGTH_SHORT).show();
mUploadMessage.onReceiveValue(null);
mUploadMessage = null;
return;
}
Log.e(TAG, "requestCode=" + requestCode + "resultCode=" + resultCode + "intent.getdata()==");
if (Build.VERSION.SDK_INT == 19) {
String realPathFromURI = getRealPathFromURI(intent.getData());
File file = new File(realPathFromURI);
if (file.exists()) {
Uri uri = Uri.fromFile(file);
Log.e(TAG, "文件uri-->" + uri);
Toast.makeText(MainActivity.this, "图片上传中...", Toast.LENGTH_SHORT).show();
mUploadMessage.onReceiveValue(uri);
}
}
mUploadMessage = null;
} else if (requestCode == FILECHOOSER_RESULTCODE_FOR_ANDROID_5) {
if (null == mUploadMessageForAndroid5)
return;
Uri result = (intent == null || resultCode != RESULT_OK) ? null : intent.getData();
if (result != null) {
Toast.makeText(MainActivity.this, "图片上传中...", Toast.LENGTH_SHORT).show();
mUploadMessageForAndroid5.onReceiveValue(new Uri[]{result});
} else {
mUploadMessageForAndroid5.onReceiveValue(new Uri[]{});
}
mUploadMessageForAndroid5 = null;
}
}
public String getRealPathFromURI(Uri contentUri) {
String res = null;
String[] proj = {MediaStore.Images.Media.DATA};
Cursor cursor = MainActivity.this.getContentResolver().query(contentUri, proj, null, null, null);
if (cursor.moveToFirst()) {
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
res = cursor.getString(column_index);
}
cursor.close();
return res;
}
这些都是项目中遇到的吧,不大也不小,记录下来,也希望能帮助到有同样问题的你。