React Native入門-實(shí)戰(zhàn)解析

概述

隨著app端越來(lái)越復(fù)雜,迭代越來(lái)越快,很多app采用原生+html5的方式來(lái)實(shí)現(xiàn),然后不知道什么時(shí)候,它就有了個(gè)高大上的名字 - hybrid app。類似的框架也很多,比較有名的有

這種app的原理是,用webview來(lái)實(shí)現(xiàn)類似原生的界面,也就是用h5寫的代碼是運(yùn)行在webview里的。優(yōu)點(diǎn)很明顯

  • 動(dòng)態(tài)部署(不需要每次通過應(yīng)用商店的審核,尤其是iOS,審核有時(shí)候真的覺得兩顆蛋不夠疼的。)
  • 界面靈活(用h5來(lái)寫的界面要比原生代碼靈活的多)
  • 開發(fā)迅速(一套代碼,iOS,安卓都可以跑;不管是安卓還是iOS,用原生的代碼實(shí)現(xiàn)類似webview的頁(yè)面,布局什么的都是很復(fù)雜的一件事情)

同樣,缺點(diǎn)也很明顯

  • 性能相對(duì)較差(這一點(diǎn)在安卓上尤其明顯,安卓的webview性能真的很差,而且安卓機(jī)型較多)
  • 數(shù)據(jù)處理能力較差,跑在webview里的程序,而且JavaScript是單線程的,所以很難利用好多核多線程,
  • 和硬件組建的交互較差,例如相冊(cè),藍(lán)牙,motion等。

React Native

React native是facebook公司推出的一個(gè)app開發(fā)框架,facebook也有很多其他的框架,在這里都可以找到。

使用純r(jià)eact native開發(fā)的時(shí)候,實(shí)際的開發(fā)語(yǔ)言是JavaScript和框架React。寫一套代碼,同時(shí)運(yùn)行在多個(gè)平臺(tái)上。

和上文提到的hybrid app最大的區(qū)別是

用Reactive Native開發(fā)的時(shí)候,實(shí)際運(yùn)行的都是原生的代碼

目前React Native支持

  • Android 4.1
  • iOS 7.0

它的優(yōu)點(diǎn)

  • 可以動(dòng)態(tài)部署,因?yàn)槭怯肑S寫的,并不需要編譯,所以可以在運(yùn)行期動(dòng)態(tài)的從服務(wù)器下載,并且執(zhí)行。
  • 開發(fā)期間,修改界面的時(shí)候,只需要Command+R來(lái)刷新即可,不需要每次修改都蛋疼的重新編譯
  • 支持和原生代碼混合編程,所以,對(duì)于那些性能要求較高,數(shù)據(jù)處理復(fù)雜的頁(yè)面,仍然可以用原生的代碼來(lái)實(shí)現(xiàn)。

本文最終的效果

本文的目的是實(shí)現(xiàn)一個(gè)從網(wǎng)絡(luò)獲取數(shù)據(jù),加載到ListView,然后點(diǎn)擊某一行可以跳轉(zhuǎn)到詳情頁(yè)。


React Native環(huán)境搭建

由于本文側(cè)重的是如何使用React Native進(jìn)行開發(fā),所以并不會(huì)詳細(xì)講解如何安裝和搭建環(huán)境??梢詤⒖脊俜轿臋n,搭建很簡(jiǎn)單

官方文檔鏈接

有幾點(diǎn)提一下

  • 最好是Mac電腦,OS X系統(tǒng),因?yàn)閕OS運(yùn)行環(huán)境需要OSX
  • iOS目前需要XCode 7 +
  • 安卓需要Android SDK,和模擬器

文檔

關(guān)于React Native的文檔,在這里你都可以找到,這個(gè)系列我不會(huì)翻譯facebook的文檔。能閱讀英文文檔是程序員的一項(xiàng)基本技能,但是我會(huì)在使用的時(shí)候簡(jiǎn)單提一下

創(chuàng)建一個(gè)工程

打開終端,cd到想要的目錄去,然后

react-native init LeoRNWeather

可以看到生成了一個(gè)LeoRNWeather的文件夾,這個(gè)文件夾的默認(rèn)的文件如下

android  //安卓的工程        
index.ios.js //iOS的程序入口文件   
node_modules //
index.android.js //安卓的入口文件  
ios         //iOS的工程
package.json //全局的描述信息,本文就使用默認(rèn)的了

