精华内容
下载资源
问答
  • 适用于tp3.2 <?php error_reporting(E_ALL); ini_set('display_errors', '1'); // 定义时区 ini_set('date.timezone', 'Asia/Shanghai'); class Weixinpay { // 定义配置项 private $config = array... ...

    适用于tp3.2

     

    <?php
    
    error_reporting(E_ALL);
    ini_set('display_errors', '1');
    
    // 定义时区
    ini_set('date.timezone', 'Asia/Shanghai');
    
    class Weixinpay
    {
        // 定义配置项
        private $config = array(
            'APPID'      => '', // 微信支付APPID
            'MCHID'      => '', // 微信支付MCHID 商户收款账号
            'KEY'        => '', // 微信支付KEY
            'APPSECRET'  => '', //公众帐号secert
            'NOTIFY_URL' => 'http://baijunyao.com/Api/WeixPay/notify/order_number/', // 接收支付状态的连接  改成自己的域名
        );
    
        // 构造函数
        public function __construct()
        {
            // 如果是在thinkphp中 那么需要补全/Application/Common/Conf/config.php中的配置
            // 如果不是在thinkphp框架中使用;那么注释掉下面一行代码;直接补全 private $config 即可
            $this->config = C('WEIXINPAY_CONFIG');
        }
    
        /**
         *
         * 产生随机字符串,不长于32位
         * @param int $length
         * @return 产生的随机字符串
         */
        public function getNonceStr($length = 32)
        {
            $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
            $str   = "";
            for ($i = 0; $i < $length; $i++) {
                $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
            }
            return $str;
        }
        /**
         * 统一下单
         * @param  array $order 订单 必须包含支付所需要的参数 body(产品描述)、total_fee(订单金额)、out_trade_no(订单号)、product_id(产品id)、trade_type(类型:JSAPI,NATIVE,APP)
         */
        public function unifiedOrder($order)
        {
            // 获取配置项
            $weixinpay_config = $this->config;
            $config           = array(
                'appid'            => $weixinpay_config['APPID'],
                'mch_id'           => $weixinpay_config['MCHID'],
                // 'nonce_str'        => 'test',
                'nonce_str'        => $this->getNonceStr(),
                'spbill_create_ip' => get_client_ip(0, true),
                // 'spbill_create_ip' => '192.168.0.1',
    
                'notify_url'       => $weixinpay_config['NOTIFY_URL'],
            );
    
            // 合并配置数据和订单数据
            $data = array_merge($order, $config);
            // 生成签名
            $sign         = $this->makeSign($data);
            $data['sign'] = $sign;
            $xml          = $this->toXml($data);
            $url          = 'https://api.mch.weixin.qq.com/pay/unifiedorder'; //接收xml数据的文件
            $header[]     = "Content-type: text/xml"; //定义content-type为xml,注意是数组
            $ch           = curl_init($url);
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 兼容本地没有指定curl.cainfo路径的错误
            curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
            $response = curl_exec($ch);
            if (curl_errno($ch)) {
                // 显示报错信息;终止继续执行
                die(curl_error($ch));
            }
            curl_close($ch);
            $result = $this->toArray($response);
            // print_R($result);exit;
            // 显示错误信息
            if ($result['return_code'] == 'FAIL') {
                die($result['return_msg']);
            }
            $result['sign'] = $sign;
            // $result['nonce_str'] = 'test';
            return $result;
        }
    
        /**
         * 验证
         * @return array 返回数组格式的notify数据
         */
        public function notify()
        {
            // 获取xml
            $xml = file_get_contents('php://input', 'r');
            // 转成php数组
            $data = $this->toArray($xml);
            // 保存原sign
            $data_sign = $data['sign'];
            // sign不参与签名
            unset($data['sign']);
            $sign = $this->makeSign($data);
            // 判断签名是否正确  判断支付状态
            if ($sign === $data_sign && $data['return_code'] == 'SUCCESS' && $data['result_code'] == 'SUCCESS') {
                $result = $data;
            } else {
                $result = false;
            }
            // 返回状态给微信服务器
            if ($result) {
                $str = '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
            } else {
                $str = '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[签名失败]]></return_msg></xml>';
            }
            echo $str;
            return $result;
        }
    
        /**
         * 输出xml字符
         * @throws WxPayException
         **/
        public function toXml($data)
        {
            if (!is_array($data) || count($data) <= 0) {
                throw new WxPayException("数组数据异常!");
            }
            $xml = "<xml>";
            foreach ($data as $key => $val) {
                if (is_numeric($val)) {
                    $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
                } else {
                    $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
                }
            }
            $xml .= "</xml>";
            return $xml;
        }
    
        /**
         * 生成签名
         * @return 签名,本函数不覆盖sign成员变量,如要设置签名需要调用SetSign方法赋值
         */
        public function makeSign($data)
        {
            // 去空
            $data = array_filter($data);
            //签名步骤一:按字典序排序参数
            ksort($data);
            $string_a = http_build_query($data);
            $string_a = urldecode($string_a);
            //签名步骤二:在string后加入KEY
            $config           = $this->config;
            $string_sign_temp = $string_a . "&key=" . $config['KEY'];
            //签名步骤三:MD5加密
            $sign = md5($string_sign_temp);
            // 签名步骤四:所有字符转为大写
            $result = strtoupper($sign);
            return $result;
        }
    
        /**
         * 将xml转为array
         * @param  string $xml xml字符串
         * @return array       转换得到的数组
         */
        public function toArray($xml)
        {
            //禁止引用外部xml实体
            libxml_disable_entity_loader(true);
            $result = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
            return $result;
        }
    
        public function GetOpenid()
        {
            // 获取配置项
            $config = $this->config;
            // 如果没有get参数没有code;则重定向去获取openid;
            if (!isset($_GET['code'])) {
                // 获取订单号
                $orderno = I('orderno');
                // 返回的url
                //触发微信返回code码
                $redirect_uri = urlencode('http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']); //.$_SERVER['QUERY_STRING']
                // $redirect_uri = urlencode($redirect_uri);
                $url = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=' . $config['APPID'] . '&redirect_uri=' . $redirect_uri . '&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect';
                redirect($url);
            } else {
                // 如果有code参数;则表示获取到openid
                $code = I('get.code');
                // 取出订单号
                $out_trade_no = I('get.state', 0, 'intval');
                // 组合获取prepay_id的url
                $url = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=' . $config['APPID'] . '&secret=' . $config['APPSECRET'] . '&code=' . $code . '&grant_type=authorization_code';
    
                $ch = curl_init();
                //设置超时
                curl_setopt($ch, CURLOPT_TIMEOUT, 360000);
                curl_setopt($ch, CURLOPT_URL, $url);
                curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
                curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
                curl_setopt($ch, CURLOPT_HEADER, false);
                curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
                //运行curl,结果以jason形式返回
                $res = curl_exec($ch);
                curl_close($ch);
                //取出openid
                $data   = json_decode($res, true);
                $openid = $data['openid'];
                return $openid;
            }
        }
        /**
         * 获取jssdk需要用到的数据
         * @return array jssdk需要用到的数据
         */
        public function getParameters()
        {
            // 获取配置项
            $config = $this->config;
            // 如果没有get参数没有code;则重定向去获取openid;
            if (!isset($_GET['code'])) {
                // 获取订单号
                $out_trade_no = I('get.out_trade_no', 1, 'intval');
                // 返回的url
                $redirect_uri = U('Api/Weixinpay/pay', '', '', true);
                $redirect_uri = urlencode($redirect_uri);
                $url          = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=' . $config['APPID'] . '&redirect_uri=' . $redirect_uri . '&response_type=code&scope=snsapi_base&state=' . $out_trade_no . '#wechat_redirect';
                redirect($url);
            } else {
                // 如果有code参数;则表示获取到openid
                $code = I('get.code');
                // 取出订单号
                $out_trade_no = I('get.state', 0, 'intval');
                // 组合获取prepay_id的url
                $url = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=' . $config['APPID'] . '&secret=' . $config['APPSECRET'] . '&code=' . $code . '&grant_type=authorization_code';
                // curl获取prepay_id
                $result = $this->curl_get_contents($url);
                $result = json_decode($result, true);
                $openid = $result['openid'];
                // 订单数据  请根据订单号out_trade_no 从数据库中查出实际的body、total_fee、out_trade_no、product_id
                $order = array(
                    'body'         => 'test', // 商品描述(需要根据自己的业务修改)
                    'total_fee'    => 1, // 订单金额  以(分)为单位(需要根据自己的业务修改)
                    'out_trade_no' => $out_trade_no, // 订单号(需要根据自己的业务修改)
                    'product_id'   => '1', // 商品id(需要根据自己的业务修改)
                    'trade_type'   => 'JSAPI', // JSAPI公众号支付
                    'openid'       => $openid, // 获取到的openid
                );
                // 统一下单 获取prepay_id
                $unified_order = $this->unifiedOrder($order);
                // 获取当前时间戳
                $time = time();
                // 组合jssdk需要用到的数据
                $data = array(
                    'appId'     => $config['APPID'], //appid
                    'timeStamp' => strval($time), //时间戳
                    'nonceStr'  => $unified_order['nonce_str'], // 随机字符串
                    'package'   => 'prepay_id=' . $unified_order['prepay_id'], // 预支付交易会话标识
                    'signType'  => 'MD5', //加密方式
                );
                // 生成签名
                $data['paySign'] = $this->makeSign($data);
                return $data;
            }
        }
    
        /**
         * 生成支付二维码
         * @param  array $order 订单 必须包含支付所需要的参数 body(产品描述)、total_fee(订单金额)、out_trade_no(订单号)、product_id(产品id)、trade_type(类型:JSAPI,NATIVE,APP)
         */
        public function pay($order)
        {
            $result    = $this->unifiedOrder($order);
            $decodeurl = urldecode($result['code_url']);
            qrcode($decodeurl);
        }
        public function mweb($order)
        {
            $result       = $this->unifiedOrder($order);
            $redirect_uri = U('order/readx', '', '', true);
            $redirect_uri .= '?orderno=' . $order['out_trade_no'];
            $url = $result['mweb_url'];
            $url .= '&redirect_url=' . urlencode($redirect_uri);
            echo '<script>window.location.href="' . $url . '"</script>';
        }
    
        /**
         *
         * 获取jsapi支付的参数
         * @param array $UnifiedOrderResult 统一支付接口返回的数据
         * @throws WxPayException
         *
         * @return json数据,可直接填入js函数作为参数
         */
        public function GetJsApiParameters($OrderResult)
        {
            $config             = $this->config;
            $jsapi              = [];
            $jsapi['appId']     = $config['appid'];
            $timeStamp          = time();
            $jsapi['timeStamp'] = "$timeStamp";
            $jsapi['nonceStr']  = $this->getNonceStr();
            $jsapi['package']   = $OrderResult['prepay_id'];
            $jsapi['signType']  = 'MD5';
            $jsapi['paySign']   = $OrderResult['paySign'];
            $parameters         = json_encode($jsapi);
            return $parameters;
        }
        public function jspay($order)
        {
            $config = $this->config;
            $openId = $this->GetOpenid();
            $openId or exit('登录失败!');
            // 统一下单 获取prepay_id
            $order['openid'] = $openId;
            $unified_order   = $this->unifiedOrder($order);
            // 获取当前时间戳
            $time = time();
            // 组合jssdk需要用到的数据
            $data = array(
                'appId'     => $config['APPID'], //appid
                'timeStamp' => strval($time), //时间戳
                'nonceStr'  => $unified_order['nonce_str'], // 随机字符串
                'package'   => 'prepay_id=' . $unified_order['prepay_id'], // 预支付交易会话标识
                'signType'  => 'MD5', //加密方式
            );
            // 生成签名
            $data['paySign'] = $this->makeSign($data);
            $data            = json_encode($data);
            return $data;
        }
    
        /**
         * curl 请求http
         */
        public function curl_get_contents($url)
        {
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, $url); //设置访问的url地址
            // curl_setopt($ch,CURLOPT_HEADER,1);               //是否显示头部信息
            curl_setopt($ch, CURLOPT_TIMEOUT, 5); //设置超时
            curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); //用户访问代理 User-Agent
            curl_setopt($ch, CURLOPT_REFERER, $_SERVER['HTTP_HOST']); //设置 referer
            curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); //跟踪301
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回结果
            $r = curl_exec($ch);
            curl_close($ch);
            return $r;
        }
    
    }
    

     调用方式

     

    <?php
    namespace Home\Controller;
    
    use Think\Controller;
    
    class PayController extends BaseController
    {
    
        //生成微信支付二维码
        public function wxpay()
        {
            $orderno = I('orderno'); //订单号
            $info    = getorderbyno($orderno); //订单详情
            if ($info['state'] == 0) {
                $order = array(
                    'body'         => '起名网',
                    'detail'       => '起名网',
                    'total_fee'    => $info['price'] * 100, //分
                    'out_trade_no' => strval($orderno),
                    'product_id'   => $info['id'],
                );
                if (Mobile_Detect()->isWechat()) {
                    header("Content-type: text/html; charset=utf-8");
                    $parameters = wxjspay($order);
                    $url        = "/order/mlist.html?orderno=$orderno";
                    $this->assign('successurl', $url);
                    $this->assign('errorurl', $url);
                    $this->assign('jsApiParameters', $parameters);
                    $this->display('wxjsapi');
                } else if (Mobile_Detect()->isMobile()) {
                    wxh5pay($order);
                } else {
                    weixinpay($order);
                }
            } else {
                echo '已付款';
            }
    
        }
    
    
        //异步回调
        public function wxnotify()
        {
            // 导入微信支付sdk
            Vendor('Weixinpay.Weixinpay');
            $wxpay  = new \Weixinpay();
            $result = $wxpay->notify();
            //$result['out_trade_no'] = 'CS151494351797648';
            if ($result) {
                // 验证成功 修改数据库的订单状态等 $result['out_trade_no']为订单id
                $this->handleOrder($result['out_trade_no']);
    
            }
        }
    
    
    
    
    
        /**
         * 处理订单
         * @param  [type] $order_no [订单编号]
         * @return [type]           [description]
         */
        private function handleOrder($order_no)
        {
            $res = M('order')->where("order_no='%s'", array($order_no))->field("state,id")->find();
            if ($res['state'] == 0) {
                $data = array(
                    'state'   => 1,
                    'paytime' => time(),
                );
                M('order')->where(array('id' => $res['id']))->save($data);
            }
        }
    
    }
    

     

    展开全文
  • 想要编写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下载

    展开全文
  • 自己摸索了一下,并参考其他人的代码,修复了原来代码存在的问题, 问题1:字符串的getBytes函数,返回null【需要通过new的方式创建字符串】 问题2:初始化打印一次,之后无法使用【不要重复调用device....

    自己摸索了一下,并参考其他人的代码,修复了原来代码存在的问题,
    问题1:字符串的getBytes函数,返回null【需要通过new的方式创建字符串】
    问题2:初始化打印一次,之后无法使用【不要重复调用device.createInsecureRfcommSocketToServiceRecord(uuid)或重复引入CLASS】

    代码如下,可正常运行【注意,蓝牙打印机必须先匹配,保证手机蓝牙打开】

    mui.plusReady(function() {
                main = plus.android.runtimeMainActivity();
                BluetoothAdapter = plus.android.importClass("android.bluetooth.BluetoothAdapter");
                UUID = plus.android.importClass("java.util.UUID");
                uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");//不需要更改
                BAdapter = BluetoothAdapter.getDefaultAdapter();
                BAdapter.cancelDiscovery(); //停止扫描
                device = BAdapter.getRemoteDevice("DC:1D:30:25:19:23");//这里是蓝牙打印机的蓝牙地址
                plus.android.importClass(device);
                bluetoothSocket = device.createInsecureRfcommSocketToServiceRecord(uuid);
                plus.android.importClass(bluetoothSocket);
            });
    
            function print() {
    
                if(!bluetoothSocket.isConnected()) {
                    console.log('断开了,需要重新连接,连接中');
                    bluetoothSocket.connect();
                }
    
                if(bluetoothSocket.isConnected()) {
                    console.log('连接成功');
                    var outputStream = bluetoothSocket.getOutputStream();
                    plus.android.importClass(outputStream);
                    var s = plus.android.importClass('java.lang.String');
                    var string = new s("测试数据"+new Date().getMilliseconds()+'\n\n\n\n');//必须以创建字符串对象的形式创建对象,否则返回NULL
                    var bytes = string.getBytes('gbk');
                    console.log(bytes);
                    outputStream.write(bytes);
                    outputStream.flush();
                } else {
                    console.log('fail');
                }
    
            }
    

    至于打印的内容和格式,请参考各自打印机提供的打印指令集,把它拼成字符串调用getBytes函数即可打印。

    展开全文
  • [Android开发:原生+H5]系列的文章,将主要讲解Android原生+H5开发相关,这一节主要是Android原生+H5开发时要使用WebView,要使WebView正确的显示加载H5页面和功能需要做相关的配置。 AndroidManifest权限添加  ....

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

    AndroidManifest权限添加

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

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

    WebView使用步骤

    1. 添加AndroidManifest权限。

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

    2. 布局文件

     1 <?xml version="1.0" encoding="utf-8"?>
     2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     3     xmlns:app="http://schemas.android.com/apk/res-auto"
     4     xmlns:tools="http://schemas.android.com/tools"
     5     android:layout_width="match_parent"
     6     android:layout_height="match_parent"
     7     android:orientation="vertical"
     8     tools:context="com.app.www.webapp.SecondActivity">
     9 
    10     <WebView
    11         android:id="@+id/webView"
    12         android:layout_width="match_parent"
    13         android:layout_height="match_parent" />
    14 
    15 </LinearLayout>

    3. 获取WebView对象。

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

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

    1 webView.loadUrl(url);

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

    WebSettings类配置

     1         /**支持Js**/
     2         setting.setJavaScriptEnabled(true);
     3 
     4         /**设置自适应屏幕,两者合用**/
     5         //将图片调整到适合webview的大小
     6         setting.setUseWideViewPort(true);
     7         // 缩放至屏幕的大小
     8         setting.setLoadWithOverviewMode(true);
     9 
    10         /**缩放操作**/
    11         // 是否支持画面缩放,默认不支持
    12         setting.setBuiltInZoomControls(true);
    13         setting.setSupportZoom(true);
    14         // 是否显示缩放图标,默认显示
    15         setting.setDisplayZoomControls(false);
    16         // 设置网页内容自适应屏幕大小
    17         setting.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
    18 
    19         /**设置允许JS弹窗**/
    20         setting.setJavaScriptCanOpenWindowsAutomatically(true);
    21         setting.setDomStorageEnabled(true);
    22 
    23         /**关闭webview中缓存**/
    24         setting.setCacheMode(WebSettings.LOAD_NO_CACHE);
    25         /**设置可以访问文件 **/
    26         setting.setAllowFileAccess(true);
    27         setting.setAllowFileAccessFromFileURLs(true);
    28         setting.setAllowUniversalAccessFromFileURLs(true);

    WebViewClient类

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

    WebView加载失败设置

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

    1 webView.setWebViewClient(new WebViewClient() {
    2     /*
    3     网络连接错误时调用
    4     */
    5     @Override
    6     public void onReceivedError(WebView view, int errorCode, String description, String failingUrl){
    7         super.onReceivedError(view, errorCode, description, failingUrl);
    8     }
    9 });

    WebView网页加载进度条显示

     1         webView.setWebChromeClient(new WebChromeClient() {
     2             @Override
     3             public void onProgressChanged(WebView view, int newProgress) {
     4                 super.onProgressChanged(view, newProgress);
     5                 progressBar.setProgress(newProgress);//网络加载时设置进度条进度
     6             }
     7         });
     8         webView.setWebViewClient(new WebViewClient() {
     9             /*
    10             网络开始加载时调用
    11             */
    12             @Override
    13             public void onPageStarted(WebView view, String url, Bitmap favicon) {
    14                 super.onPageStarted(view, url, favicon);
    15                 progressBar.setVisibility(View.VISIBLE);//设置显示进度条
    16             }
    17 
    18             /*
    19             网络加载结束时调用
    20             */
    21             @Override
    22             public void onPageFinished(WebView view, String url) {
    23                 super.onPageFinished(view, url);
    24                 progressBar.setVisibility(View.GONE);//设置去除进度条
    25             }
    26         });

    WebChromeClient类

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

      1 import android.annotation.SuppressLint;
      2 import android.annotation.TargetApi;
      3 import android.app.Activity;
      4 import android.content.ClipData;
      5 import android.content.ComponentName;
      6 import android.content.ContentUris;
      7 import android.content.Context;
      8 import android.content.Intent;
      9 import android.content.pm.PackageManager;
     10 import android.content.pm.ResolveInfo;
     11 import android.database.Cursor;
     12 import android.graphics.Bitmap;
     13 import android.net.Uri;
     14 import android.os.Build;
     15 import android.os.Environment;
     16 import android.os.Parcelable;
     17 import android.provider.DocumentsContract;
     18 import android.provider.MediaStore;
     19 import android.support.v7.app.AppCompatActivity;
     20 import android.os.Bundle;
     21 import android.view.KeyEvent;
     22 import android.webkit.ValueCallback;
     23 import android.webkit.WebChromeClient;
     24 import android.webkit.WebSettings;
     25 import android.webkit.WebView;
     26 import android.webkit.WebViewClient;
     27 import android.widget.Toast;
     28 
     29 import java.io.File;
     30 import java.util.ArrayList;
     31 import java.util.List;
     32 
     33 import static android.view.KeyEvent.KEYCODE_BACK;
     34 
     35 public class SecondActivity extends Activity {
     36     private WebView webView;
     37     private Intent intent;
     38     private String url;
     39     /**
     40      * 表单的数据信息
     41      */
     42     private ValueCallback<Uri> mUploadMessage;
     43     private ValueCallback<Uri[]> mUploadCallbackAboveL;
     44     /**
     45      * 表单的结果回调</span>
     46      */
     47     private final static int FILECHOOSER_RESULTCODE = 1;
     48     private Uri imageUri;
     49     @Override
     50     protected void onCreate(Bundle savedInstanceState) {
     51         super.onCreate(savedInstanceState);
     52         setContentView(R.layout.activity_second);
     53         intent = getIntent();
     54         url = intent.getStringExtra("url");
     55         Toast.makeText(this, url, Toast.LENGTH_SHORT).show();
     56         webView = this.findViewById(R.id.webView);
     57 
     58         initWebView();
     59 //        this.webView.loadUrl("http://192.168.1.105:8099/photo");
     60         webView.loadUrl(url);
     61 
     62         webView.setWebViewClient(new WebViewClient() {
     63             @Override
     64             public boolean shouldOverrideUrlLoading(WebView view, String url) {
     65                 return super.shouldOverrideUrlLoading(view, url);
     66             }
     67 
     68             @Override
     69             public void onPageStarted(WebView view, String url, Bitmap favicon) {
     70                 super.onPageStarted(view, url, favicon);
     71             }
     72 
     73             @Override
     74             public void onPageFinished(WebView view, String url) {
     75                 super.onPageFinished(view, url);
     76             }
     77         });
     78         webView.setWebChromeClient(new OpenFileChromeClient());
     79     }
     80 
     81     public class OpenFileChromeClient extends WebChromeClient {
     82 
     83         @Override
     84         public boolean onShowFileChooser(WebView webView,
     85                                          ValueCallback<Uri[]> filePathCallback,
     86                                          FileChooserParams fileChooserParams) {
     87             mUploadCallbackAboveL = filePathCallback;
     88             take();
     89             return true;
     90         }
     91 
     92         /**
     93          * Android < 3.0 调用这个方法
     94          *
     95          * @param uploadMsg
     96          */
     97         public void openFileChooser(ValueCallback<Uri> uploadMsg) {
     98             mUploadMessage = uploadMsg;
     99             take();
    100         }
    101 
    102         /**
    103          * Android < 3.0 调用这个方法
    104          *
    105          * @param uploadMsg
    106          * @param acceptType
    107          */
    108         public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
    109             mUploadMessage = uploadMsg;
    110             take();
    111         }
    112 
    113         /**
    114          * Android > 4.1.1 调用这个方法
    115          *
    116          * @param uploadMsg
    117          * @param acceptType
    118          * @param capture
    119          */
    120         public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
    121             mUploadMessage = uploadMsg;
    122             take();
    123         }
    124     }
    125 
    126 
    127     private void initWebView() {
    128         WebSettings setting = webView.getSettings();
    129         /**支持Js**/
    130         setting.setJavaScriptEnabled(true);
    131 
    132         /**设置自适应屏幕,两者合用**/
    133         //将图片调整到适合webview的大小
    134         setting.setUseWideViewPort(true);
    135         // 缩放至屏幕的大小
    136         setting.setLoadWithOverviewMode(true);
    137 
    138         /**缩放操作**/
    139         // 是否支持画面缩放,默认不支持
    140         setting.setBuiltInZoomControls(true);
    141         setting.setSupportZoom(true);
    142         // 是否显示缩放图标,默认显示
    143         setting.setDisplayZoomControls(false);
    144         // 设置网页内容自适应屏幕大小
    145         setting.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
    146 
    147         /**设置允许JS弹窗**/
    148         setting.setJavaScriptCanOpenWindowsAutomatically(true);
    149         setting.setDomStorageEnabled(true);
    150 
    151 
    152         /**关闭webview中缓存**/
    153         setting.setCacheMode(WebSettings.LOAD_NO_CACHE);
    154         /**设置可以访问文件 **/
    155         setting.setAllowFileAccess(true);
    156         setting.setAllowFileAccessFromFileURLs(true);
    157         setting.setAllowUniversalAccessFromFileURLs(true);
    158 
    159     }
    160 
    161 
    162     @Override
    163     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    164         super.onActivityResult(requestCode, resultCode, data);
    165         if (requestCode == FILECHOOSER_RESULTCODE) {
    166             if (null == mUploadMessage && null == mUploadCallbackAboveL) return;
    167             Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
    168             if (mUploadCallbackAboveL != null) {
    169                 onActivityResultAboveL(requestCode, resultCode, data);
    170             } else if (mUploadMessage != null) {
    171 
    172                 if (result != null) {
    173                     String path = getPath(getApplicationContext(),
    174                             result);
    175                     Uri uri = Uri.fromFile(new File(path));
    176                     mUploadMessage
    177                             .onReceiveValue(uri);
    178                 } else {
    179                     mUploadMessage.onReceiveValue(imageUri);
    180                 }
    181                 mUploadMessage = null;
    182 
    183 
    184             }
    185         }
    186     }
    187 
    188     @Override
    189     public boolean onKeyDown(int keyCode, KeyEvent event) {
    190         if ((keyCode == KEYCODE_BACK) && webView.canGoBack()) {
    191             webView.goBack();
    192             return true;
    193         }
    194         return super.onKeyDown(keyCode, event);
    195     }
    196 
    197     @SuppressWarnings("null")
    198     @TargetApi(Build.VERSION_CODES.BASE)
    199     private void onActivityResultAboveL(int requestCode, int resultCode, Intent data) {
    200         if (requestCode != FILECHOOSER_RESULTCODE
    201                 || mUploadCallbackAboveL == null) {
    202             return;
    203         }
    204 
    205         Uri[] results = null;
    206 
    207         if (resultCode == Activity.RESULT_OK) {
    208 
    209             if (data == null) {
    210 
    211                 results = new Uri[]{imageUri};
    212             } else {
    213                 String dataString = data.getDataString();
    214                 ClipData clipData = data.getClipData();
    215 
    216                 if (clipData != null) {
    217                     results = new Uri[clipData.getItemCount()];
    218                     for (int i = 0; i < clipData.getItemCount(); i++) {
    219                         ClipData.Item item = clipData.getItemAt(i);
    220                         results[i] = item.getUri();
    221                     }
    222                 }
    223 
    224                 if (dataString != null)
    225                     results = new Uri[]{Uri.parse(dataString)};
    226             }
    227         }
    228         if (results != null) {
    229             mUploadCallbackAboveL.onReceiveValue(results);
    230             mUploadCallbackAboveL = null;
    231         } else {
    232             results = new Uri[]{imageUri};
    233             mUploadCallbackAboveL.onReceiveValue(results);
    234             mUploadCallbackAboveL = null;
    235         }
    236 
    237         return;
    238     }
    239 
    240 
    241     private void take() {
    242         File imageStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "MyApp");
    243         // Create the storage directory if it does not exist
    244         if (!imageStorageDir.exists()) {
    245             imageStorageDir.mkdirs();
    246         }
    247         File file = new File(imageStorageDir + File.separator + "IMG_" + String.valueOf(System.currentTimeMillis()) + ".jpg");
    248         imageUri = Uri.fromFile(file);
    249 
    250         final List<Intent> cameraIntents = new ArrayList<Intent>();
    251         final Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    252         final PackageManager packageManager = getPackageManager();
    253         final List<ResolveInfo> listCam = packageManager.queryIntentActivities(captureIntent, 0);
    254         for (ResolveInfo res : listCam) {
    255             final String packageName = res.activityInfo.packageName;
    256             final Intent i = new Intent(captureIntent);
    257             i.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
    258             i.setPackage(packageName);
    259             i.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
    260             cameraIntents.add(i);
    261 
    262         }
    263         Intent i = new Intent(Intent.ACTION_GET_CONTENT);
    264         i.addCategory(Intent.CATEGORY_OPENABLE);
    265         i.setType("image/*");
    266         Intent chooserIntent = Intent.createChooser(i, "Image Chooser");
    267         chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toArray(new Parcelable[]{}));
    268         SecondActivity.this.startActivityForResult(chooserIntent, FILECHOOSER_RESULTCODE);
    269     }
    270 
    271     @SuppressLint("NewApi")
    272     @TargetApi(Build.VERSION_CODES.KITKAT)
    273     public static String getPath(final Context context, final Uri uri) {
    274         final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
    275 
    276         // DocumentProvider
    277         if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
    278             // ExternalStorageProvider
    279             if (isExternalStorageDocument(uri)) {
    280                 final String docId = DocumentsContract.getDocumentId(uri);
    281                 final String[] split = docId.split(":");
    282                 final String type = split[0];
    283 
    284                 if ("primary".equalsIgnoreCase(type)) {
    285                     return Environment.getExternalStorageDirectory() + "/" + split[1];
    286                 }
    287 
    288                 // TODO handle non-primary volumes
    289             }
    290             // DownloadsProvider
    291             else if (isDownloadsDocument(uri)) {
    292 
    293                 final String id = DocumentsContract.getDocumentId(uri);
    294                 final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
    295 
    296                 return getDataColumn(context, contentUri, null, null);
    297             }
    298             // MediaProvider
    299             else if (isMediaDocument(uri)) {
    300                 final String docId = DocumentsContract.getDocumentId(uri);
    301                 final String[] split = docId.split(":");
    302                 final String type = split[0];
    303 
    304                 Uri contentUri = null;
    305                 if ("image".equals(type)) {
    306                     contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
    307                 } else if ("video".equals(type)) {
    308                     contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
    309                 } else if ("audio".equals(type)) {
    310                     contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
    311                 }
    312 
    313                 final String selection = "_id=?";
    314                 final String[] selectionArgs = new String[]{split[1]};
    315 
    316                 return getDataColumn(context, contentUri, selection, selectionArgs);
    317             }
    318         }
    319         // MediaStore (and general)
    320         else if ("content".equalsIgnoreCase(uri.getScheme())) {
    321             return getDataColumn(context, uri, null, null);
    322         }
    323         // File
    324         else if ("file".equalsIgnoreCase(uri.getScheme())) {
    325             return uri.getPath();
    326         }
    327 
    328         return null;
    329     }
    330 
    331 
    332     /**
    333      * Get the value of the data column for this Uri. This is useful for
    334      * MediaStore Uris, and other file-based ContentProviders.
    335      *
    336      * @param context       The context.
    337      * @param uri           The Uri to query.
    338      * @param selection     (Optional) Filter used in the query.
    339      * @param selectionArgs (Optional) Selection arguments used in the query.
    340      * @return The value of the _data column, which is typically a file path.
    341      */
    342     public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
    343         Cursor cursor = null;
    344         final String column = "_data";
    345         final String[] projection = {column};
    346 
    347         try {
    348             cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
    349             if (cursor != null && cursor.moveToFirst()) {
    350                 final int column_index = cursor.getColumnIndexOrThrow(column);
    351                 return cursor.getString(column_index);
    352             }
    353         } finally {
    354             if (cursor != null) cursor.close();
    355         }
    356         return null;
    357     }
    358 
    359 
    360     /**
    361      * @param uri The Uri to check.
    362      * @return Whether the Uri authority is ExternalStorageProvider.
    363      */
    364     public static boolean isExternalStorageDocument(Uri uri) {
    365         return "com.android.externalstorage.documents".equals(uri.getAuthority());
    366     }
    367 
    368 
    369     /**
    370      * @param uri The Uri to check.
    371      * @return Whether the Uri authority is DownloadsProvider.
    372      */
    373     public static boolean isDownloadsDocument(Uri uri) {
    374         return "com.android.providers.downloads.documents".equals(uri.getAuthority());
    375     }
    376 
    377 
    378     /**
    379      * @param uri The Uri to check.
    380      * @return Whether the Uri authority is MediaProvider.
    381      */
    382     public static boolean isMediaDocument(Uri uri) {
    383         return "com.android.providers.media.documents".equals(uri.getAuthority());
    384     }
    385 }

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

    转载于:https://www.cnblogs.com/univalsoft-mobile-team/p/8807938.html

    展开全文
  • 公司app使用的开发模式是native(原生)+H5(webview)模式 启动页别搞错了 ——坑1:所见即所得,但是所见非所想得;一定要找开发确认,app的启动页是否是MainActivity页面,如果不是,赶快改! webview元素...
  • Hybrid-Native+H5,主流框架深度分析

    千次阅读 2018-05-29 22:47:41
    更多的APP采用了Native结合H5的混合开发模式,因此也衍生了大量的Hybrid框架。这其中有对载入H5做通信和性能优化的框架,如Phonegap等,也有使用JS脚本生成本地代码的框架,如Facdbook推出的react native这一类的...
  • 前言小编所在项目的客户端是比较奇怪的一个APP,大部分页面Android...但是,如果要我们测试要做基于Appium做自动化来说这就不是一件好事儿了,因为Native的客户端可以借助ADB命令和封装的Appium实现大多数点击、跳转...
  • [Android开发:原生+H5]系列的文章,将主要讲解Android原生+H5开发相关,这一节主要是Android原生+H5开发时要使用WebView,要使WebView正确的显示加载H5页面和功能需要做相关的配置。 AndroidManifest权限添...
  • H5+ native 函数

    2015-09-18 20:34:49
    plus.nativeUI.toast(str)  获取应用首页WebviewObject窗口对象 写道 plus.webview.getLaunchWebview()  查找指定标识的WebviewObject窗口 写道 plus.webview.getWebviewById(str);    等待提示 ...
  • 佳博Q80蓝牙便携打印机,H5开发;使用工具为HBuilder+Native.js
  • uniapp、nativeJS、H5+退出APP应用(IOS+安卓) 点击此处文字,扫码进入此链接可阅读原文 直接扫码进入此链接可阅读原文 前言: 近几日使用uni-app 开发移动应用APP,在用户首次安装的时候,需要仔细阅读隐私政策后,...
  • 我们称这些应用为原生应用(Native App),因为它们是需要安装在用户设备上的软件,它们的代码和界面都是针对所运行的平台开发和设计的。这些原生应用能够最大程度地发挥用户设备的性能,例如使用存储空间实现离线...
  • NativeH5+JS 解决方案

    千次阅读 2016-02-28 10:30:57
    iOS开发趋势:NativeH5+JS 解决方案 支付宝红包火了,微信红包火了,作为开发者,敏感的就发现之前并不被看好的H5已经悄悄渗透进来,在原生(Native)代码中部分功能采用动态网页(HTML5+JavaScript)来实现,...
  • iOS开发趋势:NativeH5+JS 解决方案  2015-07-31 16:00 2809人阅读 评论(0) 收藏 举报 版权声明:本文为博主原创文章,未经博主允许不得转载。 目录(?)[+] 支付宝红包火了,...
  • H5+ 移动app学习之二 Native.js Native.js技术,简称NJS,是一种将手机操作系统的原生对象转义,映射为JS对象,在JS里编写原生代码的技术。如果说Node.js把js扩展到服务器世界,那么Native.js则把js扩展到...
  • 描述 在进行APP+H5混合开发的时候,一些功能是用native方法实现的,如登陆,一些功能是用H5实现的。所以往往需要将在native方法登陆的状态同步到H5中避免再次登陆。这种情况在Android开发中比较常见,因为Android不会...
  • 2019独角兽企业重金招聘Python工程师标准>>> ...
  • DCloud 5+ SDK插件开发 http://ask.dcloud.net.cn/docs/#http://ask.dcloud.net.cn/article/66 开发文档的链接 首先官方文档这方面描述的不太清楚,走了不少弯路,下面的评论很重要!!! ...
  • 官网直播网址http://www.dongyibiancheng.com/expand/4yg2w9 每天上午十点 ,下午三点,晚上八点,直播课程H5混合开发,框架MUI-H5+,微信小程序,Vue.js,PHP,JavaScript,ReactNative,
  • H5+ and mui学习记录

    2019-10-01 05:51:25
    2、其他的原生方法可以通过Native.js调用 webview 3、webview是调用原生界面的H5+对象 4、单个webview只承载单个页面的dom,多个webview可以组合,嵌套。 例如,为了流畅滚动,可以将目标页面分解为主页面和内容...

空空如也

空空如也

1 2 3 4 5 ... 16
收藏数 308
精华内容 123
关键字:

+h5native