Android与H5交互,以及WebView加载进度条

前言

虽然项目主体是原生Android开发,但是难免会遇到一些H5页面,如:首页Banner的活动推送。我们之所以选择H5来实现,因为它相对灵活,可以多平台运行,从而提高开发效率,节约开发成本。

文章分为两个章节:Android与H5交互、WebView加载进度条。

一、Android与H5交互

Android与H5的简单交互.gif
1、WebView加载HTML页面
// 加载一个网页:
webView.loadUrl("http://www.baidu.com/");

// 加载assets文件夹下的test.html页面
webView.loadUrl("file:///android_asset/test.html");

为了点击HTML的内部的链接不跳到外部浏览器,我们还需要setWebViewClient:

// 帮助WebView处理各种通知、请求事件,不写html页面里的链接会跳到外部浏览器哦
mWebView.setWebViewClient(new WebViewClient());
2、本地Java调用js方法

想要调用js方法,就需要让webView支持才可以:

WebSettings webSettings = mWebView.getSettings();
// 设置为可调用js方法
webSettings.setJavaScriptEnabled(true);

若调用的js方法没有返回值,则直接可以调用mWebView.loadUrl("javascript:show()"),其中show是js中的方法;
若有返回值时我们可以调用mWebView.evaluateJavascript()方法:

// 直接访问H5里不带返回值的方法,show()为H5里的方法
mWebView.loadUrl("JavaScript:show()");

// Android调用有返回值js方法,安卓4.4以上才能用这个方法
mWebView.evaluateJavascript("sum(1,2)", new ValueCallback<String>() {
    @Override
    public void onReceiveValue(String value) {
        Log.e(TAG, "js返回的结果为 = " + value);
        Toast.makeText(MainActivity.this, "js返回的结果为 = " + value, Toast.LENGTH_LONG).show();
    }
});

js代码如下:

<script type="text/javascript">

    // 无参无返回值的方法
    function show(){
        document.getElementById("p").innerHTML="hello world";
    }

    // 有参有返回值的方法
    function sum(a,b){
        return a+b;
    }
</script>
3、js调用本地Java方法

在Android4.2以上可以直接使用@JavascriptInterface注解来声明,下面是在一个本地Java方法:

 public class JsInteration {

    // 一定要写,不然H5调不到这个方法
    @JavascriptInterface
    public String back() {
        return "我是java方法的返回值";
    }

    @JavascriptInterface
    public void goNewAct(String str) {
        // 也可接收js中的参数
        Log.e(TAG, "goNewAct: " + str);
        startActivity(new Intent(MainActivity.this, NewActivity.class));
    }
}

定义完这个方法后再调用mWebView.addJavascriptInterface()方法:

// 打开js接口給H5调用,参数1为本地类名,参数2为别名;h5用window.别名.类名里的方法名才能调用方法里面的内容
// 例如:window.android.back();
mWebView.addJavascriptInterface(new JsInteration(), "android");

js代码如下:

<script type="text/javascript">
     function s(){
         // 调用原生的方法,android为约定的别名;back()为原生的方法
         var result=window.android.back();
         // 将返回结果显示在id为p的控件上
         document.getElementById("p").innerHTML=result;
     }

     function goNewAct(str){
         // 打开原生界面,也可以传递参数
         window.android.goNewAct(str);
    }
</script>
4、WebView监听返回键

当webview包含多个页面的时候,当我们点击返回键的时候,更多时候我们需要的是返回上个页面,而不是直接关闭webview。所以我们需要重写返回键事件:

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if ((keyCode == KeyEvent.KEYCODE_BACK)) {
        if (webView.canGoBack()) {
            // goBack()表示返回WebView的上一页面
            webView.goBack();
            return true;
        } else {
            finish();
            return true;
        }
    }
    return false;
}

项目源码在文章末尾给出,已上传至GitHub。

二、WebView加载进度条

在实现基本功能的同时,我们还要注重用户体验,所以加载进度条也就变得不可或缺。本着用户是上帝的原则,还能说些什么(摊手)。。


ProgressWebView.gif
实现思路

首先在自定义的WebView中加入了一个水平方向的ProgressBar,然后为这个ProgressBar设置progressDrawable;创建WebChromeClient 继承 WebChromeClie,监听加载进度的变化onProgressChanged,并做出相应的设置;重写onScrollChanged方法,防止因滑动而造成ProgressBar的移动。
完整代码如下:

public class ProgressWebView extends WebView {

    private ProgressBar mProgressBar;

    public ProgressWebView(Context context) {
        super(context);
    }

    public ProgressWebView(Context context, AttributeSet attrs) {
        super(context, attrs);

        // 使用ProgressBar作为加载进度条,当然也可使用其他view作为进度显示
        mProgressBar = new ProgressBar(context, null, android.R.attr.progressBarStyleHorizontal);
        LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 8);
        mProgressBar.setLayoutParams(layoutParams);

        // 获取Drawable资源,并为ProgressBar设置setProgressDrawable
        Drawable drawable = ContextCompat.getDrawable(context, R.drawable.web_progress_bar_states);
        mProgressBar.setProgressDrawable(drawable);
        addView(mProgressBar);

