ReactNative實(shí)例 - BBC新聞客戶端

歡迎Follow我的GitHub, 關(guān)注我的簡(jiǎn)書.

又是一篇關(guān)于React Native的實(shí)例, BBC新聞客戶端. 通過訪問BBC的公開網(wǎng)絡(luò)接口, 獲取新聞內(nèi)容, 也可以根據(jù)類型顯示. 在編寫代碼中, 學(xué)習(xí)RN的知識(shí), 源碼是使用ES6的規(guī)范編寫, 符合Facebook的RN代碼最新規(guī)范.

BBC

主要技術(shù)

  1. 訪問網(wǎng)絡(luò)請(qǐng)求, 過濾內(nèi)容, 獲取數(shù)據(jù).
  2. 顯示多媒體頁(yè)面, 圖片, 視頻, 鏈接等.

本文源碼的GitHub下載地址

關(guān)于React Native項(xiàng)目的啟動(dòng), 參考1, 參考2.


配置項(xiàng)目

初始化項(xiàng)目WclBBCNews, 修改package.json, 添加依賴庫(kù).
Html解析庫(kù): htmlparser, 時(shí)間處理庫(kù): moment, 線性梯度庫(kù): react-native-linear-gradient, 視頻庫(kù): react-native-video.

  "dependencies": {
    "react": "^0.14.8",
    "react-native": "^0.24.1",

    "htmlparser": "^1.7.7",
    "moment": "^2.11.1",

    "react-native-linear-gradient": "^1.4.0",
    "react-native-video": "^0.6.1"
  }

目前, React Native禁止使用-初始化項(xiàng)目名稱, 最好使用駝峰式.

初始化主模塊index.ios.js, 使用NavigatorIOS導(dǎo)航頁(yè)面, 首頁(yè)組件Feed模塊.

render() {
  return (
    <NavigatorIOS
      style={{flex:1}}
      translucent={false}
      barTintColor={'#BB1919'}
      titleTextColor={'white'}
      tintColor={'white'}
      initialRoute={{
        component: Feed,
        title: "Feed",
        passProps: {}
      }}/>
  );
}

渲染使用動(dòng)態(tài)加載組件, StatusBar使用淺色樣式.

_renderScene(route, navigator) {
  var Component = route.component;
  StatusBar.setBarStyle('light-content');
  return (
    <Component
      {...route.props}
      changeNavBarHeight={this.changeNavBarHeight}
      navigator={navigator}
      route={route}/>
  );
}

StatusBar樣式只有兩種, 默認(rèn)default, 字是黑色; 可選light-content, 字是白色.


新聞列表

Feed頁(yè)面, 主要以列表形式, 即ListView標(biāo)簽, 顯示新聞. 未加載完成時(shí), 調(diào)用頁(yè)面加載提示符ActivityIndicatorIOS, 顯示動(dòng)畫.

render() {
  // 未加載完成時(shí), 調(diào)用加載頁(yè)面
  if (!this.state.loaded) {
    return this._renderLoading();
  }
  // ...
}

_renderLoading() {
  return (
    <View style={{flexDirection: 'row', justifyContent: 'center', flex: 1}}>
      <ActivityIndicatorIOS
        animating={this.state.isAnimating}
        style={{height: 80}}
        size="large"/>
    </View>
  );
}

加載完成后, 調(diào)用ListView顯示頁(yè)面, renderRow渲染每一行, refreshControl加載頁(yè)面的過場(chǎng).

return (
  <ListView
    testID={"Feed Screen"}
    dataSource={this.state.dataSource}
    renderRow={this._renderStories.bind(this)}
    style={{backgroundColor: '#eee'}}
    contentInset={{top:0, left:0, bottom: 64, right: 0}}
    scrollEventThrottle={200}
    {...this.props}
    refreshControl={
      <RefreshControl
        refreshing={this.state.isRefreshing}
        onRefresh={this._fetchData.bind(this)}
        tintColor='#BB1919'
        title="Loading..."
        progressBackgroundColor="#FFFF00"
      />}
    />
);

每一行使用Story模塊渲染.

_renderStories(story) {
  return (
    <Story story={story} navigator={this.props.navigator}/>
  );
}

啟動(dòng)頁(yè)面的時(shí)候, 使用fetch方法加載數(shù)據(jù).

componentDidMount() {
  this._fetchData();
}

通過訪問BBC的網(wǎng)絡(luò)請(qǐng)求, 異步獲取數(shù)據(jù). 使用_filterNews過濾需要的數(shù)據(jù), 把數(shù)據(jù)設(shè)置入每一行, 修改狀態(tài)setState, 重新渲染頁(yè)面.