對(duì)了我使用的IDE,是Atom
然后,可以手動(dòng)打開 ios 目錄下的XCode 工程,然后點(diǎn)擊運(yùn)行,如果能見到下面截圖,代表運(yùn)行成功

入門

記住,React Native沒有CSS,所有的實(shí)現(xiàn)都是JS的語(yǔ)法。當(dāng)你打開index.ios.js的時(shí)候,大概能發(fā)現(xiàn)幾個(gè)模塊

導(dǎo)入的模塊,要先導(dǎo)入才能使用

import React, {
    ****
} from 'react-native';

樣式布局定義,用JS的語(yǔ)法,由StyleSheet創(chuàng)建,其中樣式使用了React的FlexBox,讓布局變的十分簡(jiǎn)單

const styles = StyleSheet.create({
   //*
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  //*
});

視圖組件,視圖繼承自Component,可以在文檔上找到很多Components

class LeoRNWeather extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          Welcome to React Native!
        </Text>
      </View>
    );
  }
}

可以看看這一行,可以看到React的視圖語(yǔ)法和H5類似,標(biāo)準(zhǔn)的XML格式。

<Text style={styles.welcome}>
      Welcome to React Native!
</Text>

?。。?!我們刪除這個(gè)文件里的全部?jī)?nèi)容,然后替換成React的風(fēng)格代碼

這時(shí)候代碼如下

import React, {
  AppRegistry,
  Component,
  StyleSheet,
  View,
  ListView,
  Text,
} from 'react-native';

var LeoRNWeather = React.createClass({
    render(){
      return (
        <View style= {styles.container}>
          <Text style={styles.blackText}>這是一個(gè)標(biāo)題</Text>
        </View>
      );
    }
});
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: 'white',
    justifyContent: 'center',
  },
  blackText:{
    fontSize:20,
    color:'rgb(0,0,0)',
    backgroundColor:'rgba(255,255,255,0)',
    textAlign:'center',
    marginLeft:10,
  },
});
AppRegistry.registerComponent('LeoRNWeather', () => LeoRNWeather);

效果


添加導(dǎo)航欄

這里提一下,在React Native中,導(dǎo)航欄有兩種

  • Navigator,大部分的情況下使用這個(gè),由facebook的react native團(tuán)隊(duì)進(jìn)行開發(fā),一直在維護(hù),同時(shí)支持iOS和安卓,由于在導(dǎo)航切換的時(shí)候需要進(jìn)行大量的加載,所以會(huì)占用JS線程較多時(shí)間。
  • NavigatorIOS,很少使用,由開源社區(qū)開發(fā),有很多bug,僅僅支持iOS。但是內(nèi)部由原生的- UINavigationController實(shí)現(xiàn),所以實(shí)際運(yùn)行的時(shí)候,和原生的iOS導(dǎo)航一樣,有一樣的動(dòng)畫
    本文使用NavigatorIOS,react native的相關(guān)資料還不是很多,一定要會(huì)看英文文檔,NavigationIOS的文檔可以在這里找到

在頂部import引入

NavigatorIOS,

然后,重寫LeoRNWeather,增加導(dǎo)航欄,其中

  • initialRoute 定義最初的頁(yè)面,類似iOS中的rootViewController,title表示標(biāo)題,component表示渲染的對(duì)象,是Component的子類
var LeoRNWeather = React.createClass({
  render: function() {
    return (
      <NavigatorIOS
        style={styles.container}
        initialRoute={{
          title: '主頁(yè)',
          component: ListScreen,
        }}
      />
    );
  }
});

創(chuàng)建ListScreen

var ListScreen = React.createClass({
    render(){
      return (
        <View style= {styles.container}>
          <Text style={styles.blackText}>blog.csdn.net/hello_hwc</Text>
        </View>
      );
    }
});

然后, Save,選擇模擬器,command+R刷新,可以看到效果(修改了文字)

添加背景圖

首先,在目錄里添加一張圖片

Tips:當(dāng)向iOS工程中Images.xcassets添加了圖片或者android添加了res/drawable添加圖片的時(shí)候,需要重新編譯

然后,將index.ios.js修改成如下

