歡迎你們收看react-native-android系列教程,跟着本系列教程學習,能夠熟練掌握react-native-android的開發,你值得擁有:javascript
http://my.oschina.net/MrHou/blog?catalog=3590216&temp=1466310573630java
本章,我們一塊兒動個手,來模仿一下手機百度的新聞流。學習同樣東西,最好在有了必定基礎以後,最好照着已有的一些產品,簡單的實現一下。react
1. 手機百度的feed流樣式觀察android
動手以前,咱們先來看看手機百度新聞feed流,都由那些元素組成吧,如圖1.1:git
圖1.1github
咱們看到,總體由上方的搜索框+工具條組成,咱們暫且叫上方部分爲搜索區吧,下方則是由新聞內容區組合而成。下方是由幾個工具組成的底部工具欄。在滑動下方新聞feed流過程當中,上方的搜索區位置不變。下方的工具欄位置也不變。web
2. 設計目錄結構及代碼書寫react-native
好了,咱們大體的結構分析完了。要coding?別捉急,咱們在coding以前仍是要好好設計目錄結構的。根據上面分析的信息,咱們決定先將整個activity拆分爲三大塊。app
1. 搜索區函數
2. 中間內容區
3. 底部工具欄區
2.1 拆分模塊
因此,我認爲,首先要將三個區塊拆爲三個模塊。咱們須要一個static文件夾,而後咱們在static文件夾下,爲每一個區域生成一個模塊(如圖2.1.0):
圖2.1.0
接下來的樣式文件,也是各自放於本身的模塊中去維護,包括圖片,經過這種方式,組件能夠很好的成爲一個總體。
好比,咱們的searchArea組件,咱們在searchArea中建立一個index.js存放searchArea組件的類與jsx元素。而咱們將它的樣式放在同目錄下的style.js裏面,如圖2.1.1所示:
圖2.1.1
好了,組件都創建好了。咱們開始書寫主入口代碼吧。
2.2 reactjs基礎粗講(學過react-web的同窗,可跳過)
初學react的同窗可能會迷惑,以前不都是寫在一個文件裏嗎,爲何能夠拆分紅好幾個文件,咱們在這裏簡單的先講解一下react的基礎,以後我會在個人reac-native-android系列教學中,詳細講解這一基礎知識。
reactjs中,組件概念是比較強的,舉一個簡單的例子,咱們寫一個簡單的、分多個快組成的應用的時候,可能會這樣寫:
class App extends Component { constructor(props) { super(props); } render() { return <View> <View><Text>這是這個應用的第一塊</Text></View> <View><Text>這是這個應用的第二塊</Text></View> </View> } } AppRegistry.registerComponent('hellowReact', () => hellowReact);
咱們看到,這個應用由兩個快組成。兩個塊兒的邏輯若是很是複雜的話,放在一個類裏面維護,是個很是痛苦的事情。
因此react主張,將本身的應用化爲多個組建。這樣,複用行獲得了提升,維護成本也大大下降了。因此,按照標準作法,咱們能夠將上述兩個塊兒,拆解成兩個組件:
class App extends Component { constructor(props) { super(props); } render() { return <View> <BlockOne /> <BlockTwo /> <View><Text>這是這個應用的第二塊</Text></View> </View> } } class BlockOne extends Component { constructor(props) { super(props); } render() { return <View><Text>這是這個應用的第一塊</Text></View>; } } class BlockTwo extends Component { constructor(props) { super(props); } render() { return <View><Text>這是這個應用的第二塊</Text></View>; } } AppRegistry.registerComponent('hellowReact', () => hellowReact);
這樣達到了代碼封裝的效果,react支持將兩個組建做爲基礎組建使用,就像上面代碼中的BlockOne與BlockTwo,都是能夠當作普通組建被插入到組建中去的。
接下來,咱們將BlockOne與BlockTwo拆出去,變成兩個文件。這樣,代碼之間就互相隔離了,代碼以下:
/** * @file index.android.js * @author 侯禹 * @desc 應用的主入口 */ // 將兩個組件以模塊的形式導入 let BlockOne = require('blockOne.js'); let BlockTwo = require('blockTwo.js'); class App extends Component { constructor(props) { super(props); } render() { // 直接使用寫好的兩個組件,以標籤的形式使用便可 return <View> <BlockOne /> <BlockTwo /> <View><Text>這是這個應用的第二塊</Text></View> </View> } } AppRegistry.registerComponent('hellowReact', () => hellowReact);
/** * @file blockOne.js * @author 侯禹 * @desc 第一起的組件 */ class BlockOne extends Component { constructor(props) { super(props); } render() { return <View><Text>這是這個應用的第一塊</Text></View>; } } // 將組件導出 module.exports = BlockOne;
/** * @file blockTwo.js * @author 侯禹 * @desc 第二塊兒的組件 */ class BlockTwo extends Component { constructor(props) { super(props); } render() { return <View><Text>這是這個應用的第二塊</Text></View>; } } // 將組件導出 module.exports = BlockTwo;
上面這種形式的代碼,在reactjs中是很是常見的。因此各位看官,也儘可能要使用這種組件化的方法去寫react。接下來,咱們的樣例代碼,也會使用這種組件化的方法去寫。
2.3 入口代碼
看過我以前教程的同窗們可能還記得,react-native工程的入口js文件,安卓版本的是:'index.android.js'。
咱們就先將主入口文件搭建成一個空的架子。
import React, { Component } from 'react'; import { AppRegistry, StyleSheet, Text, View, ListView, Image, NativeModules } from 'react-native'; class hellowReact extends Component { constructor(props) { super(props); this.state = {}; } render() { return ( <View style={styles.container}> </View> ); } } const styles = StyleSheet.create({ container: { flex: 1, flexDirection: 'column', backgroundColor: '#fff', }, }); AppRegistry.registerComponent('hellowReact', () => hellowReact);
運行上述代碼,咱們會看到一片空白,這是由於咱們只寫了一個view,其餘的什麼都沒有寫(如圖2.3.1):
圖2.1.1
2.4 搜索框部分組件(searchArea)
咱們先寫搜索框部分的jsx和樣式,如圖2.4.1。
圖2.4.1
咱們將兩個素材從手機百度的包中拽出來(將手機百度apk的後綴名從.apk改爲.zip,而後解壓,在其中找找就能找到,或者在本文後面的示例代碼中也能找到)。
而後開始書寫index.js
咱們首先要將必要的基礎模塊和兩個圖片引入:
'use strict' // 必要的基礎模塊 import React, {Component} from 'react'; import { View, Text, Image } from 'react-native'; // 所須要的兩張資源圖片(均從百度的apk中獲取) let homeIcon = require('./icn_feed_go_home.png'); let refreshIcon = require('./icn_feed_refresh.png');
而後咱們開始書寫本身的react組件,SearchArea,咱們先把組件的結果大體寫出來,咱們會有一個外圍包裹的View,而後上面會有一個搜索框,下面有一個工具欄的包裹用的view:
class SearchArea extends Component { constructor(props) { super(props); this.state = { }; } render() { return ( <View> <View></View> <View></View> </View> ); } } module.exports = SearchArea;
接下來,咱們爲其添加樣式,仍是老辦法,咱們將之前與組建寫在一塊兒的style,放在style.js中,再將其導出,並在index.js中,將其引入:
/* * @file searchArea/style.js * @author houyu * @desc 模擬手百的搜索框區域 */ 'use strict' import React, {Component} from 'react'; import { StyleSheet, Image } from 'react-native'; // 建立樣式 const headminiStyle = StyleSheet.create({ topBar: { height: 90, backgroundColor: '#f8f8f8', borderBottomWidth: 1, borderColor: '#f2f2f2', }, }); // 將樣式導出 module.exports.headminiStyle = headminiStyle;
咱們在樣式中增長一個搜索區域的大體樣式,方便咱們本身先查看一下(效果如圖2.4.2):
// 引入本身模塊的樣式 import headStyle from './style.js'; class SearchArea extends Component { constructor(props) { super(props); // 將樣式放在state中 this.state = { headStyle: headStyle.headminiStyle }; } render() { // 用state中存下的樣式去渲染 return ( <View style={this.state.headStyle.topBar}> <View></View> <View></View> </View> ); } } module.exports = SearchArea;
圖2.4.2
接着咱們細化一下,將搜索框部分。寫出點樣式來:
const headminiStyle = StyleSheet.create({ topBar: { height: 90, backgroundColor: '#f8f8f8', borderBottomWidth: 1, borderColor: '#f2f2f2', }, searchbox: { flex: 5, marginTop: 7, marginLeft: 9, marginRight: 9, borderColor: '#d0d2d5', borderWidth: 1, }, toolBar: { flex: 4, marginLeft: 9, marginRight: 9, }, });
這裏,咱們就用到了上節課說到的flex,搜索框與工具欄的高度比例大概是5:4,因而咱們寫了兩個樣式的flex爲5與4,再將搜索框的邊框與背景勾勒出來,就獲得了一個大概的搜索框部分(如圖2.4.3):
圖2.4.3
緊接着,咱們須要在下面的工具欄裏面加上左邊的首頁按鈕(如圖2.4.1),和右邊的刷新按鈕(如圖2.4.2),左邊是一個圖片+一個文字塊(首頁),右邊是一個刷新圖片
圖2.4.1
圖2.4.2
class SearchArea extends Component { constructor(props) { super(props); this.state = { headStyle: headStyle.headminiStyle }; } render() { return ( <View style={this.state.headStyle.topBar}> <View style={this.state.headStyle.searchbox}></View> <View style={this.state.headStyle.toolBar}> <View style={this.state.headStyle.toIndex}> <Image source={homeIcon} style={this.state.headStyle.topBarHomeIcon} /> <Text>首頁</Text> </View> <View style={this.state.headStyle.refresh}> <Image source={refreshIcon} style={this.state.headStyle.topBarRefreshIcon} /> </View> </View> </View> ); } }
咱們將元素都寫好,加上樣式:
const headminiStyle = StyleSheet.create({ topBar: { height: 90, backgroundColor: '#f8f8f8', borderBottomWidth: 1, borderColor: '#f2f2f2', }, searchbox: { // flex的高度佔比爲搜索框5,工具欄4--5:4 flex: 5, marginTop: 7, marginLeft: 9, marginRight: 9, borderColor: '#d0d2d5', borderWidth: 1, }, toolBar: { flex: 4, // 讓首頁按鈕與刷新按鈕,橫向並排 flexDirection: 'row', // 讓首頁按鈕與刷新按鈕,上下處於居中對其 alignItems: 'center', // 讓首頁按鈕與刷新按鈕左右 justifyContent: 'space-between', marginLeft: 9, marginRight: 9, }, toIndex: { // 讓首頁圖片與首頁文字並排成一行 flexDirection: 'row', // 讓首頁圖標,與首頁文字上下居中 alignItems: 'center', }, topBarHomeIcon: { // 背景圖片拉伸 resizeMode: Image.resizeMode.contain, height: 27, width: 27, marginRight: 5, }, topBarRefreshIcon: { // 背景圖片拉伸 resizeMode: Image.resizeMode.contain, height: 45, width: 45, }, });
經過上面的帶啊,咱們又重溫了一些上節的知識點,元素默認是垂直排列的,但是咱們須要元素橫向排列的話,就得用到flexDirection,設置爲row。而且咱們須要,"首頁按鈕"與"刷新按鈕",並排顯示,且左右分開(如圖2.4.3):
圖2.4.3
因而咱們用到了 flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between',這三個屬性。因而,左邊的首頁按鈕和右邊的刷新按鈕橫排顯示,且左右靠邊了,垂直方向上,兩側的按鈕也居中對其了。
接下來,咱們看一下上部分的總體(如圖2.4.4):
圖2.4.4
接下來,咱們先把內容區與底部的工具欄的大體形狀給作出來。
2.5 內容區組建與底部欄組建(content與toolBar)
咱們首先將三個組建塊兒都引入進來:
// 三個組件塊兒一一引入 const SearchArea = require('./static/searchArea'); const Content = require('./static/content'); const ToolBar = require('./static/toolBar'); class hellowReact extends Component { constructor(props) { super(props); this.state = {}; } render() { // 三個組建塊兒均渲染到主要容器上 return ( <View style={styles.container}> <SearchArea /> <Content /> <ToolBar /> </View> ); } } const styles = StyleSheet.create({ container: { flex: 1, flexDirection: 'column', backgroundColor: '#fff', }, }); AppRegistry.registerComponent('hellowReact', () => hellowReact);
咱們先把中間內容區與底部工具欄的比例按照設計圖算出,獲得大概這樣的比例:
頂部搜索區高度 : 中間內容區高度 : 底部工具欄高度 = 9 : 41 : 5
因而咱們將三塊兒區域的fex分別寫爲:9/41/5。
咱們把底部工具欄簡寫一下,目前用圖片代替。
'use strict' import React, {Component} from 'react'; import { View, Text, Image } from 'react-native'; // 引入底部工具欄的樣式 import {ToolBarStyle} from './style.js'; // 底部工具欄暫時不作,使用一張大的圖片代替 let bottomBanner = require('./bottom_banner.png'); class ToolBar extends Component { constructor(props) { super(props); } render() { return ( <View style={ToolBarStyle.bottomBar}> <Image source={bottomBanner} style={ToolBarStyle.bottomBanner} /> </View> ); } } // 導出組件 module.exports = ToolBar;
而後咱們寫一下沒有內容的中間區域,樣式也很簡單,就是一個白色的塊兒:
'use strict' import React, {Component} from 'react'; import { View, Text, Image, ListView } from 'react-native'; import {contentStyle} from './style.js'; class Content extends Component { constructor(props) { super(props); this.state = {}; } render() { return ( <View style={contentStyle.content}> </View> ); } } module.exports = Content;
'use strict' import React, {Component} from 'react'; import { StyleSheet, Image, } from 'react-native'; const contentStyle = StyleSheet.create({ content: { flex: 41, backgroundColor: '#fff', }, }); module.exports.contentStyle = contentStyle;
效果如圖2.5.1:
圖2.5.1
請注意下面的工具欄用的是圖片哦。
2.6 新聞列表
緊接着,由於咱們的內容區是新聞列表,因此咱們須要一個列表組件。咱們還須要去寫每個item的樣式。
react-native中有一個原生組建,叫作listView,顧名思義,這就是個列表組件,下面咱們就將會用這個列表組建完成咱們的新聞列表。
2.6.1 listView的使用方法
listView須要兩個基本的屬性一個是dataSoutce,接受列表的數據,另外一個是renderRow,指定對於列表中的每一項的渲染方法。
render() { return ( <View style={contentStyle.content}> <ListView dataSource={this.state.dataSource} renderRow={this.renderOneRow} /> </View> ); }
datasource中傳入的數據,須要是DataSource型。因此,咱們先創建一個假數據(數據的話,看一下手白大體一個條目上都有哪些信息,抄一下便可):
constructor(props) { super(props); var list = [ { title: '英國首相卡梅倫辭職"脫歐派"靈魂人物或接任', from: '搜狐新聞', time: '3分鐘前', tag: '視頻', images: ['http://static.oschina.net/uploads/user/588/1177792_100.jpg?t=1465096324000'] }, { title: '2.英國首相卡梅倫辭職"脫歐派"靈魂人物或接任', from: '搜狐新聞', time: '3分鐘前', tag: '視頻', images: ['http://static.oschina.net/uploads/user/588/1177792_100.jpg?t=1465096324000'] }, ]; var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}); this.state = { dataSource: ds.cloneWithRows(list) }; }
咱們還須要建造一個簡單的、渲染每個item的渲染方法--renderOneRow:
renderOneRow(rowData) { return <View><Text>一筒最可愛</Text></View>; }
請注意,這裏的rowData就是listView在遍歷咱們傳入的dataSource的時候,給咱們的渲染函數傳入遍歷的當前項數據。
咱們能夠看到粗略的展現了一個list如圖2.6.1
圖2.6.1
由於咱們的假數據寫死了兩個,因此list中出現了兩項。
咱們將這兩項再變換一下,寫成手機百度的樣式,咱們先來看看手機百度中,每一項大概的層級,應該是左右結構,右邊是圖片區域,左側是文字區域。因此,話很少說,先來兩個區域再說(如圖2.6.2):
圖2.6.2
renderOneRow(rowData) { return <View style={contentStyle.itemContainer}> <View style={contentStyle.itemContent}> <View style={contentStyle.textArea}> </View> <View style={contentStyle.imgArea}> <Image source={{uri: rowData.images[0]}} style={contentStyle.rightImg} /> </View> </View> </View>; }
代碼如上,右側使用一個view包裹的圖片,左側使用一個包裹的view(如圖2.6.3所示):
圖2.6.3
const contentStyle = StyleSheet.create({ content: { flex: 41, backgroundColor: '#fff', }, // 每一項的容器的樣式,左右留白10dp,高度爲100dp itemContainer: { height: 100, paddingHorizontal: 10, }, // 每一項的內部包裹 itemContent: { height: 99, // 指定橫向排列 flexDirection: 'row', // 內部元素上線居中 alignItems: 'center', // 底部一個灰邊 borderBottomWidth: 1, borderBottomColor: '#eee', }, textArea: { // 由於不選用成比例,使用flex:1,左側的文字區域填充滿除了圖片剩下的那部分區域 flex: 1, height: 70, paddingRight: 12, }, imgArea: { height: 85, width: 110, }, rightImg: { height: 85, width: 110, backgroundColor: '#000', // 指定內容圖片填充方式 resizeMode: Image.resizeMode.cover, }, });
這樣咱們就完成了一個基本的item,這個item是左右結構的,左側的文字區域咱們再進一步細化:
左側文字區域,能夠看出是上下結構的(如圖2.6.4),高度比例大概是4:1
圖2.6.4
因而,咱們填充了兩個塊兒,而且左側內容區域的flexDirection就默認爲column就行了。
renderOneRow(rowData) { return <View style={contentStyle.itemContainer}> <View style={contentStyle.itemContent}> <View style={contentStyle.textArea}> <Text style={contentStyle.textContent}> </Text> <View style={contentStyle.tagArea}> </View> </View> <View style={contentStyle.imgArea}> <Image source={{uri: rowData.images[0]}} style={contentStyle.rightImg} /> </View> </View> </View>; }
tagArea: { flex: 1, backgroundColor: '#f00', flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', }, textContent: { flex: 4, backgroundColor: '#0f0', fontSize: 16, fontWeight: 'bold', color: '#000', },
效果如圖2.6.5所示:
圖2.6.5
good!咱們完成了一個上線結構,接着,咱們將新聞每一條的標題,填充到上面的綠色框中去(如圖2.6.6所示),獲取數據,直接從rowData中拿就行,rowData指代的就是上面我們寫的假數據中的每一條:
renderOneRow(rowData) { return <View style={contentStyle.itemContainer}> <View style={contentStyle.itemContent}> <View style={contentStyle.textArea}> <Text style={contentStyle.textContent}> {rowData.title} </Text> <View style={contentStyle.tagArea}> </View> </View> <View style={contentStyle.imgArea}> <Image source={{uri: rowData.images[0]}} style={contentStyle.rightImg} /> </View> </View> </View>; }
圖2.6.6
接下來的下方tag要注意,是由三個元素組成的左左右,怎麼辦(如圖2.6.7),flex佈局中,並無講到,如何左左右,只知道如何居中,如何將元素打散到兩側............等等,其實左左右這種結構,不正是左右結構嗎?左邊是一個由兩個元素組成的view,右邊是一個由一個元素組成的view,這樣一想,咱們的思路瞬間清晰了許多,如圖2.6.8所示
圖2.6.7
圖2.6.8
左側的紅色區域能夠放入兩個tag"視頻"和"2分鐘前"。如法炮製,咱們寫兩個小的text,便可完成,如圖2.6.9所示:
renderOneRow(rowData) { return <View style={contentStyle.itemContainer}> <View style={contentStyle.itemContent}> <View style={contentStyle.textArea}> <Text style={contentStyle.textContent}> {rowData.title} </Text> <View style={contentStyle.tagArea}> <View style={contentStyle.tags}> <View style={contentStyle.tagContainer}> <Text style={contentStyle.tagEntity}> {rowData.tag} </Text> </View> <Text style={contentStyle.srcNet}> {rowData.from} </Text> </View> <View style={contentStyle.closeTag}> <Text style={contentStyle.closeTagText}>X</Text> </View> </View> </View> <View style={contentStyle.imgArea}> <Image source={{uri: rowData.images[0]}} style={contentStyle.rightImg} /> </View> </View> </View>; }
圖2.6.9
咱們再把假數據多寫點,因而一個list就完整的展示了出來:
圖2.6.10
3 課後做業
又到了課後做業時間,今天的做業是,請完成如圖3.1所示形狀的item:
聰明的讀着,知道該怎麼作了嗎?
本文中提到的例子與素材,均在下面的github上,須要的話請下載:
https://github.com/houyu01/react-native-android-tutorial/tree/master/baiduapp
接下來,我會詳細的講解一下reactjs的基礎知識。包括本節中提到的建立組件等,不要走開,請關注我.....
http://my.oschina.net/MrHou/blog/702561
原創文章,版權全部,轉載請註明出處