+ h5 native_native + h5 - CSDN
  • 想要编写native+h5混合的app,首先你必须要知道一些js/html/css的东西,特别是js。我有一两年没有碰前段的东西了,也忘记的差不多了,...接下来就来说说native+h5 混合开发的第一步,伟大航海路线的第一步,弄一条船。

    想要编写native+h5混合的app,首先你必须要知道一些js/html/css的东西,特别是js。

    我有一两年没有碰前段的东西了,也忘记的差不多了,不过混合开发的初步还是没问题的。

    接下来就来说说native+h5 混合开发的第一步,伟大航海路线的第一步,弄一条船。

    先来说说大概:
    native app中布局很简单,就一个webview,这里就不贴出来了。

    前端包含一个index.html 和一个index.js;里面是测试数据

    先来说说实现哪些功能:
    1,native app显示html
    2,模拟服务器端的html和js
    3,通过协商的方法完成 native app调用前段js中的数据
    4,通过协商的方法完成native app 回馈给js信息(也就是app和前端 js的双向调用)

    先看看成功调用js后的效果图:
    这里写图片描述
    这里写图片描述
    这里写图片描述
    这里写图片描述
    这里写图片描述

    1,index.html代码

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
        <title>Hello Native+H5!</title>
        <script type="text/javascript" charset="UTF-8" src="../js/index.js"></script>
        <script type="text/javascript" charset="UTF-8">
    
                function showMessage() {
                    javascript:control.showToast("欢迎使用native+h5");
                }
            </script>
    </head>
    <body>
        <input type="button" value="欢迎1" onclick="showToast('Hell_Native+H5')" />
        <input type="button" value="欢迎2" onclick="showMessage()" />
        <input type="button" value="警告" onclick="onAlert()" />
        <input type="button" value="确定" onclick="onConfirm()" />
        <input type="button" value="提示" onclick="onPrompt()" />
    </body>
    </html>

    2,index.js代码

    function loadingImg() {
        alert("欢迎使用native+h5");
    }
    
    //android中的toast显示信息框
    function showToast(toast) {
        javascript: android.showToast(toast);
    }
    //警告  
    function onAlert() {
        alert("This is a alert sample from html");
    }
    //确定  
    function onConfirm() {
        //得到true/false
        var b = confirm("are you sure to login?");
        javascript: control.showToast("your choice is " + b);
    }
    //提示  
    function onPrompt() {
        //得到输入内容  
        var b = prompt("please input your password", "aaa");
        javascript: control.showToast("your input is " + b);
    }
    
    //用于app向js反向输出日志
    function info(msg) {
        console.info(msg);
    }

    3,MainActivity.javaM代码

    package com.example.hellonativeh5;
    
    public class MainActivity extends Activity { 
        private WebView native_web;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            native_web = (WebView) findViewById(R.id.native_web);
            initWeb();
        }
    
        /**
         * 初始化webview 允许js后要小心XSS攻击了
         * 
         * @JavascriptInterface这个注解 在4.2以上修复了,但是需要注意兼容性
         */
        @SuppressLint("SetJavaScriptEnabled")
        private void initWeb() {
            WebSettings webSettings = native_web.getSettings();
            //
            webSettings.setJavaScriptEnabled(true);
    
            // 第一个参数里面包含了需要从js调用的方法或者反向回馈给js的方法
            // 第二个参数调用的是和js里规定的方法一样,调用function:android中的方法showToast,前缀javascript可有可无
            // js代码:
            // function showToast(toast) {
            // javascript:android.showToast(toast);
            // }
            // 在js中,android.showToast(toast);是不能被执行的,可能在html中执行会报错。因为它是js和原生app协商后定义的方法,不是js原有方法
            native_web.addJavascriptInterface(new JsInterface(), "android");
            // 设置拦截js中的三种弹框
            native_web.setWebChromeClient(new MyWebChromeClien());
            // 监听点击的每一个url,做自己的特殊处理用
            native_web.setWebViewClient(new MyWebViewClient());
            // 加载项目asset文件夹下的本地html
            native_web.loadUrl("file:///android_asset/h5/html/index.html");// 加载本地的html布局文件
            // 加载 asset 文件的内容,第二种加载本地html的方法
            String tpl = getFromAssets("h5/html/index.html");
            // native_web.loadDataWithBaseURL(null, tpl, "text/html", "utf-8",null);
        }
    
        /*
         * 获取html文件的内容
         */
        public String getFromAssets(String fileName) {
            try {
                InputStreamReader inputReader = new InputStreamReader(getResources().getAssets().open(fileName));
                BufferedReader bufReader = new BufferedReader(inputReader);
                String line = "";
                String Result = "";
                while ((line = bufReader.readLine()) != null)
                    Result += line;
                return Result;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return "";
        }
    
        /**
         * 监听 所有点击的链接,如果拦截到我们需要的,就跳转到相对应的页面。
         */
        private class MyWebViewClient extends WebViewClient {
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                Log.d("webview", "url===>" + url);
                try {
                    // 如果url满足某种特定的格式,则进行特殊处理
                    if (url.contains("http://")) {
                        view.loadUrl(url);
                    } else {
                        view.loadUrl(url);
                    }
                    return true;
                } catch (Exception e) {
                    return true;
                }
            }
        }
    
        /**
         * 拦截js的提示框
         */
        private class MyWebChromeClien extends WebChromeClient {
            // 提示框
            @Override
            public boolean onJsPrompt(WebView view, String url, String message, String defaultValue,
                    final JsPromptResult result) {
                new AlertDialog.Builder(MainActivity.this).setMessage(message)
                        .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                // 当弹出框点击的时候,手动用代码给js返回确认的信息
                                result.confirm("true");
                            }
                        }).setNegativeButton("取消", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                result.cancel();
                            }
                        }).show();
                // 返回false会弹出原声dialog,这里不让原声的弹出
                return true;
            }
    
            // 警告框
            @SuppressLint("NewApi")
            @Override
            public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
                // api 17以后才有onDismiss 接口,这里稍作处理
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                    new AlertDialog.Builder(MainActivity.this).setMessage(message)
                            .setOnDismissListener(new OnDismissListener() {
    
                                @Override
                                public void onDismiss(DialogInterface arg0) {
                                    // TODO Auto-generated method stub
                                    result.cancel();
                                }
                            }).show();
                } else {
                    AlertDialog alertDialog = new AlertDialog.Builder(MainActivity.this).setMessage(message)
                            .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    // 当弹出框点击的时候,手动用代码给js返回确认的信息
                                    result.cancel();
                                }
                            }).show();
                    alertDialog.setCanceledOnTouchOutside(false);
                    alertDialog.setCancelable(false);
                }
                return true;
            }
    
            // 确认框
            @Override
            public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) {
                new AlertDialog.Builder(MainActivity.this).setMessage(message)
                        .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                // 当弹出框点击的时候,手动用代码给js返回确认的信息
                                result.confirm();
                            }
                        }).setNegativeButton("取消", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                result.cancel();
                            }
                        }).show();
                return true;
            }
        }
    
        /**
         * 对应js中的调用方法
         */
        public class JsInterface {
            //这个方法就是js中带有android.前缀的方法名
            @JavascriptInterface    
            public void showToast(String toast) {
                Toast.makeText(MainActivity.this, toast, Toast.LENGTH_SHORT).show();
                // 当显示出toast的时候,就向js反向输出一条log
                log("show toast success");
            }
    
            //这里是将app产生的信息回馈给js
            public void log(final String msg) {
                native_web.post(new Runnable() {
                    @Override
                    public void run() {
                        // 调用js中指向info的方法
                        // 这个方法会将信息在浏览器的控制台的info中显示出来
                        native_web.loadUrl("javascript:info(" + "'" + msg + "'" + ")");
                    }
                });
            }
        }
    }
    

    这里需要注意的是:js中的特定方法是前段和app端协商定义下的方法名和参数名,是不受js控制的

    也就是说,如果在前端调用了这些“特殊”的方法,前端会报错。

    如果你向深入的了解native+h5 那么一定要熟悉js/html和css;
    因为如果没有其他人给你做前端页面的话,你自己可能就要亲自去写这些了。
    就算有人给你完好的html和js,你也得会看,会调用。

    实战中的js和html可没有这么简单。

    上述功能是点击html页面中的js代码,实现js对android的回调。还有一种,直接使用android代码调用js,与之前的相反,实现android调用js代码。
    这个就很简单了:

    web_view.loadUrl("javascript:hideDown()");//直接loadUrl。hideDown()是html中的js代码方法名

    demo下载

    展开全文
  • 更多的APP采用了Native结合H5的混合开发模式,因此也衍生了大量的Hybrid框架。这其中有对载入H5做通信和性能优化的框架,如Phonegap等,也有使用JS脚本生成本地代码的框架,如Facdbook推出的react native这一类的...

    导语:
    近年来,随着移动端app的广泛使用,对于降低app的开发成本,热更新等诉求日益增多。更多的APP采用了Native结合H5的混合开发模式,因此也衍生了大量的Hybrid框架。这其中有对载入H5做通信和性能优化的框架,如Phonegap等,也有使用JS脚本生成本地代码的框架,如Facdbook推出的react native这一类的框架。

    React Native

    正如项目的名称表露的那样,React Native的目的是构建真正Native的应用。不同于传统Hybrid框架,React Native完全脱离WebView进行开发,因为它挣脱了复杂的DOM,高效率渲染、流畅交互使用户体验得到非常多的提升。

    它能使用够使用同样的语言同时支持iOS、Android平台,减少了我们在跨平台开发时对Native语言的不熟悉而产生的障碍。

    使用React Native需要了解HTML、CSS、JavaScript与同为Facebook开源的React框架等相关知识且不易掌握;能看出React Native对于新手来说不太友善,不过React Native背后有着强大的Facebook技术团队的支持以及其知名度,吸引了相当多的开发人员了解并使用它,使我们在成长的路上不会那么的孤单、无力。

    优点

    1. 相对于基于Web开发的hybrid框架,渲染效率、交互流畅度有明显提高
    2. 使用相同的语言开发便能同时开发iOS、Android

    缺点

    1. 需了解的知识面较广、学习成本较高
    2. 虽然使用可以使用相同的语言开发两种平台,但是依然需要了解原生平台的相关特性
    3. 无法实现列表视图的重用机制,使在列表视图拥有巨大数量时,导致内存攀升

    Weex

    最近较火的移动端框架 除了Reactnative,还有Weex,Weex官方的口号是Write once, run everywhere, Weex解决了的最大痛点就是使用者只要编写一次代码,就可以跨平台运行。它能帮助开发者解决跨平台的适配问题,同时,又拥有着Native原生App的性能问题。Weex目前的大优势是借助阿里的大平台,快速的迭代更新,有大批的用户帮助其完善Hybrid框架,目前已较为成熟,文档丰富,接入方便快捷。

    优点

    轻量,分模块加载,Write Once Run Everywhere,

    缺点

    H5 标签支持较少

    Cordova

    Cordova是由phonegap演变而来,在接触的多家公司中,一开始大多数采用phonegap的解决方案,这种
    前身为phonegap,历史比较久远,有很多公司在一开始接触Hybrid用的都是这种方式,早起,让不会Native的人,可以快速的用H5和js开发一个App,早起,iOS和Android都是使用的webview,现在,iOS和安卓,都有用jsCore, 大大提高了性能。

    优点:

    1.可以使用原生的js、html、css来构建一个应用
    2.支持很多的插件来去调原生的API的,这种插件的库和它的生态是非常完善的,也就是说一个前端开发者不需要懂原生就可以做
    相对来说比较稳定;

    缺点:

    1.性能瓶颈就是在于webview的性能,如果像Android 4.1以下的webview它的性能并不是很好,在Cordova打包出来的APP性能就会有问题。
    这个框架是一个比较重的框架,做Hybrid开发的话,集成在原生的app里面,使得整个APP比较重;

    Ionic

    Ionic提供了一个免费且开源的移动优化HTML,CSS和JS组件库,来构建高交互性的应用。它可以用框架中的CSS 实现有 native 风格的设计,不过相对于使用完整的 Ionic,更建议搭配 AngularJS 一起开发,从而创建完美的应用。

    它有如下特点:
    1 . 性能高,运行速度快,操作最少的DOM,非 jQuery且和硬件加速过渡;
    2 . 设计简单,并且实用,它为当前移动设备呈现了完美的设计;
    3 . 以原生SDK为蓝本,便于移动端开发人员的理解,完成时通过PhoneGap发布,达到一次开发,处处使用的效果;
    4 . 核心架构是为开发专业应用创建,框架轻量级;
    5 .一个命令就可以创建,构建,测试,部署你的应用程序在任何平台上,只需要npm install -g ionic 就可以创建您的应用。
    6 . 代码标准,后台维护人员专注,具有强大的社区。

    缺点
    在了解Ionic的同时,还需要了解AngularJS,为开发增加了一定的复杂以及难度;

    NativeScript

    Native script是一个开源项目,用 JavaScript 语言编写 app ,它和Weex有点类似,只需要编写一次,然后它可以针对不同的操作系统(包括 Android,iOS 和 Windows Phone)产生对应的可执行代码,打包后,应用是按原生程序的方式运行的,并没有打开和运行浏览器,框架是将js脚本翻译成原生应用的api来执行的。

    优点

    1.一次编写,跨平台运行
    2.英文文档丰富,中文文档较少

    缺点

    1.有一定的学习门槛
    2.没有完善的技术支持




    展开全文
  • android + h5 混合开发 webview 版 小实例 + 数据交互 + 界面交互+ js调用java +java 调用js
  • NativeH5+JS 解决方案

    2016-02-28 10:30:57
    iOS开发趋势:NativeH5+JS 解决方案 支付宝红包火了,微信红包火了,作为开发者,敏感的就发现之前并不被看好的H5已经悄悄渗透进来,在原生(Native)代码中部分功能采用动态网页(HTML5+JavaScript)来实现,...
    iOS开发趋势:Native与H5+JS 解决方案

    支付宝红包火了,微信红包火了,作为开发者,敏感的就发现之前并不被看好的H5已经悄悄渗透进来,在原生(Native)代码中部分功能采用动态网页(HTML5+JavaScript)来实现,即保证了整体App的流畅度,又能及时推出一些活动和动态,目前主流App已经开始默认这种开发模式,未来的移动开发团队也需要H5的小伙伴加入进来了,实现动静结合,让页面更加灵活多变,接下来笔者就根据之前涉及到的项目经验来谈谈原生与网页交互的那些不得不说的琐事:



    一、 原生代码中直接加载页面
    1.    具体案例
    加载本地/网络HTML5作为功能介绍页
    2.    代码示例
    //本地
    -(void)loadLocalPage:(UIWebView*)webView
    {
       NSString* htmlPath = [[NSBundle mainBundle]pathForResource:@"demo" ofType:@"html"];
    NSString* appHtml =[NSString stringWithContentsOfFile:htmlPath encoding:NSUTF8StringEncodingerror:nil];
    NSURL *baseURL = [NSURLfileURLWithPath:htmlPath];
     [webView loadHTMLString:appHtmlbaseURL:baseURL];
    }


    //网络
    -(void)loadWebPage:(UIWebView *)webView
    {
        NSURL *url = [NSURLURLWithString:@"http://www.baidu.com"];
        NSURLRequest *request = [NSURLRequestrequestWithURL:url];
        [webView loadRequest:request];
    }


    3.    额外操作
    a  iOS中承载网页的容器是UIWebView,可以借助它的代理来监听网页加载情况;
    b  在加载过程中,我们还可以获取该网页中的meta值,例如代码:
    NSString *shareUrl = [messWebViewstringByEvaluatingJavaScriptFromString:@"document.getElementsByName(\"shareUrl\")[0].content"];


    就是从meta中得到shareUrl对应的value值;
    c  截获当前是发起的那种请求,以便native来做对应的控制,例如代码:
    - (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest *)requestnavigationType:(UIWebViewNavigationType)navigationType
    {
        NSString *requestString = [[[request URL]absoluteString]stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
        if ([requestString hasPrefix:@"http://customersharetrigger"]){
           //执行一些操作
            return NO;
        }
        return YES;
    } //可以监听到这个请求,从而达到控制作用;


     
    二、  原生代码操作页面元素
    1.    具体案例
    在嵌入H5后需要操作页面元素
    2.    代码示例
    a、获取当前页面的url。
    -(void)webViewDidFinishLoad:(UIWebView *)webView { 
      NSString *currentURL = [webView stringByEvaluatingJavaScriptFromString:@"document.location.href"];
    }


    b、获取页面title:
      NSString *currentURL = [webViewstringByEvaluatingJavaScriptFromString:@"document.location.href"];
     
       NSString *title = [webviewstringByEvaluatingJavaScriptFromString:@"document.title"];


    c、修改界面元素的值。
        NSString *js_result = [webViewstringByEvaluatingJavaScriptFromString:@"document.getElementsByName('q')[0].value='朱祁林';"];
    d、表单提交:
       NSString *js_result2 =[webView stringByEvaluatingJavaScriptFromString:@"document.forms[0].submit();"];
    3.    代码说明
    stringByEvaluatingJavaScriptFromString方法可以将javascript代码片段嵌入到页面中,通过这个方法就可以让iOS与UIWebView中的网页元素交互,例如上面的代码片段,它
    功能非常的强大,用起来也相对简单,通过它我们可以很方便的操作页面元素,而且能直接插入一段JS方法,然后调用该方法执行;
      
    三、  原生代码处理本地H5+JS
    1.    具体案例
    需要动态显示曲线图,如果直接加载绘制图形特别慢,所以采用本地放置模板,传入参数,然后模板自动绘制,提高体验,加快绘制;
    2.    示例代码
    -(void)loadWebPage:(UIWebView *)webView
    {
        NSURL *localPathURL = [[NSBundlemainBundle] URLForResource:@"detail" withExtension:@"html"subdirectory:@"htmlResources"];
        NSString *localPathUrl = [localPathURLabsoluteString];
        NSString *localParamPathUrl = [NSStringstringWithFormat:@"%@?symbol=%@&t=%f",localPathUrl,self.stockCode,self.time];
        NSURL *requestURL = [NSURLURLWithString:localParamPathUrl];
        [webView loadRequest:[NSURLRequestrequestWithURL:requestURL]];
     
    }


    3.    代码说明
    a 这里需要采用绝对路径拖入H5模板,就是选择CreateFolder Reference, 只有这样才能保证H5能调用到本地的JS代码,不然加载不成功,这个最初找了很多原因,最后才发现是拖入时候选择问题;
    b 如果要加入参数,注意需要先转成string,然后再转为URL;
      
    四、  原生代码与网页交互通信
    1.    具体案例
    原生代码与H5相互调用方法,并传递参数,而且能回调数据;
    2.    借助第三方实现
    WebViewJavascriptBridge,该开源库非常完美的解决了原生代码与H5交互,即互殴;
    3.    代码示例
    1.初始化一个webview(viewdidload)
     
    UIWebView* webView =[[UIWebView alloc] initWithFrame:self.view.bounds];
        [self.view addSubview:webView];


     2.将此webview与WebViewJavascriptBridge关联(viewdidload)
     
    if (_bridge) { return; }
     
      [WebViewJavascriptBridge enableLogging];
     
      _bridge = [WebViewJavascriptBridgebridgeForWebView:webView webViewDelegate:self handler:^(id data,WVJBResponseCallback responseCallback) {
        NSLog(@"ObjC received message from JS:%@", data);
       
        responseCallback(@"Response formessage from ObjC");
      }];


    此时webview就与js搭上桥了。下面就是方法的互调和参数的互传。
     
     (1) js调oc方法(可以通过data给oc方法传值,使用responseCallback将值再返回给js)
     
    [_bridgeregisterHandler:@"testObjcCallback" handler:^(id data,WVJBResponseCallback responseCallback) {
            NSLog(@"testObjcCallback called:%@", data);
            responseCallback(@"Response fromtestObjcCallback");
        }];


      这里注意testObjcCallback这个方法的标示。html那边的命名要跟ios这边相同,才能调到这个方法。当然这个名字可以两边商量着自定义。简单明确即可。
     
      (2)oc调js方法(通过data可以传值,通过 response可以接受js那边的返回值 )
     
    id data = @{@"greetingFromObjC": @"Hi there, JS!" };
        [_bridgecallHandler:@"testJavascriptHandler" data:data responseCallback:^(idresponse) {
            NSLog(@"testJavascriptHandlerresponded: %@", response);
        }];


     注意这里的 testJavascriptHandler也是个方法标示。


     (3)oc给js传值(通过 response接受返回值 )


    [_bridge send:@"Astring sent from ObjC to JS" responseCallback:^(id response) {
            NSLog(@"sendMessage got response:%@", response);
        }];


      (4)oc给js传值(无返回值)
     
    [_bridge send:@"A string sent from ObjC after Webview hasloaded."];
     
     
    五、 总结
    关于Native和H5的交互有各种形式,随着H5越来越成熟,未来的趋势就是两者形影不离,让App更具灵活性和实效性,也一定程度上提高了开发效率和迭代周期,是企业级移动应用开发的必选解决方案,推荐:IT面试宝典(典型)。






     

    展开全文
  • 在上一篇文章Android 原生开发、H5、React-Native开发特点,我们可以了解到三种Android开发方式的区别和优缺点。[Android开发:原生+H5]系列的文章,将主要讲解Android原生+H5开发相关,这一节主要是Android原生+H5...

      在上一篇文章Android 原生开发、H5、React-Native开发特点,我们可以了解到三种Android开发方式的区别和优缺点。[Android开发:原生+H5]系列的文章,将主要讲解Android原生+H5开发相关,这一节主要是Android原生+H5开发时要使用WebView,要使WebView正确的显示加载H5页面和功能需要做相关的配置。

    AndroidManifest权限添加

      
      请一定、务必在AndroidManifest中添加如下权限,否则是无法正常打开显示H5页面的。
      这个一定要单独拿出来强调一下,以防你其他代码啊,配置啊什么的都写好了,但就是不显示,然后你就各种找问题,发愁,恼怒,耽误时间。因为楼主就曾经犯过这样的错误,真的被自己粗心蠢哭,哭哭哭……

    <uses-permission android:name="android.permission.INTERNET"/>

    WebView使用步骤

    1. 添加AndroidManifest权限。

    <uses-permission android:name="android.permission.INTERNET"/>

    2. 布局文件

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context="com.app.www.webapp.SecondActivity">
    
        <WebView
            android:id="@+id/webView"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    
    </LinearLayout>

    3. 获取WebView对象

       webView = this.findViewById(R.id.webView);

    4. WebSettings配置WebView。下面有具体的配置说明。
    5. 设置加载地址。

       this.webView.loadUrl(url);

      到这一步为止,WebView就可以正常的显示了,如果我们想要对WebView做进一步的监听处理,就需要下面的设置。
      
    6. 设置WebViewClient。 WebViewClient主要是监听WebView加载进程,平常我们对webview加载的处理,例如加一些进度条、跳转设置之类的都是通过WebViewClient类完成。在下面我们会讲到。
    7. 设置WebChromeClient。 有时候我们不想原生去调用手机的拍照和相册,如果我们想要用H5去掉用的话,我们需要去重新WebChromeClient类,并进行设置,这样H5才能成功的调用拍照和相册。下面会细讲。

    WebSettings类配置

            /**支持Js**/
            setting.setJavaScriptEnabled(true);
    
            /**设置自适应屏幕,两者合用**/
            //将图片调整到适合webview的大小
            setting.setUseWideViewPort(true);
            // 缩放至屏幕的大小
            setting.setLoadWithOverviewMode(true);
    
            /**缩放操作**/
            // 是否支持画面缩放,默认不支持
            setting.setBuiltInZoomControls(true);
            setting.setSupportZoom(true);
            // 是否显示缩放图标,默认显示
            setting.setDisplayZoomControls(false);
            // 设置网页内容自适应屏幕大小
            setting.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
    
            /**设置允许JS弹窗**/
            setting.setJavaScriptCanOpenWindowsAutomatically(true);
            setting.setDomStorageEnabled(true);
    
            /**关闭webview中缓存**/
            setting.setCacheMode(WebSettings.LOAD_NO_CACHE);
            /**设置可以访问文件 **/
            setting.setAllowFileAccess(true);
            setting.setAllowFileAccessFromFileURLs(true);
            setting.setAllowUniversalAccessFromFileURLs(true);

    WebViewClient类

      如果不进行设置WebViewClient的话,我们的WebView通常会跳转到手机自带的浏览器去进行显示,但是我们想要的是在app内显示,所以我们需要对WebViewClient进行设置。对这个类的使用我们之前在 Android 网络连接——WebView这篇文章中也讲过,这里我就只复制一下。

    WebView加载失败设置

      我们在使用浏览器时,我们经常会看到,如果页面加载失败会出现一个提示的页面。我们自己的浏览器当然也少不了这个功能。设置加载页面失败调用WebView的setWebViewClient()方法,传入匿名WebViewClient对象,重写onReceivedError()方法,在该方法内进行处理。

    webView.setWebViewClient(new WebViewClient() {
        /*
        网络连接错误时调用
        */
        @Override
        public void onReceivedError(WebView view, int errorCode, String description, String failingUrl){
            super.onReceivedError(view, errorCode, description, failingUrl);
        }
    });

    WebView网页加载进度条显示

            webView.setWebChromeClient(new WebChromeClient() {
                @Override
                public void onProgressChanged(WebView view, int newProgress) {
                    super.onProgressChanged(view, newProgress);
                    progressBar.setProgress(newProgress);//网络加载时设置进度条进度
                }
            });
            webView.setWebViewClient(new WebViewClient() {
                /*
                网络开始加载时调用
                */
                @Override
                public void onPageStarted(WebView view, String url, Bitmap favicon) {
                    super.onPageStarted(view, url, favicon);
                    progressBar.setVisibility(View.VISIBLE);//设置显示进度条
                }
    
                /*
                网络加载结束时调用
                */
                @Override
                public void onPageFinished(WebView view, String url) {
                    super.onPageFinished(view, url);
                    progressBar.setVisibility(View.GONE);//设置去除进度条
                }
            });

    WebChromeClient类

      H5想要调用我们手机的相册和拍照,直接调用是不行的,我们必须在WebView设置中进行设置。重写WebChromeClient类。

    
    import android.annotation.SuppressLint;
    import android.annotation.TargetApi;
    import android.app.Activity;
    import android.content.ClipData;
    import android.content.ComponentName;
    import android.content.ContentUris;
    import android.content.Context;
    import android.content.Intent;
    import android.content.pm.PackageManager;
    import android.content.pm.ResolveInfo;
    import android.database.Cursor;
    import android.graphics.Bitmap;
    import android.net.Uri;
    import android.os.Build;
    import android.os.Environment;
    import android.os.Parcelable;
    import android.provider.DocumentsContract;
    import android.provider.MediaStore;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.view.KeyEvent;
    import android.webkit.ValueCallback;
    import android.webkit.WebChromeClient;
    import android.webkit.WebSettings;
    import android.webkit.WebView;
    import android.webkit.WebViewClient;
    import android.widget.Toast;
    
    import java.io.File;
    import java.util.ArrayList;
    import java.util.List;
    
    import static android.view.KeyEvent.KEYCODE_BACK;
    
    public class SecondActivity extends Activity {
        private WebView webView;
        private Intent intent;
        private String url;
        /**
         * 表单的数据信息
         */
        private ValueCallback<Uri> mUploadMessage;
        private ValueCallback<Uri[]> mUploadCallbackAboveL;
        /**
         * 表单的结果回调</span>
         */
        private final static int FILECHOOSER_RESULTCODE = 1;
        private Uri imageUri;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_second);
            intent = getIntent();
            url = intent.getStringExtra("url");
            Toast.makeText(this, url, Toast.LENGTH_SHORT).show();
            webView = this.findViewById(R.id.webView);
    
            initWebView();
    //        this.webView.loadUrl("http://192.168.1.105:8099/photo");
            webView.loadUrl(url);
    
            webView.setWebViewClient(new WebViewClient() {
                @Override
                public boolean shouldOverrideUrlLoading(WebView view, String url) {
                    return super.shouldOverrideUrlLoading(view, url);
                }
    
                @Override
                public void onPageStarted(WebView view, String url, Bitmap favicon) {
                    super.onPageStarted(view, url, favicon);
                }
    
                @Override
                public void onPageFinished(WebView view, String url) {
                    super.onPageFinished(view, url);
                }
            });
            webView.setWebChromeClient(new OpenFileChromeClient());
        }
    
        public class OpenFileChromeClient extends WebChromeClient {
    
            @Override
            public boolean onShowFileChooser(WebView webView,
                                             ValueCallback<Uri[]> filePathCallback,
                                             FileChooserParams fileChooserParams) {
                mUploadCallbackAboveL = filePathCallback;
                take();
                return true;
            }
    
            /**
             * Android < 3.0 调用这个方法
             *
             * @param uploadMsg
             */
            public void openFileChooser(ValueCallback<Uri> uploadMsg) {
                mUploadMessage = uploadMsg;
                take();
            }
    
            /**
             * Android < 3.0 调用这个方法
             *
             * @param uploadMsg
             * @param acceptType
             */
            public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
                mUploadMessage = uploadMsg;
                take();
            }
    
            /**
             * Android > 4.1.1 调用这个方法
             *
             * @param uploadMsg
             * @param acceptType
             * @param capture
             */
            public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
                mUploadMessage = uploadMsg;
                take();
            }
        }
    
    
        private void initWebView() {
            WebSettings setting = webView.getSettings();
            /**支持Js**/
            setting.setJavaScriptEnabled(true);
    
            /**设置自适应屏幕,两者合用**/
            //将图片调整到适合webview的大小
            setting.setUseWideViewPort(true);
            // 缩放至屏幕的大小
            setting.setLoadWithOverviewMode(true);
    
            /**缩放操作**/
            // 是否支持画面缩放,默认不支持
            setting.setBuiltInZoomControls(true);
            setting.setSupportZoom(true);
            // 是否显示缩放图标,默认显示
            setting.setDisplayZoomControls(false);
            // 设置网页内容自适应屏幕大小
            setting.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
    
            /**设置允许JS弹窗**/
            setting.setJavaScriptCanOpenWindowsAutomatically(true);
            setting.setDomStorageEnabled(true);
    
    
            /**关闭webview中缓存**/
            setting.setCacheMode(WebSettings.LOAD_NO_CACHE);
            /**设置可以访问文件 **/
            setting.setAllowFileAccess(true);
            setting.setAllowFileAccessFromFileURLs(true);
            setting.setAllowUniversalAccessFromFileURLs(true);
    
        }
    
    
        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
            if (requestCode == FILECHOOSER_RESULTCODE) {
                if (null == mUploadMessage && null == mUploadCallbackAboveL) return;
                Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
                if (mUploadCallbackAboveL != null) {
                    onActivityResultAboveL(requestCode, resultCode, data);
                } else if (mUploadMessage != null) {
    
                    if (result != null) {
                        String path = getPath(getApplicationContext(),
                                result);
                        Uri uri = Uri.fromFile(new File(path));
                        mUploadMessage
                                .onReceiveValue(uri);
                    } else {
                        mUploadMessage.onReceiveValue(imageUri);
                    }
                    mUploadMessage = null;
    
    
                }
            }
        }
    
        @Override
        public boolean onKeyDown(int keyCode, KeyEvent event) {
            if ((keyCode == KEYCODE_BACK) && webView.canGoBack()) {
                webView.goBack();
                return true;
            }
            return super.onKeyDown(keyCode, event);
        }
    
        @SuppressWarnings("null")
        @TargetApi(Build.VERSION_CODES.BASE)
        private void onActivityResultAboveL(int requestCode, int resultCode, Intent data) {
            if (requestCode != FILECHOOSER_RESULTCODE
                    || mUploadCallbackAboveL == null) {
                return;
            }
    
            Uri[] results = null;
    
            if (resultCode == Activity.RESULT_OK) {
    
                if (data == null) {
    
                    results = new Uri[]{imageUri};
                } else {
                    String dataString = data.getDataString();
                    ClipData clipData = data.getClipData();
    
                    if (clipData != null) {
                        results = new Uri[clipData.getItemCount()];
                        for (int i = 0; i < clipData.getItemCount(); i++) {
                            ClipData.Item item = clipData.getItemAt(i);
                            results[i] = item.getUri();
                        }
                    }
    
                    if (dataString != null)
                        results = new Uri[]{Uri.parse(dataString)};
                }
            }
            if (results != null) {
                mUploadCallbackAboveL.onReceiveValue(results);
                mUploadCallbackAboveL = null;
            } else {
                results = new Uri[]{imageUri};
                mUploadCallbackAboveL.onReceiveValue(results);
                mUploadCallbackAboveL = null;
            }
    
            return;
        }
    
    
        private void take() {
            File imageStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "MyApp");
            // Create the storage directory if it does not exist
            if (!imageStorageDir.exists()) {
                imageStorageDir.mkdirs();
            }
            File file = new File(imageStorageDir + File.separator + "IMG_" + String.valueOf(System.currentTimeMillis()) + ".jpg");
            imageUri = Uri.fromFile(file);
    
            final List<Intent> cameraIntents = new ArrayList<Intent>();
            final Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            final PackageManager packageManager = getPackageManager();
            final List<ResolveInfo> listCam = packageManager.queryIntentActivities(captureIntent, 0);
            for (ResolveInfo res : listCam) {
                final String packageName = res.activityInfo.packageName;
                final Intent i = new Intent(captureIntent);
                i.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
                i.setPackage(packageName);
                i.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
                cameraIntents.add(i);
    
            }
            Intent i = new Intent(Intent.ACTION_GET_CONTENT);
            i.addCategory(Intent.CATEGORY_OPENABLE);
            i.setType("image/*");
            Intent chooserIntent = Intent.createChooser(i, "Image Chooser");
            chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toArray(new Parcelable[]{}));
            SecondActivity.this.startActivityForResult(chooserIntent, FILECHOOSER_RESULTCODE);
        }
    
        @SuppressLint("NewApi")
        @TargetApi(Build.VERSION_CODES.KITKAT)
        public static String getPath(final Context context, final Uri uri) {
            final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
    
            // DocumentProvider
            if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
                // ExternalStorageProvider
                if (isExternalStorageDocument(uri)) {
                    final String docId = DocumentsContract.getDocumentId(uri);
                    final String[] split = docId.split(":");
                    final String type = split[0];
    
                    if ("primary".equalsIgnoreCase(type)) {
                        return Environment.getExternalStorageDirectory() + "/" + split[1];
                    }
    
                    // TODO handle non-primary volumes
                }
                // DownloadsProvider
                else if (isDownloadsDocument(uri)) {
    
                    final String id = DocumentsContract.getDocumentId(uri);
                    final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
    
                    return getDataColumn(context, contentUri, null, null);
                }
                // MediaProvider
                else if (isMediaDocument(uri)) {
                    final String docId = DocumentsContract.getDocumentId(uri);
                    final String[] split = docId.split(":");
                    final String type = split[0];
    
                    Uri contentUri = null;
                    if ("image".equals(type)) {
                        contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                    } else if ("video".equals(type)) {
                        contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                    } else if ("audio".equals(type)) {
                        contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                    }
    
                    final String selection = "_id=?";
                    final String[] selectionArgs = new String[]{split[1]};
    
                    return getDataColumn(context, contentUri, selection, selectionArgs);
                }
            }
            // MediaStore (and general)
            else if ("content".equalsIgnoreCase(uri.getScheme())) {
                return getDataColumn(context, uri, null, null);
            }
            // File
            else if ("file".equalsIgnoreCase(uri.getScheme())) {
                return uri.getPath();
            }
    
            return null;
        }
    
    
        /**
         * Get the value of the data column for this Uri. This is useful for
         * MediaStore Uris, and other file-based ContentProviders.
         *
         * @param context       The context.
         * @param uri           The Uri to query.
         * @param selection     (Optional) Filter used in the query.
         * @param selectionArgs (Optional) Selection arguments used in the query.
         * @return The value of the _data column, which is typically a file path.
         */
        public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
            Cursor cursor = null;
            final String column = "_data";
            final String[] projection = {column};
    
            try {
                cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
                if (cursor != null && cursor.moveToFirst()) {
                    final int column_index = cursor.getColumnIndexOrThrow(column);
                    return cursor.getString(column_index);
                }
            } finally {
                if (cursor != null) cursor.close();
            }
            return null;
        }
    
    
        /**
         * @param uri The Uri to check.
         * @return Whether the Uri authority is ExternalStorageProvider.
         */
        public static boolean isExternalStorageDocument(Uri uri) {
            return "com.android.externalstorage.documents".equals(uri.getAuthority());
        }
    
    
        /**
         * @param uri The Uri to check.
         * @return Whether the Uri authority is DownloadsProvider.
         */
        public static boolean isDownloadsDocument(Uri uri) {
            return "com.android.providers.downloads.documents".equals(uri.getAuthority());
        }
    
    
        /**
         * @param uri The Uri to check.
         * @return Whether the Uri authority is MediaProvider.
         */
        public static boolean isMediaDocument(Uri uri) {
            return "com.android.providers.media.documents".equals(uri.getAuthority());
        }
    }
    

    完整代码下载地址:下载地址

    展开全文
  • H5+ native 函数

    2015-10-24 10:48:36
    plus.nativeUI.toast(str)  获取应用首页WebviewObject窗口对象 写道 plus.webview.getLaunchWebview()  查找指定标识的WebviewObject窗口 写道 plus.webview.getWebviewById(str);    等待提示 ...
    • 本地化信息提示
    写道
    plus.nativeUI.toast(str)
    •  获取应用首页WebviewObject窗口对象
    写道
    plus.webview.getLaunchWebview()
       查找指定标识的WebviewObject窗口
    写道
    plus.webview.getWebviewById(str);
      
    •      等待提示
    plus.nativeUI.showWaiting('加载中...',{padlock:true});
       第一个参数为提示语, padlock: true:点击时提示自动关闭;
    展开全文
  • 5+ App开发Native.js入门指南,详细的示例代码,开发参考使用。
  • 2019独角兽企业重金招聘Python工程师标准>>> ...
  • iOS开发趋势:NativeH5+JS 解决方案 支付宝红包火了,微信红包火了,作为开发者,敏感的就发现之前并不被看好的H5已经悄悄渗透进来,在原生(Native)代码中部分功能采用动态网页(HTML5+JavaScript)来实现,...
  • 自己摸索了一下,并参考其他人的代码,修复了原来代码存在的问题, 问题1:字符串的getBytes函数,返回null【需要通过new的方式创建字符串】 问题2:初始化打印一次,之后无法使用【不要重复调用device....
  • (其实更省事可以wap,android和ios一套带走,一下R闪秒3个平台,没毛病)框架介绍这套框架源于DCloud社区,DCloud的社区工程师们通过对h5的扩张,通过运用js在webview调用原生接口,最后通过DC
  • APPIUM Android 元素定位方式,原生+H5 1.定位元素应用元素   1.1通过id定位元素 Android里面定位的id一般为resrouce-id: 代码可以这样写: WebElement element = driver.findElement(By.id(...
  • NativeH5交互

    2017-06-21 15:54:46
    NativeH5交互的那些事 本文转自: http://zhengxiaoyong.me/2016/04/20/Native%E4%B8%8EH5%E4%BA%A4%E4%BA%92%E7%9A%84%E9%82%A3%E4%BA%9B%E4%BA%8B/ 前言 Hybrid开发模式目前几乎每家公司都有涉及和使用,这种...
  • react-nativeH5的通信

    2019-07-19 20:14:05
    1.react-nativeH5通信的情景 首先如果你的项目中需要接入一个H5写的项目,但是这个服务有必须用到react-native中的服务比如打开摄像头,获取地理位置等等。但是这些数据在H5中是获取不到的,那么就需要和react-...
  • H5native有啥区别?

    2018-07-17 18:37:21
    app测试,H5native有啥区别? native是使用原生系统内核的,相当于直接在系统上操作。是我们传统意义上的软件,更加稳定。 但是H5的APP先得调用系统的浏览器内核,相当于是在网页中进行操作,较原生APP稳定性稍...
  • 代码实现摸索了一下,自己写了一个蓝牙串口连接接收数据的小示例。还是希望有大神告知如何开启多线程来接收数据。如果要测试此代码需要自行引入vue.js。
  • 摘要 2015年是React Native发展的一年,2016年必定是React Native蓬勃的一年!...那么React Native相比H5Native又有哪些优势呢?使用React Native的正确姿势又是怎样呢? React Native React h5 《React Na
  • JSSDK + H5混合开发是一个生态,腾讯的这条构建 web 生态系统的路,还是有很多高手已经看懂了的。很快各大互联网巨头都会有自己的对策。但不管是什么对策,都是要基于 HTML5 来做了。对于 HTML5 的开发者和从业者,...
1 2 3 4 5 ... 20
收藏数 15,502
精华内容 6,200
关键字:

+ h5 native