import React, {
  AppRegistry,
  Component,
  StyleSheet,
  View,
  ListView,
  Text,
  NavigatorIOS,
  Image,
} from 'react-native';
var ListScreen = React.createClass({
    render(){
      return (
        <Image source={require('./img/background.png')} style={styles.backgroundImg}>
          <Text style={styles.whiteText}>blog.csdn.net/hello_hwc</Text>
        </Image>
      );
    }
});

var LeoRNWeather = React.createClass({
  render: function() {
    return (
      <NavigatorIOS
        style={styles.container}
        initialRoute={{
          title: '主頁(yè)',
          component: ListScreen,
        }}
      />
    );
  }
});

const styles = StyleSheet.create({
  backgroundImg:{
    flex:1,
    width: null,
    height: null,
    flexDirection: 'row',
    justifyContent: 'center',
    alignItems: 'center',
  },
  whiteText:{
    fontSize:20,
    color:'rgb(255,255,255)',
    backgroundColor:'rgba(255,255,255,0)',
    textAlign:'left',
    marginLeft:10,
  },
container: {
    flex: 1,
    backgroundColor: 'white',
    justifyContent: 'center',
  },
  blackText:{
    fontSize:20,
    color:'rgb(0,0,0)',
    backgroundColor:'rgba(255,255,255,0)',
    textAlign:'center',
    marginLeft:10,
  },
});

AppRegistry.registerComponent('LeoRNWeather', () => LeoRNWeather);

效果圖

Tips
通過設(shè)置flex為1來(lái)讓寬度高度填充100%,通過height,width為null,來(lái)讓Image填充屏幕
通過設(shè)置父視圖的alignItems:'center' flexDirection:'column'來(lái)設(shè)置水平居,alignItems:'center' flexDirection:'row'來(lái)設(shè)置垂直居中

關(guān)于Flexbox布局,可以參考這片文章,寫的非常詳細(xì)

進(jìn)行網(wǎng)絡(luò)請(qǐng)求

React Native網(wǎng)絡(luò)請(qǐng)求的文檔可以在這里找到,在React中,網(wǎng)絡(luò)請(qǐng)求使用Fetch,

例如,你可以這樣去調(diào)用一個(gè)POST請(qǐng)求

fetch('https://mywebsite.com/endpoint/', {
  method: 'POST',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    firstParam: 'yourValue',
    secondParam: 'yourOtherValue',
  })
})

由于網(wǎng)絡(luò)請(qǐng)求是一個(gè)異步的請(qǐng)求,所以,它返回的是一個(gè)Promise對(duì)象,對(duì)于這個(gè)對(duì)象,有兩種處理方式

  • 同步處理then和catch
  • 異步處理,async/await

還有一個(gè)API是XMLHttpRequest,它是建立在iOS網(wǎng)絡(luò)請(qǐng)求api之上的,本文不做討論。

由于本文是這個(gè)React Native系列的第一篇,所以處理方式采用同步處理。簡(jiǎn)單直接。

在類ListScreen中,添加如下兩個(gè)方法

//Component掛載完畢后調(diào)用
  componentDidMount() {
    this.fetchData();
  },
  fetchData() {
   fetch(REQUEST_URL)
     .then((response) => response.json())
     .then((responseData) => {

     })
     .done();
  },

這里的REQUEST_URL是一個(gè)全局變量

var REQUEST_URL = 'https://raw.githubusercontent.com/LeoMobileDeveloper/React-Native-Files/master/person.json';

然后,save,command+R刷新模擬器,會(huì)發(fā)現(xiàn)Log如下

2016-04-21 13:53:49.563 [info][tid:com.facebook.React.JavaScript] [ { nickname: 'Leo', realname: 'WenchenHuang' },
{ nickname: 'Jack', realname: 'SomethingElse' } ]

為了顯示到ListView中,我們要把網(wǎng)絡(luò)請(qǐng)求來(lái)的數(shù)據(jù)存儲(chǔ)下來(lái),為L(zhǎng)istScreen添加如下方法

  • 用loaded來(lái)判斷網(wǎng)絡(luò)數(shù)據(jù)是否加載完畢
  • 用users來(lái)存儲(chǔ)實(shí)際的的網(wǎng)絡(luò)數(shù)據(jù),這里因?yàn)閡sers是ListView的dataSource,所以用ListView的DataSource來(lái)初始化
