React Native基礎(chǔ)教程—實(shí)現(xiàn)微信精選列表

從本章開始我們將一起來開發(fā)一個(gè)簡單的微信精選列表應(yīng)用。主要包括兩個(gè)界面,主界面是微信精選列表,點(diǎn)擊每一條信息可以進(jìn)入到詳細(xì)信息展示的界面。通過這個(gè)應(yīng)用我們將學(xué)習(xí)如何從網(wǎng)絡(luò)獲取數(shù)據(jù)、怎樣對UI組件進(jìn)行布局及設(shè)置樣式、以及常用UI組件的使用。

可以在React NativeReact的官方網(wǎng)站查看到更多ReactNative及React的相關(guān)內(nèi)容。對應(yīng)的中文社區(qū):React NativeReact。

項(xiàng)目完整源碼

項(xiàng)目的完整源碼下載地址。

注意
一、教程以開放IOS應(yīng)用為基礎(chǔ)講解。
二、涉及到的原生代碼采用Objective-C語言完成。

微信精選

單條內(nèi)容

可以將微信精選列表看做一個(gè)tableview,那么每一條內(nèi)容就是一個(gè)cell。我們將從搭建單條內(nèi)容的界面開始講解。先看一下最終完成的樣子:

單條內(nèi)容.jpeg

這個(gè)界面主要包括文本和圖片,采用文本堆疊在圖片之上的布局方式。

開發(fā)React Native應(yīng)用主要需要JavaScript、CSS和JSX三種技術(shù)。其中JavaScript負(fù)責(zé)業(yè)務(wù)邏輯;CSS負(fù)責(zé)UI樣式和布局;JSX將UI組件封裝為標(biāo)簽語言,使我們可以通過JSX標(biāo)簽構(gòu)建UI。React Native的這種開發(fā)模式借鑒了傳統(tǒng)的web開發(fā)。這樣做主要的優(yōu)勢是可以將內(nèi)容和表現(xiàn)分離,JSX負(fù)責(zé)內(nèi)容,CSS負(fù)責(zé)表現(xiàn)。代碼邏輯更加清晰。

JSX

相信大部分同學(xué)對于JavaScript和CSS肯定不陌生。如果沒有使用React的經(jīng)驗(yàn)可能不了解JSX是什么。那么就來說一說JSX,先來看一段代碼:

<View>
  <Text>
    JSX,JavaScript語法擴(kuò)展
  </Text>
</View>

上面就是一段JSX代碼。

每一個(gè)UI閉合的標(biāo)簽都稱之為組件,<View/>組件可以認(rèn)為代表UIView<Text/>組件可以認(rèn)為代表UILabel。ReactNative就是通過這些JSX組件與原生UI一一對應(yīng)起來的。

JSX,本質(zhì)是JavaScript的語法擴(kuò)展。React在運(yùn)行期將JSX轉(zhuǎn)換為JavaScript代碼。

JSX以<起始,以/>結(jié)束的閉合標(biāo)簽。JSX級能夠解析HTML標(biāo)簽,也能夠解析React組件。規(guī)定HTML標(biāo)簽必須以小寫字母開頭表示,React組件以大寫字母開頭。

JSX,在構(gòu)建ReactNative應(yīng)用并不是必須的,也可以直接通過JavaScript創(chuàng)建UI組件。但是使用JSX能夠讓我們的代碼更加直觀清晰,并且JSX支持與JavaScript混編,這也賦予其非常大的靈活性。

Flexbox彈性盒

搭建UI界面必須解決的一個(gè)問題就是屏幕適配的問題。在IOS開發(fā)中我們使用autolayout進(jìn)行布局,ReactNative也為我們提供了它解決布局問題的技術(shù)——Flexbox彈性盒。Flexbox本身是CSS3的技術(shù)標(biāo)準(zhǔn),ReactNative因?yàn)槭褂肅SS進(jìn)行布局和樣式設(shè)置,所以也就順理成章的使用Flexbox彈性盒技術(shù)。

可以通過下面的代碼在組件中聲明樣式:

<View style = {styles.container}>
  <Text style = {styles.content}>
    JSX,JavaScript語法擴(kuò)展
  </Text>
</View>

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
});

ReactNative并沒有支持全部的CSS屬性,ReactNative支持的CSS屬性可以看這里。關(guān)于Flexbox彈性盒詳細(xì)的屬性可以查看這里。

關(guān)于Flexbox的教程推薦這兩篇文章:Flex 布局教程:語法篇、Flex 布局教程:實(shí)例篇。

編寫單條內(nèi)容界面

了解了JSX與Flexbox之后,我們就可以正式開始編寫單條內(nèi)容的界面。下面是完整的代碼:

'use strict';

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

class ReactNativeLearn extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Image 
          style={{
            width: 300,
            height: 200,
          }}
          resizeMode={"contain"}
          source={{uri:'http://facebook.github.io/react/img/logo_og.png'}}
        />
        <Text
          style={{
            color: 'black',
            fontSize: 16,
            fontWeight: 'normal',
            fontFamily: 'Helvetica Neue',
          }}>
          新聞內(nèi)容
        </Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
});

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

啟動(dòng)Xcode,運(yùn)行工程:

單條內(nèi)容.jpeg

可以看到我們已經(jīng)實(shí)現(xiàn)簡單的上下布局的圖片和文本。

現(xiàn)在我們來分析一下上面的代碼。首先是'use strict',這是JavaScript的嚴(yán)格模式,在嚴(yán)格模式下一些可能會(huì)帶來安全隱患的JavaScript語言特性將被禁用(ES6標(biāo)準(zhǔn)Module默認(rèn)采用嚴(yán)格模式)。然后是import React, { Component } from 'react'這行代碼的意思是將React庫的Component組件導(dǎo)入到當(dāng)前作用域中。這是采用ES6標(biāo)準(zhǔn)的寫法。之后的代碼就是我們創(chuàng)建界面的主要代碼。我們可以看到render()函數(shù),render是渲染的意思,我們在render函數(shù)中創(chuàng)建我們的UI組件渲染到屏幕。

關(guān)于組件的詳細(xì)信息可以在ReactNative官網(wǎng)找到。AppRegistry.registerComponent('ReactNativeLearn', () => ReactNativeLearn),表示將ReactNativeLearn組件作為整個(gè)ReactNative應(yīng)用的入口。ReactNativeLearn的名稱與AppDelegate中的moduleName對應(yīng):

RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
                                                      moduleName:@"ReactNativeLearn"
                                               initialProperties:nil
                                                   launchOptions:launchOptions];

現(xiàn)在我們可以通過修改組件的style屬性和組件的包裹方式,實(shí)現(xiàn)我們想要的最終效果。代碼如下:

class ReactNativeLearn extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Image 
          style={{
            width:  Dimensions.get('window').width - 40,
            height: 300,
            marginLeft: 20,
            marginRight: 20,
            marginTop: 10,
            marginBottom: 10,
            borderColor: "rgb(0,0,0)",
            borderWidth: 2,
            borderRadius: 8,
            justifyContent: 'flex-end',
          }}
          resizeMode={"stretch"}
          source={{uri:'http://facebook.github.io/react/img/logo_og.png'}}
        >
          <View
            style={{
              flex: 0,
              justifyContent: 'flex-start',
              alignItems: 'stretch',
              backgroundColor: "rgba(0,0,0,0.4)",
            }}>
            <Text
              style={{
                color: "rgb(255,255,255)",
                fontSize: 20,
                fontWeight: 'normal',
                fontFamily: 'Helvetica Neue',
                marginLeft: 10,
                marginRight: 10,
                marginTop: 10,
                marginBottom: 10,
              }}
              numberOfLines={3}
            >
              A React component for displaying different types of images, including network images, static resources, temporary local images, and images from local disk, such as the camera roll.
            </Text>
          </View>
        </Image>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    backgroundColor: '#F5FCFF',
  },
});

最終效果:

最終效果.jpeg

我們不應(yīng)該將全部代碼都寫在index.ios.js文件,而是應(yīng)該分成不同的模塊,方便管理。我們新建一個(gè)名為newsCell的js文件,將代碼復(fù)制到該文件當(dāng)中。最后將newsCell聲明為一個(gè)可以被其他模塊引用的組件:

export default NewsCell;

微信精選列表

完成單條內(nèi)容的UI搭建,現(xiàn)在我們就來實(shí)現(xiàn)列表的界面。在React Native應(yīng)用中列表通常使用ListView組件。ListView的功能類似于UITableView。先實(shí)現(xiàn)最基本的ListView,代碼如下:

class ReactNativeLearn extends Component {
  constructor(props) {
    super(props);
    this.state = {
      dataSource: new ListView.DataSource({
        rowHasChanged: (r1, r2) => r1 !== r2
      })
    };
    this.renderRow = this.renderRow.bind(this);
  }
  
  componentDidMount() {
    this.fetchNewsData();
  }
  
