android 网页设计 android 内嵌网页_ide

新增了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);
    }
}