//自動(dòng)調(diào)用一次,用來(lái)設(shè)置this.state的初始狀態(tài)
  getInitialState: function() {
    return {
      loaded: false,
      users: new ListView.DataSource({
        rowHasChanged: (row1, row2) => row1 !== row2,
      }),
    };
  },

然后修改fetchData方法,在加載完畢后保存數(shù)據(jù)

fetchData() {
   fetch(REQUEST_URL)
     .then((response) => response.json())
     .then((responseData) => {
       this.setState({
         users: this.state.users.cloneWithRows(responseData),
         loaded: true,
       });
     })
     .done();
  },

Tips:this.setState會(huì)觸發(fā)render重新調(diào)用,進(jìn)行重繪

寫出一個(gè)列表

移動(dòng)開發(fā)中,列表是一個(gè)非常常用的控件。(iOS中的Tableview,android中的listview)。

ListView的文檔鏈接

ListView的優(yōu)點(diǎn)是,當(dāng)視圖離開屏幕的時(shí)候,會(huì)被復(fù)用或者移除,降低內(nèi)存使用。關(guān)于ListVIew,ReactNative團(tuán)隊(duì)進(jìn)行了很多優(yōu)化,比如event-loop只渲染一個(gè)cell,將渲染工作分成很多個(gè)小的碎片執(zhí)行,來(lái)防止掉幀。

如何使用ListView
最少需要以下兩個(gè)

  • dataSource,一個(gè)簡(jiǎn)單數(shù)組來(lái)描述MVC中的model,類似于iOS中的dataSource
  • renderRow,返回一個(gè)視圖組建.類似于iOS中的cellForRowAtIndexPath
  • renderSeparator,一般也需要這個(gè)方法,來(lái)說(shuō)生成一個(gè)分隔線

當(dāng)然,listView也支持很多,比如像iOS那樣的section header,header,footer,以及很多的事件回調(diào),在listView的文檔里,你都可以找到。

這時(shí)候的ListScreen類如下

var ListScreen = React.createClass({
  getInitialState: function() {
    return {
      loaded: false,
      users: new ListView.DataSource({
        rowHasChanged: (row1, row2) => row1 !== row2,
      }),
    };
  },
  componentDidMount() {
    this.fetchData();
  },
  fetchData() {
   fetch(REQUEST_URL)
     .then((response) => response.json())
     .then((responseData) => {
       this.setState({
         users: this.state.users.cloneWithRows(responseData),
         loaded: true,
       });
     })
     .done();
  },

  render(){
   if (!this.state.loaded) {
     return this.renderLoadingView()
    }
   return this.renderList()
  },

  renderLoadingView() {
    return (
      <Image source={require('./img/background.png')} style={styles.backgroundLoading}>
      <ActivityIndicatorIOS
        style={[styles.centering, {height: 80}]}
        size="large"
        color="#ffffff"
       />
      </Image>
    );
  },
renderList(){
    return (
      <Image source={require('./img/background.png')} style={styles.backgroundImg}>
        <ListView
          dataSource={this.state.users}
          renderRow={this.renderRow}
          style={styles.fullList}
          renderSeparator={(sectionID, rowID) => <View key={`${sectionID}-${rowID}`} style={styles.separator} />}
         />
      </Image>
    );
  },
  renderRow(user){
    return (
      <TouchableHighlight
        onPress={() => this.rowClicked(user)}
        underlayColor = '#ddd'>
      <View style={styles.rightCongtainer}>
        <Text style={styles.whiteText}>{user.nickname}</Text>
        <Text style={styles.whiteText}>{user.realname}</Text>
      </View>
      </TouchableHighlight>
    );
  },
  rowClicked(user){
    console.log(user);
  },
});

Styles如下