        // 辅助WebView处理js的对话框,网站图标,网站title,加载进度等
        setWebChromeClient(new WebChromeClient());
    }

    public class WebChromeClient extends android.webkit.WebChromeClient {
        @Override
        public void onProgressChanged(WebView view, int newProgress) {
            if (newProgress == 100) {
                // 加载完成,将进度条隐藏
                mProgressBar.setVisibility(GONE);
            } else {
                if (mProgressBar.getVisibility() == GONE) {
                    mProgressBar.setVisibility(VISIBLE);
                }
                // 设置加载进度
                mProgressBar.setProgress(newProgress);
            }
            super.onProgressChanged(view, newProgress);
        }
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        // 使进度条始终固定在顶部位置,快速滑动时还是会有影响,日常使用ok
        // 使用ViewGroup.LayoutParams滑动会消失,原因不详,求大神告知
        LayoutParams lp = (LayoutParams) mProgressBar.getLayoutParams();
        lp.x = l;
        lp.y = t;
        mProgressBar.setLayoutParams(lp);
        super.onScrollChanged(l, t, oldl, oldt);
    }
}

进度条Drawable这里写的比较简单,当然也可以写的炫酷点。web_progress_bar_states.xm代码如下:

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@android:id/background">
        <color android:color="@color/transparent"/>
    </item>

    <item android:id="@android:id/progress">
        <clip>
            <shape>
                <gradient
                    android:centerColor="#aaff0000"
                    android:endColor="#ff0000"
                    android:startColor="#99ff0000"/>
            </shape>
        </clip>
    </item>
</layer-list>

完整的MainActivity代码:

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "--->";
    private ProgressWebView mWebView;

    @SuppressLint({"JavascriptInterface", "SetJavaScriptEnabled"})
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mWebView = findViewById(R.id.webview);

        // 加载本地asset下面的test.html文件
        mWebView.loadUrl("file:///android_asset/test.html");
        // 加载普通网页
//        mWebView.loadUrl("http://image.baidu.com/search/index?tn=baiduimage&ct=201326592&lm=-1&cl=2&ie=gbk&word=%CD%BC%C6%AC&fr=ala&ala=1&alatpl=others&pos=0");

        WebSettings webSettings = mWebView.getSettings();
        // 打开js支持
        webSettings.setJavaScriptEnabled(true);

        // 打开js接口給H5调用,参数1为本地类名,参数2为别名;h5用window.别名.类名里的方法名才能调用方法里面的内容,例如:window.android.back()
        mWebView.addJavascriptInterface(new JsInteration(), "android");
        // 帮助WebView处理各种通知、请求事件,不写html页面里的链接会跳到外部浏览器哦
        mWebView.setWebViewClient(new WebViewClient());
        // 辅助WebView处理js的对话框,网站图标,网站title,加载进度等,非必须(由于ProgressWebView重写了该方法,这里应该注销,否则自定义无效)
//        mWebView.setWebChromeClient(new WebChromeClient());

        // 缓存模式(方便测试加载进度条)
        webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);
    }

    /**
     * 自己写一个类,里面是提供给H5访问的方法
     */
    public class JsInteration {
        /**
         * 一定要写,不然H5调不到这个方法
         */
        @JavascriptInterface
        public String back() {
            return "我是java方法的返回值";
        }

        @JavascriptInterface
        public void goNewAct(String str) {
            // 可接收js中的参数
            Log.e(TAG, "goNewAct: " + str);
            Toast.makeText(MainActivity.this, str, Toast.LENGTH_LONG).show();
            startActivity(new Intent(MainActivity.this, NewActivity.class));
        }
    }

    /**
     * 点击按钮,访问H5里带返回值的方法
     */
    @TargetApi(Build.VERSION_CODES.KITKAT)
    public void onClick(View v) {

        // 直接访问H5里不带返回值的方法,show()为H5里的方法
        mWebView.loadUrl("JavaScript:show()");

        // 传固定字符串可以直接用单引号括起来
        // 访问H5里带参数的方法,alertMessage(message)为H5里的方法
        mWebView.loadUrl("javascript:alertMessage('哈哈')");

        // 当出入变量名时,需要用转义符隔开
        String content = "2333";
        mWebView.loadUrl("javascript:alertMessage(\"" + content + "\")");

        // Android调用有返回值js方法,安卓4.4以上才能用这个方法
        mWebView.evaluateJavascript("sum(1,2)", new ValueCallback<String>() {
            @Override
            public void onReceiveValue(String value) {
                Log.e(TAG, "js返回的结果为 = " + value);
                Toast.makeText(MainActivity.this, "js返回的结果为 = " + value, Toast.LENGTH_LONG).show();
            }
        });
    }
}

项目源码:https://github.com/princekin-f/webview
随缘点赞,传播正能量~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,335评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,895评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,766评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,918评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,042评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,169评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,219评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,976评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,393评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,711评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,876评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,562评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,193评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,903评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,699评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,764评论 2 351

推荐阅读更多精彩内容