新增了android版块内容,因为最近一直客串android开发,这个过程中学习到不少有趣的知识点,有必要记录分享一下😝
Android 可以通过 webview 来内嵌 html 页面,从而实现灵活的信息展示;最近客串 android 开发中,正好遇到了这样的一个小场景,所以简单的记录一下 Android 与 html 之间的交互,包含以下内容
- webview 的基本设置
- Andriod 调用 js 方法
- js 调用 android 方法
- 图片长按下载
I. 内嵌 html
1. 布局
我们这里主要介绍通过原生的WebView
来嵌入 Html 网页,首先是在布局文件中,添加 webview 控件
<WebViewandroid:id="@+id/wv_detail"android:layout_width="match_parent"android:layout_height="match_parent"
/>
<WebViewandroid:id="@+id/wv_detail"android:layout_width="match_parent"android:layout_height="match_parent"
/>
2. WebView 配置
在 Activity 类中,获取WebView
控件
// butterknife 方式,也可以直接通过 findViewById 获取
@BindView(R.id.wv_detail)
WebView webView;
// butterknife 方式,也可以直接通过 findViewById 获取
@BindView(R.id.wv_detail)
WebView webView;
webview 的基本配置参数,比如是否可以缩放,自适应等
//声明WebSettings子类
WebSettings webSettings = webView.getSettings();
//如果访问的页面中要与Javascript交互,则webview必须设置支持Javascript
webSettings.setJavaScriptEnabled(true);
//支持插件
webSettings.setPluginsEnabled(true);
//设置自适应屏幕,两者合用
webSettings.setUseWideViewPort(true); //将图片调整到适合webview的大小
webSettings.setLoadWithOverviewMode(true); // 缩放至屏幕的大小
//缩放操作
webSettings.setSupportZoom(true); //支持缩放,默认为true。是下面那个的前提。
webSettings.setBuiltInZoomControls(true); //设置内置的缩放控件。若为false,则该WebView不可缩放
webSettings.setDisplayZoomControls(false); //隐藏原生的缩放控件
//其他细节操作
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); //关闭webview中缓存
webSettings.setAllowFileAccess(true); //设置可以访问文件
webSettings.setJavaScriptCanOpenWindowsAutomatically(true); //支持通过JS打开新窗口
webSettings.setLoadsImagesAutomatically(true); //支持自动加载图片
webSettings.setDefaultTextEncodingName("utf-8");//设置编码格式
//重写shouldOverrideUrlLoading()方法,使得打开网页时不调用系统浏览器, 而是在本WebView中显示
webView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
//声明WebSettings子类
WebSettings webSettings = webView.getSettings();
//如果访问的页面中要与Javascript交互,则webview必须设置支持Javascript
webSettings.setJavaScriptEnabled(true);
//支持插件
webSettings.setPluginsEnabled(true);
//设置自适应屏幕,两者合用
webSettings.setUseWideViewPort(true); //将图片调整到适合webview的大小
webSettings.setLoadWithOverviewMode(true); // 缩放至屏幕的大小
//缩放操作
webSettings.setSupportZoom(true); //支持缩放,默认为true。是下面那个的前提。
webSettings.setBuiltInZoomControls(true); //设置内置的缩放控件。若为false,则该WebView不可缩放
webSettings.setDisplayZoomControls(false); //隐藏原生的缩放控件
//其他细节操作
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); //关闭webview中缓存
webSettings.setAllowFileAccess(true); //设置可以访问文件
webSettings.setJavaScriptCanOpenWindowsAutomatically(true); //支持通过JS打开新窗口
webSettings.setLoadsImagesAutomatically(true); //支持自动加载图片
webSettings.setDefaultTextEncodingName("utf-8");//设置编码格式
//重写shouldOverrideUrlLoading()方法,使得打开网页时不调用系统浏览器, 而是在本WebView中显示
webView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
3. 回退与销毁
内嵌 html,当存在多个 html 页面跳转时,如果直接后退,可能的结果就是回到上一个 activity,而不是我们预期的回到上一个 html 页面,因此我们需要处理一下回退事件
@Override
public void onBackPressed() {
if (webView == null) {
return;
}
if (webView.canGoBack()) {
webView.goBack();
return;
}
super.onBackPressed();
}
@Override
public void onBackPressed() {
if (webView == null) {
return;
}
if (webView.canGoBack()) {
webView.goBack();
return;
}
super.onBackPressed();
}
其次在退出 activity 时,别忘了销毁 WebView
@Override
protected void onDestroy() {
if (webView != null) {
final ViewGroup viewGroup = (ViewGroup) webView.getParent();
if (viewGroup != null) {
viewGroup.removeView(webView);
}
webView.destroy();
}
super.onDestroy();
}
@Override
protected void onDestroy() {
if (webView != null) {
final ViewGroup viewGroup = (ViewGroup) webView.getParent();
if (viewGroup != null) {
viewGroup.removeView(webView);
}
webView.destroy();
}
super.onDestroy();
}
II. Android 与 JS 交互
1. 加载 html
基本配置完毕之后,开始加载 html 页面,主要是借助loadUrl
来实现
// 加载assets 资源文件下html
webView.loadUrl("file:///android_asset/bs.html");
// 加载在线的html
webView.loadUrl("https://mweb.hhui.top/")
// 加载assets 资源文件下html
webView.loadUrl("file:///android_asset/bs.html");
// 加载在线的html
webView.loadUrl("https://mweb.hhui.top/")
2. js 调用 android 方法
为了实现 js 调用 android 方法,我们新建一个桥接类
public class JsBrager {
private Activity activity;
public JsBrager(Activity activity) {
this.activity = activity;
}
/**
* 显示提示信息
*
* @param message
*/
@JavascriptInterface
public void toastMessage(String message) {
Toast.makeText(activity, "通过Natvie传递的Toast:" + message, Toast.LENGTH_LONG).show();
}
}
public class JsBrager {
private Activity activity;
public JsBrager(Activity activity) {
this.activity = activity;
}
/**
* 显示提示信息
*
* @param message
*/
@JavascriptInterface
public void toastMessage(String message) {
Toast.makeText(activity, "通过Natvie传递的Toast:" + message, Toast.LENGTH_LONG).show();
}
}
通过webView.addJavascriptInterface
来关联
// 请注意第二个参数,在js中使用`android.toastMessgae`来实现调用Android的方法
webView.addJavascriptInterface(new JsBrager(this), "android");
// 请注意第二个参数,在js中使用`android.toastMessgae`来实现调用Android的方法
webView.addJavascriptInterface(new JsBrager(this), "android");
一个简单的 html 页面如
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
head>
<body>
<center>
<h2>简单的测试h2>
center>
<div> <span><img src="https://cdn.pixabay.com/photo/2015/05/26/22/33/kindle-785686_960_720.jpg"/>span>
div>
<div>
<h3>Android Html交互:h3>
<button onclick="show()" id='btn'>toastbutton>
div>
<br/>
<script type="text/javascript">function show() {
android.toastMessage("js 点击");
}function change_theme(bg, txt) {var body = document.getElementsByTagName('body')[0];
body.style.background = bg;
body.style.color = txt;
}function callJS() {return "hello";
}script>
body>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
head>
<body>
<center>
<h2>简单的测试h2>
center>
<div> <span><img src="https://cdn.pixabay.com/photo/2015/05/26/22/33/kindle-785686_960_720.jpg"/>span>
div>
<div>
<h3>Android Html交互:h3>
<button onclick="show()" id='btn'>toastbutton>
div>
<br/>
<script type="text/javascript">function show() {
android.toastMessage("js 点击");
}function change_theme(bg, txt) {var body = document.getElementsByTagName('body')[0];
body.style.background = bg;
body.style.color = txt;
}function callJS() {return "hello";
}script>
body>
3. android 调用 js 方法
android 调用 js 方法,也是通过loadUrl
来实现的,需要注意的前缀,以及是否需要传参返回结果
// 修改主题色
webview.loadUrl("javascript:change_theme('#ffffff','#000000')");
// 修改主题色
webview.loadUrl("javascript:change_theme('#ffffff','#000000')");
如果有返回结果,则可以考虑下面的写法
// 只需要将第一种方法的loadUrl()换成下面该方法即可
mWebView.evaluateJavascript("javascript:callJS()", new ValueCallback() {@Overridepublic void onReceiveValue(String value) {//此处为 js 返回的结果
Toast.makeText(activity, "js回调:" + message, Toast.LENGTH_LONG).show();
}
});
// 只需要将第一种方法的loadUrl()换成下面该方法即可
mWebView.evaluateJavascript("javascript:callJS()", new ValueCallback() {@Overridepublic void onReceiveValue(String value) {//此处为 js 返回的结果
Toast.makeText(activity, "js回调:" + message, Toast.LENGTH_LONG).show();
}
});
III. 长按图片下载
下面实现来自于搜索,忘了具体的来源了(主要还是习惯不好,拷代码,没有拷出处,我的锅)...
一种常见的 case,长按图片下载,下面给出基本的额使用套路
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
String picFile = (String) msg.obj;
String[] split = picFile.split("/");
String fileName = split[split.length - 1];
try {
MediaStore.Images.Media.insertImage(getApplicationContext().getContentResolver(), picFile, fileName, null);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// 最后通知图库更新
getApplicationContext().sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + picFile)));
Toast.makeText(SquareDetailActivity.this, showContent("图片保存图库成功"), Toast.LENGTH_LONG).show();
}
};
// 长按点击实现图片下载
webView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
final WebView.HitTestResult hitTestResult = webView.getHitTestResult();
// 如果是图片类型或者是带有图片链接的类型
if (hitTestResult.getType() == WebView.HitTestResult.IMAGE_TYPE ||
hitTestResult.getType() == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) {
// 弹出保存图片的对话框
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle("提示");
builder.setMessage("保存图片到本地");
builder.setPositiveButton(LanguageFormatHelper.formatContent("确认"), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
String url = hitTestResult.getExtra();
// 下载图片到本地
DownPicUtil.downPic(url, new DownPicUtil.DownFinishListener() {
@Override
public void getDownPath(String s) {
Message msg = Message.obtain();
msg.obj = s;
handler.sendMessage(msg);
}
});
}
});
builder.setNegativeButton(LanguageFormatHelper.formatContent("取消"), new DialogInterface.OnClickListener() {
// 自动dismiss
@Override
public void onClick(DialogInterface dialogInterface, int i) {
}
});
AlertDialog dialog = builder.create();
dialog.show();
}
return true;
}
});
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
String picFile = (String) msg.obj;
String[] split = picFile.split("/");
String fileName = split[split.length - 1];
try {
MediaStore.Images.Media.insertImage(getApplicationContext().getContentResolver(), picFile, fileName, null);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// 最后通知图库更新
getApplicationContext().sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + picFile)));
Toast.makeText(SquareDetailActivity.this, showContent("图片保存图库成功"), Toast.LENGTH_LONG).show();
}
};
// 长按点击实现图片下载
webView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
final WebView.HitTestResult hitTestResult = webView.getHitTestResult();
// 如果是图片类型或者是带有图片链接的类型
if (hitTestResult.getType() == WebView.HitTestResult.IMAGE_TYPE ||
hitTestResult.getType() == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) {
// 弹出保存图片的对话框
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle("提示");
builder.setMessage("保存图片到本地");
builder.setPositiveButton(LanguageFormatHelper.formatContent("确认"), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
String url = hitTestResult.getExtra();
// 下载图片到本地
DownPicUtil.downPic(url, new DownPicUtil.DownFinishListener() {
@Override
public void getDownPath(String s) {
Message msg = Message.obtain();
msg.obj = s;
handler.sendMessage(msg);
}
});
}
});
builder.setNegativeButton(LanguageFormatHelper.formatContent("取消"), new DialogInterface.OnClickListener() {
// 自动dismiss
@Override
public void onClick(DialogInterface dialogInterface, int i) {
}
});
AlertDialog dialog = builder.create();
dialog.show();
}
return true;
}
});
图片下载类
/**
* 图片下载的工具类
*/
public class DownPicUtil {
/**
* 下载图片,返回图片的地址
*
* @param url
*/
public static void downPic(String url, DownFinishListener downFinishListener) {
// 获取存储卡的目录
String filePath = Environment.getExternalStorageDirectory().getPath();
File file = new File(filePath + File.separator + "webViewCache");
if (!file.exists()) {
file.mkdir();
}
loadPic(file.getPath(), url, downFinishListener);
}
private static void loadPic(final String filePath, final String url, final DownFinishListener downFinishListener) {
Log.e("下载图片的url", url);
new AsyncTask() {
String fileName;
InputStream is;
OutputStream out;@Overrideprotected String doInBackground(Void... voids) {// 下载文件的名称
String[] split = url.split("/");
String newString = split[split.length - 1];if (newString.length() >= 20) {
fileName = newString.substring(newString.length() - 20, newString.length() - 1);
} else {
fileName = newString;
}// 创建目标文件,不是文件夹
File picFile = new File(filePath + File.separator + fileName);if (picFile.exists()) {return picFile.getPath();
}try {
URL picUrl = new URL(url);//通过图片的链接打开输入流
is = picUrl.openStream();if (is == null) {return null;
}
out = new FileOutputStream(picFile);byte[] b = new byte[1024];int end;while ((end = is.read(b)) != -1) {
out.write(b, 0, end);
}
Log.e("OK??", "----------");if (is != null) {
is.close();
}if (out != null) {
out.close();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}return picFile.getPath();
}@Overrideprotected void onPostExecute(String s) {super.onPostExecute(s);if (s != null) {
downFinishListener.getDownPath(s);
}
}
}.execute();
}//下载完成回调的接口public interface DownFinishListener {void getDownPath(String s);
}
}
/**
* 图片下载的工具类
*/
public class DownPicUtil {
/**
* 下载图片,返回图片的地址
*
* @param url
*/
public static void downPic(String url, DownFinishListener downFinishListener) {
// 获取存储卡的目录
String filePath = Environment.getExternalStorageDirectory().getPath();
File file = new File(filePath + File.separator + "webViewCache");
if (!file.exists()) {
file.mkdir();
}
loadPic(file.getPath(), url, downFinishListener);
}
private static void loadPic(final String filePath, final String url, final DownFinishListener downFinishListener) {
Log.e("下载图片的url", url);
new AsyncTask() {
String fileName;
InputStream is;
OutputStream out;@Overrideprotected String doInBackground(Void... voids) {// 下载文件的名称
String[] split = url.split("/");
String newString = split[split.length - 1];if (newString.length() >= 20) {
fileName = newString.substring(newString.length() - 20, newString.length() - 1);
} else {
fileName = newString;
}// 创建目标文件,不是文件夹
File picFile = new File(filePath + File.separator + fileName);if (picFile.exists()) {return picFile.getPath();
}try {
URL picUrl = new URL(url);//通过图片的链接打开输入流
is = picUrl.openStream();if (is == null) {return null;
}
out = new FileOutputStream(picFile);byte[] b = new byte[1024];int end;while ((end = is.read(b)) != -1) {
out.write(b, 0, end);
}
Log.e("OK??", "----------");if (is != null) {
is.close();
}if (out != null) {
out.close();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}return picFile.getPath();
}@Overrideprotected void onPostExecute(String s) {super.onPostExecute(s);if (s != null) {
downFinishListener.getDownPath(s);
}
}
}.execute();
}//下载完成回调的接口public interface DownFinishListener {void getDownPath(String s);
}
}