const styles = StyleSheet.create({
  backgroundImg:{
    flex:1,
    width: null,
    height: null,
    flexDirection: 'row'
  },
  backgroundLoading:{
    flex:1,
    width: null,
    height: null,
    alignItems: 'center',
    justifyContent: 'center',
    flexDirection: 'row'
  },
  thumbnail: {
    width: 60,
    height: 60,
  },
  rightCongtainer:{
    flex:1,
  },
  fullList:{
    flex:1,
    paddingTop: 64,
  },
  separator: {
   height: 0.5,
   backgroundColor: 'rgba(255,255,255,0.5)',
 },
 centering: {
    alignItems: 'center',
    justifyContent: 'center',
  },
  whiteText:{
    fontSize:20,
    color:'rgb(255,255,255)',
    backgroundColor:'rgba(255,255,255,0)',
    textAlign:'left',
    marginLeft:10,
  },
  blackText:{
    fontSize:20,
    color:'rgb(0,0,0)',
    backgroundColor:'rgba(255,255,255,0)',
    textAlign:'center',
    marginLeft:10,
  },
container: {
    flex: 1,
    backgroundColor: 'white',
    justifyContent: 'center',
  },
});

這時(shí)候,save,command+R后,發(fā)現(xiàn)再網(wǎng)絡(luò)請(qǐng)求的時(shí)候會(huì)先顯示小菊花轉(zhuǎn)轉(zhuǎn)轉(zhuǎn),然后加載完畢之后,顯示一個(gè)List


加載Spinner(僅適用于iOS)

這個(gè)在上面的代碼中提到了

renderLoadingView() {
    return (
      <Image source={require('./img/background.png')} style={styles.backgroundLoading}>
      <ActivityIndicatorIOS
        style={[styles.centering, {height: 80}]} //風(fēng)格
        size="large" //大小
        color="#ffffff" //顏色
       />
      </Image>
    );
  },

控制臺(tái)打印

上文的代碼里提到

rowClicked(user){
    console.log(user);
   },

使用console.log來(lái)實(shí)現(xiàn)控制臺(tái)

這時(shí)候,我們?cè)邳c(diǎn)擊某一行,會(huì)看到XCode中輸出

在Chrome中調(diào)試

使用Command+control+Z來(lái)調(diào)出調(diào)試窗口,然后選擇Debug in chrome

這時(shí)候,App會(huì)和Chrome建立一個(gè)socket連接,這樣在Chrome中就可以進(jìn)行調(diào)試了。

打開Chrome開發(fā)者工具



點(diǎn)擊某一行,就會(huì)發(fā)現(xiàn)在chrome的控制臺(tái)進(jìn)行l(wèi)og了

添加一個(gè)詳情頁(yè),并且傳值

新建一個(gè)Component來(lái)表示詳情頁(yè)

var DetailScreen = React.createClass({
  render(){
    return (
      <View style= {styles.container}>
        <Text style={styles.blackText}>{this.props.user.nickname}</Text>
        <Text style={styles.blackText}>{this.props.user.realname}</Text>
      </View>
    );
  }
});

然后,在rowClick中,跳轉(zhuǎn)到詳情頁(yè)

rowClicked(user){
    console.log(user);
    this.props.navigator.push({
      title: "詳情頁(yè)",
      component: DetailScreen,
      passProps: {user:user},
    });
  },

Tips:

  • NavigatorIOS可以通過this.props.navigator來(lái)訪問
  • 通過 this.props.navigator.push來(lái)跳轉(zhuǎn),通過passProps: {user:user}來(lái)傳遞值
  • 每個(gè)Component的類都有兩個(gè)全獨(dú)享,this.props表示參數(shù),this.state表示當(dāng)前的狀態(tài)??梢杂脕?lái)存儲(chǔ)和傳遞數(shù)據(jù)

簡(jiǎn)單提一下React Native的性能

在RN中,主要有兩個(gè)線程

  • JavaScript線程
  • 主線程(UI線程)

其中,JavaScript是React Native的JS代碼執(zhí)行線程,React Native的觸摸處理,網(wǎng)絡(luò)請(qǐng)求,視圖配置,以及app的業(yè)務(wù)邏輯都是發(fā)生在這里的。主線程是實(shí)際原生代碼繪制視圖的執(zhí)行線程。使用React Native的時(shí)候,往往會(huì)遇到JavaScript線程執(zhí)行邏輯過多,沒有辦法及時(shí)響應(yīng)UI線程,導(dǎo)致掉幀.所以,React Native的性能,較純?cè)倪€是要差一些的

后續(xù)

Reactive Native實(shí)戰(zhàn)解析(中)會(huì)繼續(xù)講解一些基礎(chǔ)控件的適用,然后也會(huì)寫一個(gè)demo的app,Reactive Native實(shí)戰(zhàn)解析(下)會(huì)寫一個(gè)相對(duì)完善點(diǎn)的應(yīng)用,作為這個(gè)入門系列的終結(jié)。