_fetchData() {
  this.setState({isRefreshing: true});
  fetch(`http://trevor-producer-cdn.api.bbci.co.uk/content${this.props.collection || '/cps/news/world'}`)
    .then((response) => response.json())
    .then((responseData) => this._filterNews(responseData.relations))
    .then((newItems) => {
      this.setState({
        dataSource: this.state.dataSource.cloneWithRows(newItems),
        loaded: true,
        isRefreshing: false,
        isAnimating: false
      })
    }).done();
}

列表項(xiàng)提供分類顯示功能, 點(diǎn)擊類別, 可以重新加載所選類型的新聞, 把Feed頁(yè)面再次添加至導(dǎo)航navigator, 即頁(yè)面棧.

_pressedCollection(collection) {
  this.props.navigator.push({
    component: Feed,
    title: collection.content.name,
    passProps: {
      collection: collection.content.id,
      navigator: this.props.navigator
    }
  });
}

點(diǎn)擊列表項(xiàng), 跳轉(zhuǎn)至詳情頁(yè)面StoryDetail.

_pressedStory(story) {
  this.props.navigator.push({
    component: StoryDetail,
    title: this._truncateTitle(story.content.name),
    passProps: {story, navigator: this.props.navigator}
  });
}

效果

News List

新聞詳情

主要是解析HTML頁(yè)面, 加載并顯示, 除了文字之外, 會(huì)顯示圖片\視頻\超鏈接等樣式. 渲染使用動(dòng)態(tài)元素, 狀態(tài)stateelements屬性.

render() {
  if (this.state.loading) {
    return (
      <Text>Loading</Text>
    );
  }
  return this.state.elements;
}

頁(yè)面啟動(dòng)時(shí), 加載數(shù)據(jù). 在_fetchStoryData方法中, 進(jìn)行處理, 使用回調(diào)返回?cái)?shù)據(jù). 主要內(nèi)容body與多媒體media通過滾動(dòng)視圖ScrollView的形式顯示出來.

componentDidMount() {
  this._fetchStoryData(
    // media表示視頻或圖片.
    (result, media) => {
      const rootElement = result.find(item => {
        return item.name === 'body';
      });

      XMLToReactMap.createReactElementsWithXMLRoot(rootElement, media)
        .then(array => {
          var scroll = React.createElement(ScrollView, {
            contentInset: {top: 0, left: 0, bottom: 64, right: 0},
            style: {flex: 1, flexDirection: 'column', backgroundColor: 'white'},
            accessibilityLabel: "Story Detail"
          }, array);

          this.setState({loading: false, elements: scroll});
        });
    }
  );
}

處理數(shù)據(jù), 使用fetch方法, 分離視頻與圖片, 還有頁(yè)面, 通過回調(diào)cb(callback)的處理返回?cái)?shù)據(jù).

_fetchStoryData(cb) {
  // 提取數(shù)據(jù), 轉(zhuǎn)換JSON格式, 圖片過濾, 視頻過濾, 組合relations, 解析.
  fetch(`http://trevor-producer-cdn.api.bbci.co.uk/content${this.props.story.content.id}`)
    .then((response) => response.json())
    .then((responseData) => {
      const images = responseData.relations.filter(item => {
        return item.primaryType === 'bbc.mobile.news.image';
      });
      const videos = responseData.relations.filter(item => {
        return item.primaryType === 'bbc.mobile.news.video';
      });
      const relations = {images, videos};
      this._parseXMLBody(responseData.body, (result) => {
        cb(result, relations);
      });
    }).done();
}

使用Tautologistics解析dom數(shù)據(jù)與body數(shù)據(jù). DOM, 即Document Object Model, 文件對(duì)象模型.

_parseXMLBody(body, cb) {
  var handler = new Tautologistics.NodeHtmlParser.DefaultHandler(
    function (error, dom) {
      cb(dom)
    }, {enforceEmptyTags: false, ignoreWhitespace: true});

  var parser = new Tautologistics.NodeHtmlParser.Parser(handler);
  parser.parseComplete(body);
}

XML解析類XMLToReactMap比較復(fù)雜, 不做過多介紹, 參考源碼.

News Detail

通過編寫新聞?lì)悜?yīng)用, 學(xué)習(xí)使用網(wǎng)絡(luò)請(qǐng)求和解析HTML格式的文本. 多編碼多思考, 不斷學(xué)習(xí), React Native是非常有意思的開發(fā)語(yǔ)言.

感謝我的朋友Joel Trew的實(shí)例, 本文改動(dòng)一些源碼.

OK, that's all! Enjoy it!

最后編輯于
?著作權(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)容