• 完全征服React Native

    2018-10-22 21:38:05
    React Native是Facebook于2015年推出的跨平台开发...本课程采用新的ES6开发,主要内容包括ReactNative的基础知识,ReactNative的布局,组件,API,封装本地API和组件,发布ReactNative App,本地与ReactNative深度结合
  • (一)前言 前几节课程我们对于React Native的一些基础配置,开发工具以及调试,Android项目移植做了相关讲解,今天一起来学习一下另外一个比较重要的知识点,就是React Native项目签名打包。 刚创建的React Native...

    转载请标明出处:

    http://blog.csdn.net/developer_jiangqq/article/details/50525976

    本文出自:【江清清的博客】


    ()前言       

            【好消息】个人网站已经上线运行,后面博客以及技术干货等精彩文章会同步更新,请大家关注收藏:http://www.lcode.org 

           前几节课程我们对于React Native的一些基础配置,开发工具以及调试,Android项目移植做了相关讲解,今天一起来学习一下另外一个比较重要的知识点,就是React Native项目签名打包。    

              刚创建的React Native技术交流3群(496508742),React Native技术交流4群(458982758),请不要重复加群!欢迎各位大牛,React Native技术爱好者加入交流!同时博客左侧欢迎微信扫描关注订阅号,移动技术干货,精彩文章技术推送!

    在我们的React NativeFor Android应用开发完成之后,那么就需要进行发布上传应用市场了,在上传之后,那么有一个很重要的步骤就是签名打包。下面我们来详细看一下怎么样进行签名打包React Native应用。具体关于Android的签名文件生成(点击进入-注意翻墙)

    ()Android签名文件生成      

             上面有一个Android官方的签名生成方法的地址,大家可以进行详情查看,不过需要翻墙哦~。我现在给大家讲解两种签名生成的方法::keytool命令方式生成 ,②:AndroidStudio IDE进行生成。

            2.1.keytool命令生成签名秘钥

    我们可以命令行运行如下命令:

    keytool -genkey -v -keystore my-release-key.keystore  -alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000

    [注意].我这边环境变量已经配置了,注意上面的my-release-key这个名字可以自己取名,同时my-key-alias也是自己取名,其中第二个名称alias参数后边的别名,在后面你在为应用签名的时候需要用到,所以暂时记录一下这个别名。

    上面的命令我们需要输入密钥库(keystore)密码和对应秘钥的密码,然后设置名字,组织,国家,省份相关的信息,最后会生成my-release-key.keystore的签名文件。

    具体命令截图如下:


    现在我们去用户默认目录下面会生成my-release-key.keystore文件。具体截图如下:


           2.2.Android Studio IDE进行生成秘钥文件

    个人比较倾向于这一种方法,首先是图形界面的,而且少了命令行书写的问题,而且以前我记得在使用Eclipse开发Android的时候也一直使用这种方式,下面我们来看一下生成签名的具体步骤:

    首先打开AndroidStudio菜单选择build->Generate Signed APK 在打开的界面点击Next,会弹出下面的界面


             然后点击create new在弹出的界面中选择填写秘钥存放的位置,名称,密码。同样还要写别名的名字,证书的所有者,国家,组织以及城市相关信息。


    点击OK,会默认填写上创建好的签名的信息,


    最后点击finish会生成签名秘钥,不过大家请注意看这边生成的秘钥和第一个命令行方法的秘钥的后缀不太一样的,这边是以jks结尾的,不过也没问题哦~也是同样可以签名的。


          以上两种方式已经给大家演示了,打包签名的方法了,下面我们正式来进行配置打包生成APK了。

    ()Gradle配置

            3.1.Gradle配置

                 .首先我把刚刚成的签名文件复制到项目android/app文件夹下面(这边采用AS生成签名test.jks)


    然后进行修改项目中gradle.properties文件,进行添加如下的代码(注意下面的签名和别名的名称和上一步放入的test.jks要一样,下面两项分别填写签名和别名的密码)-我取的密码为ztt12345

    MYAPP_RELEASE_STORE_FILE=test.jks
    MYAPP_RELEASE_KEY_ALIAS=test_alias
    MYAPP_RELEASE_STORE_PASSWORD=ztt12345
    MYAPP_RELEASE_KEY_PASSWORD=ztt12345

    这一步我们是进行全局的gradlde进行变量化的配置,后边我们会在后边的步骤中给相应的应用进行签名。

    [注意].以上的签名秘钥请大家一定要妥善保管,因为在应用发布的时候需要的。

     3.2.给应用添加签名-配置局部应用Gradle文件

            直接在工程目录下得android/app/build.gradle中以下节点添加如下内容:

    ...
    android {
        ...
        defaultConfig { ... }
        signingConfigs {
            release {
                storeFilefile(MYAPP_RELEASE_STORE_FILE)
                storePassword MYAPP_RELEASE_STORE_PASSWORD
                keyAlias MYAPP_RELEASE_KEY_ALIAS
                keyPasswordMYAPP_RELEASE_KEY_PASSWORD
            }
        }
        buildTypes {
            release {
                ...
                signingConfigsigningConfigs.release
            }
        }
    }
    …

    具体实例配置截图如下:


    ()生成签名包

             对于生成签名包得方式我们要分两种情况进行区分对待。第一种在项目目录android/app下有react.gradle文件的(这个采用react-nativeinit xxproject命令生成项目详情请看:点击进入第一讲)

    第二种是不存在react.gradle文件,主要是通过原生Android项目移植到React Native平台中(该生成详情详情请看:点击进入第四讲)。下面这两种方法都讲一下:

            4.1.对存在react.gradle文件的项目打包

    命令行切到reactnative主目录,然后运行下面的命令,请注意下面 android就是Android项目的目录名称

     cd android && ./gradlew assembleRelease

    这样运行截图如下:



           该命令运行结束之后,会在android/app/build/outputs/apk目录下面生成app-release.apk该文件,然后可以使用该apk进行上线发布。


    4.2.对与不存在react.gradle文件的项目打包

             这边因为react.gradle文件不存在,主要针对第四讲课程中的项目(Android原生项目移植到React Native平台)来进行举例。

    首先命令切换到该reactnative项目的主目录,然后运行以下的命令,生成assets文件夹

    mkdir -p android/app/src/main/assets

       紧接着运行以下命令,进行生成inde.android.bundle文件

    react-native bundle --platform android --dev false --entry-file index.android.js \
      --bundle-output android/app/src/main/assets/index.android.bundle \
      --assets-dest android/app/src/main/res/

    具体运行截图如下:


    生成该文件目录截图如下:


    最后运行之前的命令,进行代码和资源文件打包,生成的带有签名的apk还是在上面的目录中。

    cd android && ./gradlew assembleRelease

    ()运行Apk

            上面的步骤我们已经完成了项目的签名打包在对应的目录中生成中apk文件,下面我们直接运行以下的命令进行将apk安装到设备中,我这边直接采用了模拟器进行测试了。

    cd android && ./gradlew installRelease

    该命令会进行安装我们的apk到我们的设备中,不过该不会安装完自动打开我们的app,我们需要自己点击启动一下即可了。完美运行截图如下:


    ()最后总结

              今天我们主要介绍了React Native for Android项目如何创建签名,以及打包的具体方法。大家有问题可以加一下群React Native技术交流群(282693535)或者底下进行回复一下。

           尊重原创,转载请注明:From Sky丶清(http://blog.csdn.net/developer_jiangqq) 侵权必究!

           关注我的订阅号(codedev123),每天分享移动开发技术(Android/IOS),项目管理以及博客文章!(欢迎关注,第一时间推送精彩文章)

         关注我的微博,可以获得更多精彩内容

          

    展开全文
  • (一)前言 今天我们一起来看一下WebView组件讲解以及使用实例 刚创建的React Native技术交流群(282693535),欢迎各位大牛,React Native技术爱好者加入交流!同时博客左侧欢迎微信扫描关注订阅号,移动技术干货,精彩...

    转载请标明出处:

    http://blog.csdn.net/developer_jiangqq/article/details/50676379

    本文出自:【江清清的博客】

    ()前言

             【好消息】个人网站已经上线运行,后面博客以及技术干货等精彩文章会同步更新,请大家关注收藏:http://www.lcode.org       

            今天我们一起来看一下WebView组件讲解以及使用实例

              刚创建的React Native技术交流3群(496508742),React Native技术交流4群(458982758),请不要重复加群!,欢迎各位大牛,React Native技术爱好者加入交流!同时博客左侧欢迎微信扫描关注订阅号,移动技术干货,精彩文章技术推送!

              WebView组件进行创建渲染一个原生的WebView,进行加载一个网页。

    ()属性方法

    1. 承可以使View组件所有属性和Style(具体查看:http://facebook.github.io/react-native/docs/view.html#content http://facebook.github.io/react-native/docs/view.html#style)
    2. automaticallyAdjustContentInsets bool   设置是否自动调整内容
    3. contentInset  {top:number,left:number,bottom:number,right:number}  设置内容所占的尺寸大小
    4. html  string  WebView加载的HTML文本字符串
    5. injectJavaScript  string 当网页加载之前进行注入一段js代码
    6. onError function  方法 当网页加载失败的时候调用
    7. onLoad  function 方法  当网页加载结束的时候调用
    8. onLoadEnd fucntion 当网页加载结束调用,不管是成功还是失败
    9. onLoadStart  function  当网页开始加载的时候调用
    10. onNavigationStateChange function方法  当导航状态发生变化的时候调用
    11. renderError  function  该方法用于渲染一个View视图用来显示错误信息
    12. renderLoagin function  该方法用于渲染一个View视图用来显示一个加载进度指示器
    13. startInLoadingState  bool  
    14. url  string  设置加载的网页地址
    15. allowsInlineMediaPlayback  bool   该适合iOS平台,设置决定当使用HTML5播放视频的时候在当前页面位置还是使用原生的全屏播放器播放,默认值false。【注意】.为了让视频在原网页位置进行播放,不光要设置该属性为true,还必须要设置HTML页面中video节点的包含webkit-playsinline属性
    16. bounces bool  该适合iOS平台 设置是否有界面反弹特性
    17. domStorageEnabled bool  该适合Android平台 该只适合于Android平台,用于控制是否开启DOM Storage(存储)
    18. javaScriptEnabled  bool  该适合于Android平台,是否开启javascript,在iOS中的WebView是默认开启的
    19. onShouldStartLoadWithRequest  function  该适合iOS平台,该允许拦截WebView加载的URL地址,进行自定义处理。该方法通过返回true或者falase来决定是否继续加载该拦截到请求
    20. scalesPageToFit  bool  该适合iOS平台  用于设置网页是否缩放自适应到整个屏幕视图以及用户是否可以改变缩放页面
    21. scrollEnabled  bool    该适合iOS平台 用于设置是否开启页面滚动

    ()实战实例

           上面我已经对于WebView组件的基本介绍以及相关属性方法做了讲解,下面我们用几个实例来演示一下WebView组件的使用。

           3.1.先演示一个WebView组件最基本的使用方法,直接加载一个网页,具体代码如下:

    'use strict';
    import React, {
      AppRegistry,
      Component,
      StyleSheet,
      Text,
      View,
      WebView,
    } from'react-native';
    var DEFAULT_URL = 'http://www.lcode.org';
     
    var WebViewDemo =React.createClass({
      render: function() {
        return (
          <View style={{flex:1}}>
            <Textstyle={{height:40}}>简单的网页显示</Text>
            <WebViewstyle={styles.webview_style}
              url={DEFAULT_URL}
              startInLoadingState={true}
              domStorageEnabled={true}
              javaScriptEnabled={true}
              >
            </WebView>
          </View>
        );
      },
    });
    var styles =StyleSheet.create({
        webview_style:{ 
           backgroundColor:'#00ff00',  
        }
    });
     
    AppRegistry.registerComponent('WebViewDemo',() => WebViewDemo);

    运行效果截图如下:


       3.2.WebView加载本地的HTML静态字符串,具体代码如下:

    'use strict';
    import React, {
      AppRegistry,
      Component,
      StyleSheet,
      Text,
      View,
      WebView,
    } from'react-native';
    var DEFAULT_URL = 'http://www.lcode.org';
    const HTML = `
    <!DOCTYPEhtml>\n
    <html>
      <head>
        <title>HTML字符串</title>
        <metahttp-equiv="content-type" content="text/html;charset=utf-8">
        <meta name="viewport"content="width=320, user-scalable=no">
        <style type="text/css">
          body {
            margin: 0;
            padding: 0;
            font: 62.5% arial, sans-serif;
            background: #ccc;
          }
          h1 {
            padding: 45px;
            margin: 0;
            text-align: center;
            color: #33f;
          }
        </style>
      </head>
      <body>
        <h1>加载静态的HTML文本信息</h1>
      </body>
    </html>
    `;
    var WebViewDemo =React.createClass({
      render: function() {
        return (
          <View style={{flex:1}}>
            <WebViewstyle={styles.webview_style}
              html={HTML}
              startInLoadingState={true}
              domStorageEnabled={true}
              javaScriptEnabled={true}
              >
            </WebView>
          </View>
        );
      },
    });
    var styles =StyleSheet.create({
        webview_style:{ 
           backgroundColor:'#00ff00',  
        }
    });
     
    AppRegistry.registerComponent('WebViewDemo',() => WebViewDemo);

     运行效果截图如下:


    ()最后总结

              今天我们主要学习一下WebView组件的基本介绍和实例演示使用,具体还有更加详细的使用方法会在后面进阶中继续更新的。大家有问题可以加一下群React Native技术交流群(282693535)或者底下进行回复一下。

           尊重原创,转载请注明:From Sky丶清(http://blog.csdn.net/developer_jiangqq) 侵权必究!

           关注我的订阅号(codedev123),每天分享移动开发技术(Android/IOS),项目管理以及博客文章!(欢迎关注,第一时间推送精彩文章)

         关注我的微博,可以获得更多精彩内容

          

    展开全文
  • 首先我们要有个概念:react native里面是兼容大部分我们在css里面用到的布局方式,此外接触过css里面flex布局方式的话,我们会发现react native内的flex布局方式基本上和css里的flex布局方式类似,所以不要觉得react...

    刚刚做完了一个项目,基本上把react native各种布局方式都用上了,发现了很多坑,也学会和很多,这里给大家分享一下哈。

    首先我们要有个概念:react native里面是兼容大部分我们在css里面用到的布局方式,此外接触过css里面flex布局方式的话,我们会发现react native内的flex布局方式基本上和

    css里的flex布局方式类似,所以不要觉得react native 布局不好做,其实他比起css来说要容易,比原生开发更是容易不知道多少倍。战略上藐视他!!!

    对题:居中

    首先大概说下flex需要知道的地方:

    flex布局依赖于flexDirection的,意思就是说:以View为例,如果flex布局方式的话,如果它内部子组件的话他们的排列方向有横向和纵向之分。默认的react native已经设定了flex内部的布局为纵向的。

    [javascript] view plain copy
    1. <View style={{backgroundColor:"red"}}>  
    2.         <View style={{backgroundColor:"green"}}>  
    3.           <Text>1</Text>  
    4.         </View>  
    5.         <View style={{backgroundColor:"orange"}}>  
    6.           <Text>1</Text>  
    7.         </View>  
    8.         <View style={{backgroundColor:"blue"}}>  
    9.           <Text>1</Text>  
    10.         </View>  
    11.       </View>  
    结果:



    那如果是横向的呢:

    [javascript] view plain copy
    1. <View style={{backgroundColor:"red",flexDirection:"row",height:200}}>  
    2.         <View style={{backgroundColor:"green"}}>  
    3.           <Text>1</Text>  
    4.         </View>  
    5.         <View style={{backgroundColor:"orange"}}>  
    6.           <Text>1</Text>  
    7.         </View>  
    8.         <View style={{backgroundColor:"blue"}}>  
    9.           <Text>1</Text>  
    10.         </View>  
    11.       </View>  

    结果:


    是不是很简单呢,但是需要指出的是:

    我们会发现在当flexDirection是“row”时,内部组件排列顺序为横向排列时,子组件的高度会自动撑满父组件。同样的道理如果是“column”时子组件的宽度会自动撑满父组件。记住这一点。

    所以,大概有个印象了吧。

    下面我们来说说flex上的值,我们在看别人代码的时候经常看到flelx:1,flex:2 之类的东东,这些到底是干嘛的呢?实践一下吧。我们以flexDirection为“column” 为例

    [javascript] view plain copy
    1. <View style={{backgroundColor:"red",flexDirection:"column",height:200}}>  
    2.         <View style={{backgroundColor:"green",flex:1}}>  
    3.           <Text>1</Text>  
    4.         </View>  
    5.         <View style={{backgroundColor:"orange"}}>  
    6.           <Text>3</Text>  
    7.         </View>  
    8.         <View style={{backgroundColor:"blue"}}>  
    9.           <Text>3</Text>  
    10.         </View>  
    11.       </View>  
    结果:

    结果很明显啦,flex:1 会把其余组件(没有设置flex)剩余的空间全部占完。

    那我们试试下面这些:

    [javascript] view plain copy
    1. <View style={{backgroundColor:"red",flexDirection:"column",height:200}}>  
    2.         <View style={{backgroundColor:"green",flex:1}}>  
    3.           <Text>1</Text>  
    4.         </View>  
    5.         <View style={{backgroundColor:"orange",flex:1}}>  
    6.           <Text>3</Text>  
    7.         </View>  
    8.         <View style={{backgroundColor:"blue",flex:1}}>  
    9.           <Text>3</Text>  
    10.         </View>  
    11.       </View>  

    结果:


    所以是不是比起原生来简单多了,仅仅flex:1 偷笑,就均分了。对我们已经发现了一种自动居中的方式了。


    react native 实现居中有多种方式:

    [javascript] view plain copy
    1. <View style={{backgroundColor:"red",flexDirection:"column",height:200}}>  
    2.         <View style={{backgroundColor:"green",flex:1}}>  
    3.           <Text>1</Text>  
    4.         </View>  
    5.         <View style={{backgroundColor:"orange"}}>  
    6.           <Text>2</Text>  
    7.         </View>  
    8.         <View style={{backgroundColor:"blue",flex:1}}>  
    9.           <Text>3</Text>  
    10.         </View>  
    11.       </View>  

    结果:


    so easy大笑,居中了。


    下面我们说说aliginItems justifyContent,这两个属性都是设置在父组件上,用于界定子组件的位置。
    aliginItems:官方解释:
    aligns children in the cross direction. For example, if children are flowing vertically, alignItems controls how they align horizontally. It works like align-items in CSS。
    
    大白话:
    如果父组件flexDirection设置为:"column",则aliginItems指定的是在横向上,子组件的定位方式。同理如果父组件felxDirection设置为“row”,则alignItems指定的是在纵向上子组件的定位方式。
    上代码:
    [javascript] view plain copy
    1. <View style={{backgroundColor:"red",flexDirection:"column",height:200,alignItems:"center"}}>  
    2.         <View style={{backgroundColor:"green",flex:1}}>  
    3.           <Text>1</Text>  
    4.         </View>  
    5.         <View style={{backgroundColor:"orange"}}>  
    6.           <Text>2</Text>  
    7.         </View>  
    8.         <View style={{backgroundColor:"blue",flex:1}}>  
    9.           <Text>3</Text>  
    10.         </View>  
    11.       </View>  
    结果:
    ok,我们貌似已经实现垂直水平居中了。

    so,那justifyContent这个属性是做什么的呢?

    justifyContent:官方解释:
    justifyContent aligns children in the main direction. For example, if children are flowing vertically, justifyContent controls how they align vertically. It works like justify-content in CSS (default: flex-start). See https://developer.mozilla.org/en-US/docs/Web/CSS/justify-content for more details.
    
    大白话:如果flexDirection为“row”,那么justifyContent指定的是子组件在横向上的对齐方式,如果flexDirection为“column”则justifyContent指定的是子组件在纵向上的对齐方式。
    上代码:
    [javascript] view plain copy
    1. <View style={{backgroundColor:"red",flexDirection:"column",height:200,justifyContent:"center"}}>  
    2.         <View style={{backgroundColor:"orange"}}>  
    3.           <Text>2</Text>  
    4.         </View>  
    5. </View>  
    结果:
    ok。


    在说说我们的aliginSelf属性

    我们经常会遇到这样的情况


    这在react native里面怎么实现呢?呵呵,这就用到我们说的alignSelf属性了,注意alignSelf是标定在子组件上的,用于指定子组件的对齐方式。如果父组件的flexDirection是“row”,那么alignSelf就是标定子组件在竖直方向上的对齐方式,同理如果父组件的flexDirection标定为“column”,那么alignSelf指定的就是子组件在水平方向

    上代码:

    [javascript] view plain copy
    1. <View style={{backgroundColor:"red",flexDirection:"column",height:200,justifyContent:"center"}}>  
    2.         <View style={{backgroundColor:"orange",flexDirection:"row",alignItems:"center"}}>  
    3.           <Text style={{fontSize:40}}>你好,</Text>  
    4.           <Text style={{fontSize:15,alignSelf:"flex-end"}}>小明我们明天就要去</Text>  
    5.           <Text style={{fontSize:40}}>长城</Text>  
    6.           <Text style={{fontSize:15,alignSelf:"flex-end"}}>了</Text>  
    7.         </View>  
    8.       </View>  
    结果:


    明白了吧,alignSelf有“flex-start”,"flex-end","center",这几个属性。纳尼,“center”,得意我们是不是又发现了一种新的居中方式呢。不过alignSelf只适用于与父组件布局方向相垂直方向上的对齐方式。alignSelf的优先级大于alignItems

    所以:居中如何实现呢,有下面几种方法

    1】通过flex实现、2】通过flex实现、3】还是通过flex实现大笑

    欢迎拍砖,后续给大家讲讲react-native内的局对布局,大坑

    展开全文
  • 解锁React Native开发新姿势,一网打尽React Native最新与最热技术,点我Get!!! 前言 一直想写一下我在React Native原生模块封装方面的一些经验和心得,来分享给大家,但实在抽不开身,今天看了一下日历发现2018年...

    期待已久的新课上线啦!解锁React Native开发新姿势,一网打尽React Native最新与最热技术,点我Get!!!

    前言

    一直想写一下我在React Native原生模块封装方面的一些经验和心得,来分享给大家,但实在抽不开身,今天看了一下日历发现2018年马上就结束了,所以就赶年底将这篇博文写好并发布(其实是两篇:要看iOS篇的点这里《React Native iOS原生模块开发》)。

    我平时在用React Native开发App时会用到一些原生模块,比如:在做社会化分享、第三方登录、扫描、通信录,日历等等,想必大家也是一样。

    关于在React Native中使用原生模块,在这里引用React Native官方文档的一段话:

    有时候App需要访问平台API,但在React Native可能还没有相应的模块。或者你需要复用一些Java代码,而不想用JavaScript再重新实现一遍;又或者你需要实现某些高性能的、多线程的代码,譬如图片处理、数据库、或者一些高级扩展等等。
    我们把React Native设计为可以在其基础上编写真正的原生代码,并且可以访问平台所有的能力。这是一个相对高级的特性,我们并不期望它应当在日常开发的过程中经常出现,但它确实必不可少,而且是存在的。如果React Native还不支持某个你需要的原生特性,你应当可以自己实现对该特性的封装。

    上面是我翻译React Native官方文档上的一段话,大家如果想看英文版可以点这里:Native Modules
    在这篇文章中呢,我会带着大家来开发一个从相册获取照片并裁切照片的项目,并结合这个项目来具体讲解一下如何一步步开发React Native Android原生模块的。

    React Native Android原生模块开发实战 教程 心得-如何创建一个React Native Android原生模块

    提示:告诉大家一个好消息,React Native视频教程发布了,大家现可以看视频学React Native了。

    首先,让我们先看一下,开发Android原生模块的主要流程。

    开发Android原生模块的主要流程

    在这里我把构建React Native Android原生模块的流程概括为以下三大步:

    1. 编写原生模块的相关Java代码;
    2. 暴露接口与数据交互;
    3. 注册与导出React Native原生模块;

    接下来让我们一起来看一下每一步所需要做的一些事情。

    原生模块开发实战

    在这里我们就以开发一个从相册获取照片并裁切照片的实战项目,来具体讲解一下如何开发React Native Android原生模块的。

    编写原生模块的相关Java代码

    这一步我们需要用到AndroidStudio。
    首先我们用AndroidStudio打开React Native项目根目录下的android目录,如图:

    open-react-native-android-native-project

    用AndroidStudio第一次打开这个Android项目的时候,AndroidStudio会下载一些此项目所需要的依赖,比如项目所依赖的Gradle版本等。这些依赖下载完成之后呢,AndroidStudio会对项目进行初始化,初始化成功之后在AndroidStudio的工具栏中可以看到一个名为“app”的一个可运行的模块,如图:

    open-react-native-android-native-project-success

    接下来呢,我们就可以编写Java代码了。

    首先呢,我们先来实现一个Crop接口:

    public interface Crop {
        /**
         * 选择并裁切照片
         * @param outputX
         * @param outputY
         * @param promise
         */
        void selectWithCrop(int outputX,int outputY,Promise promise);
    }
    

    我们创建一个CropImpl.java,在这个类中呢,我们实现了从相册选择照片以及裁切照片的功能:

    /**
     * React Native Android原生模块开发
     * Author: CrazyCodeBoy
     * 技术博文:http://www.devio.org
     * GitHub:https://github.com/crazycodeboy
     * Email:crazycodeboy@gmail.com
     */
    
    public class CropImpl implements ActivityEventListener,Crop{
        private final int RC_PICK=50081;
        private final int RC_CROP=50082;
        private final String CODE_ERROR_PICK="用户取消";
        private final String CODE_ERROR_CROP="裁切失败";
    
        private Promise pickPromise;
        private Uri outPutUri;
        private int aspectX;
        private int aspectY;
        private Activity activity;
        public static CropImpl of(Activity activity){
            return new CropImpl(activity);
        }
    
        private CropImpl(Activity activity) {
            this.activity = activity;
        }
        public void updateActivity(Activity activity){
            this.activity=activity;
        }
        @Override
        public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
            if(requestCode==RC_PICK){
                if (resultCode == Activity.RESULT_OK && data != null) {//从相册选择照片并裁剪
                    outPutUri= Uri.fromFile(Utils.getPhotoCacheDir(System.currentTimeMillis()+".jpg"));
                    onCrop(data.getData(),outPutUri);
                } else {
                    pickPromise.reject(CODE_ERROR_PICK,"没有获取到结果");
                }
            }else if(requestCode==RC_CROP){
                if (resultCode == Activity.RESULT_OK) {
                    pickPromise.resolve(outPutUri.getPath());
                }else {
                    pickPromise.reject(CODE_ERROR_CROP,"裁剪失败");
                }
            }
        }
    
    	//...省略部分代码
      
        private void onCrop(Uri targetUri,Uri outputUri){
            this.activity.startActivityForResult(IntentUtils.getCropIntentWith(targetUri,outputUri,aspectX,aspectY),RC_CROP);
        }
    }
    

    查看视频教程

    关于Android拍照、从相册或文件中选择照片,裁剪以及压缩照片等更高级的功能实现,大家可以参考开源项目TakePhoto

    实现了从相册选择照片以及裁切照片的功能之后呢,接下来我们需要将public void selectWithCrop(int aspectX, int aspectY, Promise promise)暴露给React Native,以供js调用。

    暴露接口与数据交互

    接下了我们就向React Native暴露接口以及做一些数据交互部分的操作。为了暴露接口以及进行数据交互我们需要借助React Native的ReactContextBaseJavaModule类,在这里我们创建一个ImageCropModule.java类让它继承自ReactContextBaseJavaModule

    创建一个ReactContextBaseJavaModule

    /**
     * React Native Android原生模块开发
     * Author: CrazyCodeBoy
     * 技术博文:http://www.devio.org
     * GitHub:https://github.com/crazycodeboy
     * Email:crazycodeboy@gmail.com
     */
    
    public class ImageCropModule extends ReactContextBaseJavaModule implements Crop{
        private CropImpl cropImpl;
        public ImageCropModule(ReactApplicationContext reactContext) {
            super(reactContext);
        }
    
        @Override
        public String getName() {
            return "ImageCrop";
        }
      
    	//...省略部分代码
      
        @Override @ReactMethod
        public void selectWithCrop(int aspectX, int aspectY, Promise promise) {
            getCrop().selectWithCrop(aspectX,aspectY,promise);
        }
        private CropImpl getCrop(){
            if(cropImpl==null){
                cropImpl=CropImpl.of(getCurrentActivity());
                getReactApplicationContext().addActivityEventListener(cropImpl);
            }else {
                cropImpl.updateActivity(getCurrentActivity());
            }
            return cropImpl;
        }
    }
    

    查看视频教程

    ImageCropModule.java类中,我们重写了public String getName()方法,来暴露我们原生模块的名字。并在public void selectWithCrop(int aspectX, int aspectY, Promise promise)上添加了@ReactMethod注解来暴露接口,这样以来我们就可以在js文件中通过ImageCrop.selectWithCrop来调用我们所暴露给React Native的接口了。

    接下来呢,我们来看一下原生模块和js模块是如何进行数据交互的?

    原生模块和JS进行数据交互

    在我们要实现的从相册选择照片并裁切的项目中,js模块需要告诉原生模块照片裁切的比例,等照片裁切完成后,原生模块需要对js模块进行回调来告诉js模块照片裁切的结果,在这里我们需要将照片裁切后生成的图片的路径告诉js模块。

    提示:在所有的情况下js和原生模块之前进行通信都是在异步的情况下进行的。

    接下来我们就来看下一JS是如何向原生模块传递数据的?

    JS向原生模块传递数据:

    为了实现JS向原生模块进行传递数据,我们可以直接通过调用原生模块所暴露出来的接口,来为接口方法设置参数。这样以来我们就可以将数据通过接口参数传递到原生模块中,如:

      /**
         * 选择并裁切照片
         * @param outputX
         * @param outputY
         * @param promise
         */
        void selectWithCrop(int outputX,int outputY,Promise promise);
    

    通过上述代码我们可以看出,js模块可以通过selectWithCrop方法来告诉原生模块要裁切照片的宽高比,最后一个参数是一个Promise,照片裁剪完成之后呢,原生模块可以通过Promise来对js模块进行回调,来告诉裁切结果。

    既然是js和Java进行数据传递,那么他们两者之间是如何进行类型转换的呢:
    在上述例子中我们通过@ReactMethod注解来暴露接口,被 @ReactMethod标注的方法支持如下几种数据类型。

    @ReactMethod标注的方法支持如下几种数据类型的参数:

    Boolean -> Bool
    Integer -> Number
    Double -> Number
    Float -> Number
    String -> String
    Callback -> function
    ReadableMap -> Object
    ReadableArray -> Array
    

    原生模块向JS传递数据:

    原生模块向JS传递数据我们可以借助Callbacks与Promises,接下来就讲一下如何通过他们两个进行数据传递的。

    Callbacks

    原生模块支持一个特殊类型的参数-Callbacks,我们可以通过它来对js进行回调,以告诉js调用原生模块方法的结果。
    将我们selectWithCrop的参数改为Callbacks之后:

    @Override
    public void selectWithCrop(int aspectX, int aspectY, Callback errorCallback,Callback successCallback) {
        this.errorCallback=errorCallback;
        this.successCallback=successCallback;
        this.aspectX=aspectX;
        this.aspectY=aspectY;
        this.activity.startActivityForResult(IntentUtils.getPickIntentWithGallery(),RC_PICK);
    }
    

    在回调的时候,我们就可以这样写:

    if (resultCode == Activity.RESULT_OK) {
        successCallback.invoke(outPutUri.getPath());
    }else {
        errorCallback.invoke(CODE_ERROR_CROP,"裁剪失败");
    }
    

    在上述代码中我们通过Callbackinvoke方法来对js进行对调,下面我们来看一下Callback.java的源码:

    public interface Callback {
      /**
       * Schedule javascript function execution represented by this {@link Callback} instance
       *
       * @param args arguments passed to javascript callback method via bridge
       */
      public void invoke(Object... args);
    }
    

    Callback.java的源码中我们可以看出,它是一个只有一个public void invoke(Object... args)方法的接口,invoke方法接受一个可变参数,所以我们可以向js传递多个参数。

    接下来呢,我们在js中就可以这样来调用我们所暴露的接口:

    ImageCrop.selectWithCrop(parseInt(x),parseInt(y),(error)=>{
        console.log(error);
    },(result)=>{
        console.log(result);
    })
    

    提示:另外要告诉大家的是,无论是Callback还是我接下来要讲的Promise,我们只能调用一次,也就是"you call me once,I can only call you once"。

    Promises

    除了上文所讲的Callback之外React Native还为了我们提供了另外一种回调js的方式叫-Promise。如果我们暴露的接口方法的最后一个参数是Promise时,如:

    @Override @ReactMethod
    public void selectWithCrop(int aspectX, int aspectY, Promise promise) {
        getCrop().selectWithCrop(aspectX,aspectY,promise);
    }
    

    那么当js调用它的时候将会返回一个Promsie:

    ImageCrop.selectWithCrop(parseInt(x),parseInt(y)).then(result=> {
        this.setState({
            result: result
        })
    }).catch(e=> {
        this.setState({
            result: e
        })
    });
    

    另外,我们也可以使用ES2016的 async/await语法,来简化我们的代码:

    async onSelectCrop() {
        var result=await ImageCrop.selectWithCrop(parseInt(x),parseInt(y));
    }
    

    这样以来代码就简化了很多。

    因为,基于回调的数据传递无论是Callback还是Promise,都只能调用一次。但,在实际项目开发中我们有时会向js多次传递数据,比如二维码扫描原生模块,针对这种多次数据传递的情况我们该怎么实现呢?

    接下来我就为大家介绍一种原生模块可以向js多次传递数据的方式:

    向js发送事件

    在原生模块中我们可以向js发送多次事件,即使原生模块没有被直接的调用。为了向js传递事件我们需要用到RCTDeviceEventEmitter,它是原生模块和js之间的一个事件发射器。

    private void sendEvent(ReactContext reactContext,String eventName, @Nullable WritableMap params) {
        reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
                .emit(eventName, params);
    }
    

    在上述方法中我们可以向js模块发送任意次数的事件,其中eventName是我们要发送事件的事件名,params是此次事件所携带的数据,接下来呢我们就可以在js模块中监听这个事件了:

    componentDidMount() {
        //注册扫描监听
        DeviceEventEmitter.addListener('onScanningResult',this.onScanningResult);
    }
    onScanningResult = (e)=> {
        this.setState({
            scanningResult: e.result,
        });
    }
    

    另外,不要忘记在组件被卸载的时候移除监听:

    componentWillUnmount(){
        DeviceEventEmitter.removeListener('onScanningResult',this.onScanningResult);//移除扫描监听
    }
    

    到现在呢,暴露接口以及数据传递已经进行完了,接下来呢,我们就需要注册与导出React Native原生模块了。

    注册与导出React Native原生模块

    为了向React Native注册我们刚才创建的原生模块,我们需要实现ReactPackageReactPackage主要为注册原生模块所存在,只有已经向React Native注册的模块才能在js模块使用。

    /**
     * React Native Android原生模块开发
     * Author: CrazyCodeBoy
     * 技术博文:http://www.devio.org
     * GitHub:https://github.com/crazycodeboy
     * Email:crazycodeboy@gmail.com
     */
    public class ImageCropReactPackage implements ReactPackage {
        @Override
        public List<Class<? extends JavaScriptModule>> createJSModules() {
            return Collections.emptyList();
        }
        @Override
        public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
            return Collections.emptyList();
        }
        @Override
        public List<NativeModule> createNativeModules(
                ReactApplicationContext reactContext) {
            List<NativeModule> modules = new ArrayList<>();
            modules.add(new ImageCropModule(reactContext));
            return modules;
        }
    }
    

    查看视频教程

    在上述代码中,我们实现一个ReactPackage,接下来呢,我们还需要在android/app/src/main/java/com/your-app-name/MainApplication.java中注册我们的ImageCropReactPackage

    @Override
    protected List<ReactPackage> getPackages() {
        return Arrays.<ReactPackage>asList(
                new MainReactPackage(),
                new ImageCropReactPackage()//在这里将我们刚才创建的ImageCropReactPackage添加进来
        );
    }
    

    原生模块注册完成之后呢,我们接下来就需要为我们的原生模块导出一个js模块,以方便我们使用它。

    我们创建一个ImageCrop.js文件,然后添加如下代码:

    import { NativeModules } from 'react-native';
    export default NativeModules.ImageCrop;
    

    这样以来呢,我们就可以在其他地方通过下面方式来使用我们所导出的这个模块了:

    import ImageCrop from './ImageCrop' //导入ImageCrop.js
    //...省略部分代码
    
        onSelectCrop() {
            let x=this.aspectX?this.aspectX:ASPECT_X;
            let y=this.aspectY?this.aspectY:ASPECT_Y;
            ImageCrop.selectWithCrop(parseInt(x),parseInt(y)).then(result=> {
                this.setState({
                    result: result
                })
            }).catch(e=> {
                this.setState({
                    result: e
                })
            });
        }
    //...省略部分代码
    }
    

    查看视频教程

    现在呢,我们这个原生模块就开发好了,而且我们也使用了我们的这个原生模块。关于Android拍照、从相册或文件中选择照片,裁剪以及压缩照片等更高级的功能实现,大家也可以参考开源项目TakePhoto

    关于线程

    在React Native中,JS模块运行在一个独立的线程中。在我们为React Native开发原生模块的时候,如果有耗时的操作比如:文件读写、网络操作等,我们需要新开辟一个线程,不然的话,这些耗时的操作会阻塞JS线程。在Android中我们可以借助AsyncTask来实现多线程。另外,如果原生模块中需要更新UI,我们需要获取主线程,然后在主线程中更新UI,如:http://coding.imooc.com/class/304.html

            activity.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    if (!activity.isFinishing()) {
    
                        mSplashDialog = new Dialog(activity,fullScreen? R.style.SplashScreen_Fullscreen:R.style.SplashScreen_SplashTheme);
                        mSplashDialog.setContentView(R.layout.launch_screen);
                        mSplashDialog.setCancelable(false);
    
                        if (!mSplashDialog.isShowing()) {
                            mSplashDialog.show();
                        }
                    }
                }
            });
    

    可参考:SplashScreen.java

    告诉大家一个好消息,为大家精心准备的React Native视频教程发布了,大家现可以看视频学React Native了。

    如果,大家在开发原生模块中遇到问题可以在课程的对应章节的右边进行留言,我看到了后会及时回复的哦。

    推荐学习:视频教程《最新版React Native+Redux打造高质量上线App》

    展开全文
  •  有时候我们并不是需要全部使用React Native,我们想和原生混合开发,那我们应该怎么办呢。 先看一下我集成完之后的项目目录: 首先安装React Native node组件  1、新建一个文件夹如目录中的RN,这个...

    序:

       有时候我们并不是需要全部使用React Native,我们想和原生混合开发,那我们应该怎么办呢。


    先看一下我集成完之后的项目目录:




    首先安装React Native node组件


           1、新建一个文件夹如目录中的RN,这个文件夹用于存放React Native相关内容

           2、新建一个package.json用于安装node_modules。package.json内容如下:

          

    {
    "name": "RNHybrid",   //记得修改项目的名字
    "version": "0.0.1",
    "private": true,
    "scripts": {
    "start": "node node_modules/react-native/local-cli/cli.js start",
    "test": "jest"
    },
    "dependencies": {
    "react": "16.0.0-alpha.6",
    "react-native": "0.44.0"
    },
    "devDependencies": {
    "babel-jest": "20.0.3",
    "babel-preset-react-native": "1.9.2",
    "jest": "20.0.4",
    "react-test-renderer": "16.0.0-alpha.6"
    },
    "jest": {
    "preset": "react-native"
    }
    }

           3、cd 你的项目路径,然后执行 nmp install。如下图:




      执行完上面的命令之后,打开你的项目目录下,你就会发现node_modules都下载到你新建的文件夹中了,如图:



             4、在新建的目录下新建index.ios.js,把之前React Native的例子拷过来就可以,记得改下modules的名字

    /**
     * Sample React Native App
     * https://github.com/facebook/react-native
     * @flow
     */
    
    import React, { Component } from 'react';
    import {
      AppRegistry,
      StyleSheet,
      Text,
      View
    } from 'react-native';
    
    export default class RNHybrid extends Component {
      render() {
        return (
          <View style={styles.container}>
            <Text style={styles.welcome}>
              iOS 原生 RN混合开发!
            </Text>
          </View>
        );
      }
    }
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#F5FCFF',
      },
      welcome: {
        fontSize: 20,
        textAlign: 'center',
        margin: 10,
      },
    });
    
    AppRegistry.registerComponent('RNHybrid', () => RNHybrid);



    Cocospod安装React Native

    既然大家都来看RN了。cocospod就不用讲了吧。

    1、podfile我们要加入的内容

    路径填写你存放node_modules的路径即可。

    pod 'Yoga',  :path => ‘./RN/node_modules/react-native/ReactCommon/yoga'
    pod 'React', :path => ‘./RN/node_modules/react-native', :subspecs => [
     'Core',
     'RCTText',
     'RCTNetwork’,
     'RCTWebSocket', 
    ]


    因为Core依赖于Yoga所以要添加一下,至于项目中需要什么组件以后可以在subspecs依次添加。


    2、然后pod install就行了,比较慢,你可以撸一把了。

    3、成功之后,我们来用一下吧,我们可以在原生项目中加入RN界面试试。


     NSURL *jsCodeLocation;

        jsCodeLocation = [NSURLURLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"];

        RCTRootView *rootView = [[RCTRootViewalloc]initWithBundleURL:jsCodeLocation

                                                            moduleName:@"RNHybrid"

                                                     initialProperties:nil

                                                         launchOptions:nil];

        rootView.frame =CGRectMake(0,0,[UIScreenmainScreen].bounds.size.width, [UIScreenmainScreen].bounds.size.height);

        [self.viewaddSubview:rootView];


    下面代码大家有疑惑的估计就是这个url从哪来的,下面当你启动的时候,会告诉你。


    4、启动RN

          cd 到你上面新建的文件夹里,如我项目中的RN文件夹,然后执行react-native start


         这时候,你可以看出来,服务器启动的端口是8081,也就知道了上面那个url


    5、这时候你启动的时候如果看到下面的画面:



    修改ATS就可以了,会iOS的基本都会,不啰嗦了。


    6、在运行下试试吧,结果如图:


    展开全文
  • 当我们运行一个React Native项目的时候,React Native会启动一个默认端口号为8081的本地服务,该8081的服务就是React Native项目的一个本地服务器,用于提供JSBundle包和一些静态资源。这一切都是React Native帮...
  • 概述 自从2015年4月React Native...不过,也有公司对React Native 并不看好,如Airbnb在去年就开始宣布弃用React Native ,不管如何,React Native 的跨平台的目的是很诱惑的。 紧接着google发布了跨平台框架Flutter...
  • React Native从零开始(一)React Native编辑器配置(webstorm) 因为之前和现在都是在做Android开发的,网页什么的接触的也不多,所以对于编辑器的选择的话,我还是倾向于Webstorm。因为他的快捷键和使用方式跟...
  • (一)前言 【好消息】个人网站已经上线运行,后面博客以及技术干货等精彩文章会同步更新,请大家关注收藏:http://www.lcode.org 前几节...今天一起来学习一下另外一个比较重要的知识点,就是React Native项目签名打包。
  • 源码传送门最近学习ReactNative感觉到挺有意思的,在学习的过程中,发现网上一些人写的文章内容过时了,这主要是ReactNative的版本升级太快,如果你现在看一篇16甚至15年写的文章,把知识点和官方文档对比下,会让你...
  • 最近学习了一个网上的React Native项目,利用React Native制作一个类似于美团的App,项目属于对之前React Native常用组件的基本使用,但是仍有一些关键点值得记录。最后做成的效果如下:1、通过React Navigation来...
  • 在过去的一年中React Native经历了从v0.40到v0.52的十几次的版本迭代,可以看到,特别是0.50之后,React Native的组件库在不断地壮大,React Native也正在越来越稳定。 随着版本的升级,React Native引进了一些新的...
  • 一直想写一下我在React Native原生模块封装方面的一些经验和心得,来分享给大家,但实在抽不开身,今天看了一下日历发现马上就春节了,所以就赶在春节之前将这篇博文写好并发布。
  • 欢迎大家关注【跨平台开发那些事】公众号,定期推...基于最新版本React Native实现JsBundle预加载,界面秒开优化 一、开源库介绍 今年1月份,新开源的react-natvigation库备受瞩目。在短短不到3个月的时间,gith...
  • React Native 发布一年多了,有不少公司已经在线上产品中或小范围试水,或大范围应用,很多公司或开发者都在为 React Native 的生态系统作出自己的贡献。React Native 的开发基本上是 Javascript + 系统原生开发...
  • 本文出自《React Native学习笔记》系列文章。一款好的APP离不了一个漂亮的布局,本文章将向大家分享React Native中的布局方式FlexBox。 在React Native中布局采用的是FleBox(弹性框)进行布局。 FlexBox提供了在不同...
  • React Native BLE蓝牙通信

    2020-06-28 10:39:56
    由于项目需要,基于React Native 开发的App要跟BLE蓝牙设备通信。 在js.coach上搜索React Native BLE蓝牙组件,只找到三个组件: react-native-ble-manager:文档清晰明了,简单易懂,基本每个月都有更新,遇到...
  • React Native 混合开发-Android篇创建一个React Native 项目1. 通过npm安装react-native的方式添加一个React Native项目2. 通过react-native init来初始化一个React Native项目二、添加React Native所需要的依赖第一...
  • 近期和一些朋友聊到了 React-Native 的官方重构状态,而刚好近期发布的 0.59.x 系列版本中,上层设计出现了比较大的调整,结合体验之后的状态,就想聊聊 React-Native 的现状、新版本的升级体验、还有新支持的 React...
1 2 3 4 5 ... 20
收藏数 12,227
精华内容 4,890