  //  設(shè)置數(shù)據(jù)
  fetchNewsData() {
    this.setState({
      dataSource: this.state.dataSource.cloneWithRows([{'content':'ReactNativeLearn ListView'},
                                                        {'content':'ReactNativeLearn ListView'},
                                                        {'content':'ReactNativeLearn ListView'},
                                                        {'content':'ReactNativeLearn ListView'},
                                                        {'content':'ReactNativeLearn ListView'},
                                                        {'content':'ReactNativeLearn ListView'},
                                                        {'content':'ReactNativeLearn ListView'},
                                                        {'content':'ReactNativeLearn ListView'},
                                                        {'content':'ReactNativeLearn ListView'},
                                                        {'content':'ReactNativeLearn ListView'}])
    });
  }
  
  //  渲染cell
  renderRow(rowData) {
    return (
      <View
       style={{
         height: 80,
         justifyContent: 'flex-start',
         alignItems: 'stretch',
         backgroundColor: "rgba(74,144,226,1)",
       }}>
        <Text
          style={{
            color: 'black',
            fontSize: 28,
            fontWeight: 'normal',
            fontFamily: 'Helvetica Neue',
          }}>
          {rowData.content}
        </Text>
      </View>
    );
  }

  //  渲染cell的分割
  renderSeparator(
    sectionID,
    rowID
  ) {
      return (
        <View key = {'cell_'+ sectionID + '_' + rowID} style = {styles.rowSeparator}/>
      );
  }
  
  render() {
    return (
      <View style={styles.container}>
        <ListView
         style = {styles.listContainer}
         dataSource = {this.state.dataSource}
         renderRow = {this.renderRow}
         renderSeparator = {this.renderSeparator}
         enableEmptySections={true}
        />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    backgroundColor: '#F5FCFF',
  },
  listContainer: {
    flex:1,
    marginTop: 64,
    backgroundColor: 'rgb(237, 240, 235)',
  },
  rowSeparator: {
    backgroundColor: 'rgb(223, 218, 223)',
    height: 1,
  },
});

最終效果:

ListView.jpeg

現(xiàn)在我們已經(jīng)完成一個(gè)最簡單的ListView??梢钥吹轿覀?yōu)長istView組件設(shè)置了三個(gè)屬性:dataSource,renderRow和renderSeparator。dataSource是ListView的數(shù)據(jù)源類似于UITableView的數(shù)據(jù)源方法,這里我們手動(dòng)設(shè)置了一個(gè)數(shù)組;renderRow接收一個(gè)渲染cell的函數(shù)回調(diào),類似于UITableView的cellForRowAtIndexPath??梢栽谶@個(gè)函數(shù)里我們可以創(chuàng)建cell的UI界面,即我們之前創(chuàng)建的單條內(nèi)容界面。還有一個(gè)renderSeparator,可以用于創(chuàng)建分割cell的UI。

目前為止ListView的每一個(gè)cell的數(shù)據(jù)都是我們手動(dòng)設(shè)置的。實(shí)際開發(fā)中,這些數(shù)據(jù)往往來自于服務(wù)器。下面我們來實(shí)現(xiàn)網(wǎng)絡(luò)請求,代碼如下:

fetchNewsData() {
    fetch(newsULR,{
        headers: {
            "apikey": "f589f2834aeab120eef2e750e4fb1dfb"
          }
        }).then((response) => response.json())
                .catch((error) => {
            console.error('error request!');
                })
                .then((responseData) => {
                    this.setState({
                        dataSource: this.state.dataSource.cloneWithRows(responseData.newslist)
                    });
                })
                .done();
}

React Native使用Fetch進(jìn)行網(wǎng)絡(luò)請求。Fetch是新的異步網(wǎng)絡(luò)請求標(biāo)準(zhǔn),相較于AJAX提供更強(qiáng)大高效的網(wǎng)絡(luò)請求API。關(guān)于ReactNative的網(wǎng)絡(luò)內(nèi)容可以看這里。

我們已經(jīng)實(shí)現(xiàn)ListView的基本功能,現(xiàn)在需要將之前單條新聞的界面newsCell作為組件,添加到ListView中,實(shí)現(xiàn)完整的列表。引入newsCell的方式與引入ReactNative原生組件的方式相同,都是通過import進(jìn)行導(dǎo)入。

import NewsCell from './News/newsCell';

我們可以通過下面的方式向NewsCell傳遞數(shù)據(jù)或者回調(diào)函數(shù):

renderRow(rowData) {
    return (
      <NewsCell newsData={rowData}/>
    );
  }

然后在NewsCell組件可以通過如下的方式獲得數(shù)據(jù):

{this.props.newsData.title}

到此我們完成完整的微信精選列表。最終列表界面:

微信精選.jpeg

像newsCell一樣,我們把列表也封裝為一個(gè)單獨(dú)的組件,命名為newsMain,然后直接在index.ios.js文件導(dǎo)入即可。

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

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

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