附錄,最終的index.ios.js全部代碼

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 */

import React, {
  AppRegistry,
  Component,
  StyleSheet,
  ListView,
  Text,
  View,
  Image,
  ActivityIndicatorIOS,
  Navigator,
  TouchableHighlight,
  TouchableOpacity,
  NavigatorIOS,
} from 'react-native';

var REQUEST_URL = 'https://raw.githubusercontent.com/LeoMobileDeveloper/React-Native-Files/master/person.json';

var ListScreen = React.createClass({
  getInitialState: function() {
    return {
      loaded: false,
      users: new ListView.DataSource({
        rowHasChanged: (row1, row2) => row1 !== row2,
      }),
    };
  },
  componentDidMount() {
    this.fetchData();
  },
  fetchData() {
   fetch(REQUEST_URL)
     .then((response) => response.json())
     .then((responseData) => {
       this.setState({
         users: this.state.users.cloneWithRows(responseData),
         loaded: true,
       });
     })
     .done();
  },

  render(){
   if (!this.state.loaded) {
     return this.renderLoadingView()
}
   return this.renderList()
  },

  renderLoadingView() {
    return (
      <Image source={require('./img/background.png')} style={styles.backgroundLoading}>
      <ActivityIndicatorIOS
        style={[styles.centering, {height: 80}]}
        size="large"
        color="#ffffff"
       />
      </Image>
    );
  },

  renderList(){
    return (
      <Image source={require('./img/background.png')} style={styles.backgroundImg}>
        <ListView
          dataSource={this.state.users}
          renderRow={this.renderRow}
          style={styles.fullList}
          renderSeparator={(sectionID, rowID) => <View key={`${sectionID}-${rowID}`} style={styles.separator} />}
         />
      </Image>
    );
  },
  renderRow(user){
    return (
      <TouchableHighlight
        onPress={() => this.rowClicked(user)}
        underlayColor = '#ddd'>
      <View style={styles.rightCongtainer}>
        <Text style={styles.whiteText}>{user.nickname}</Text>
        <Text style={styles.whiteText}>{user.realname}</Text>
      </View>
      </TouchableHighlight>
    );
  },
  rowClicked(user){
    console.log(user);
    this.props.navigator.push({
      title: "詳情頁(yè)",
      component: DetailScreen,
      passProps: {user:user},
    });
  },
});

var DetailScreen = React.createClass({
  render(){
    return (
      <View style= {styles.container}>
        <Text style={styles.blackText}>{this.props.user.nickname}</Text>
        <Text style={styles.blackText}>{this.props.user.realname}</Text>
      </View>
    );
  }
});

var LeoRNWeather = React.createClass({
  render: function() {
    return (
      <NavigatorIOS
        style={styles.container}
        initialRoute={{
          title: '主頁(yè)',
          component: ListScreen,
        }}
      />
    );
  }
});

const styles = StyleSheet.create({
  backgroundImg:{
    flex:1,
    width: null,
    height: null,
    flexDirection: 'row'
  },
  backgroundLoading:{
    flex:1,
    width: null,
    height: null,
    alignItems: 'center',
    justifyContent: 'center',
    flexDirection: 'row'
  },
  thumbnail: {
    width: 60,
    height: 60,
  },
  rightCongtainer:{
    flex:1,
  },
  fullList:{
    flex:1,
    paddingTop: 64,
  },
  separator: {
   height: 0.5,
   backgroundColor: 'rgba(255,255,255,0.5)',
 },
 centering: {
    alignItems: 'center',
    justifyContent: 'center',
  },
  whiteText:{
    fontSize:20,
    color:'rgb(255,255,255)',
    backgroundColor:'rgba(255,255,255,0)',
    textAlign:'left',
    marginLeft:10,
  },
  blackText:{
    fontSize:20,
    color:'rgb(0,0,0)',
    backgroundColor:'rgba(255,255,255,0)',
    textAlign:'center',
    marginLeft:10,
  },
  container: {
    flex: 1,
    backgroundColor: 'white',
    justifyContent: 'center',
  },
});

AppRegistry.registerComponent('LeoRNWeather', () => LeoRNWeather);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容