本篇資源:連接: https://pan.baidu.com/s/1i4HFmeT 密碼: yy79php
源碼託管到 github 上,須要源碼的 點我下載,喜歡的話記得 Star,謝謝!node
有朋友反饋這邊 屬性聲明和屬性肯定 不瞭解,這邊就來補充一下。react
在 React-Native 建立的自定義組件是能夠複用的,而開發過程當中一個組件可能會由多我的同時開發或者多我的使用一個組件,爲了讓開發人員之間減小溝通成本,咱們會對某些必要的屬性進行屬性聲明,讓使用的人知道須要傳入什麼!甚至有些須要傳入但沒有傳入值的屬性咱們會進行警告處理!git
這邊先來看下 屬性聲明 的示例:github
static propTypes = { name:PropTypes.string, ID:PropTypes.number.isRequired, }
上面咱們聲明瞭 name
和 ID
兩個屬性,而且進行了屬性的確認,其中,'isRequired' 表示若是不傳遞這個屬性,那麼開發階段中,系統會出現警告,讓咱們對其進行屬性確認,也就是說是否爲必須屬性。json
React.PropTypes.any
React.PropTypes.array; React.PropTypes.func; React.PropTypes.bool; React.PropTypes.number; React.PropTypes.object; React.PropTypes.string;
React.PropTypes.element;
React.PropTypes.oneOf(['value1', 'value2'])
React.PropTypes.oneOfType([ React.PropTypes.node, React.PropTypes.number, React.PropTypes.string ])
React.PropTypes.node;
React.PropTypes.instanceOf(NameOfClass);
React.PropTypes.arrayOf(React.PropTypes.string)
React.PropTypes..objectOf(React.PropTypes.number)
React.PropTypes.shape({ color:React.PropTypes.stirng, fontSize:React.PropTypes.number })
static defaultProps = { name:'蒼井空' };
開發中,咱們會有許多圖片都是從網絡進行請求的,可是,若是出現網絡卡頓的狀況,圖片就會遲遲不出現,又或者有的並無圖片,這樣圖片就爲空白狀態;爲了避免讓用戶感受太突兀影響用戶體驗,也爲了視圖總體性,通常咱們會選擇使用佔位圖先展現給用戶看,等到圖片加載完畢再將圖片展現出來。react-native
這邊咱們須要對cell內部進行一些處理。api
{/* 左邊圖片 */} <Image source={{uri:this.props.image === '' ? 'defaullt_thumb_83x83' : this.props.image}} style={styles.imageStyle} />
仍是網絡問題,在網絡出現問題或者沒法加載數據的時候,通常咱們會展現空白頁,在空白頁中提示 無數據
之類的提示,比較好的還會使用 指示器
的方式告訴用戶網絡出現問題等等。數組
這邊咱們作如下處理,當無數據時,咱們就先初始化基礎界面,而後展現 提示
頁面,等到有數據時,再從新渲染數據。網絡
首先設置 無數據 頁面
import React, { Component } from 'react'; import { StyleSheet, View, Text, } from 'react-native'; export default class GDNoDataView extends Component { render() { return( <View style={styles.container}> <Text style={styles.textStyle}>無數據 </Text> </View> ); } } const styles = StyleSheet.create({ container: { flex:1, justifyContent:'center', alignItems:'center', }, textStyle: { fontSize:21, color:'gray' } });
// 根據網絡狀態決定是否渲染 listview renderListView() { if (this.state.loaded === false) { return( <NoDataView /> ); }else { return( <PullList onPullRelease={(resolve) => this.fetchData(resolve)} dataSource={this.state.dataSource} renderRow={this.renderRow} showsHorizontalScrollIndicator={false} style={styles.listViewStyle} initialListSize={5} /> ); } }
<ListView dataSource={this.state.dataSource} renderRow={this.renderRow} showsHorizontalScrollIndicator={false} style={styles.listViewStyle} initialListSize={5} renderHeader={this.renderHeader} />
// 返回 listview 頭部 renderHeader() { return ( <View style={styles.headerPromptStyle}> <Text>根據每條折扣的點擊進行統計,每5分鐘更新一次</Text> </View> ); }
react-native-pull
實現下拉刷新和上拉加載更多的功能。<PullList onPullRelease={(resolve) => this.fetchData(resolve)} dataSource={this.state.dataSource} renderRow={this.renderRow} showsHorizontalScrollIndicator={false} style={styles.listViewStyle} initialListSize={5} renderHeader={this.renderHeader} />
// 網絡請求 fetchData(resolve) { setTimeout(() => { fetch('http://guangdiu.com/api/gethots.php') .then((response) => response.json()) .then((responseData) => { this.setState({ dataSource: this.state.dataSource.cloneWithRows(responseData.data), loaded:true, }); if (resolve !== undefined){ setTimeout(() => { resolve(); // 關閉動畫 }, 1000); } }) .done(); }); }
GET 和 POST 是咱們請求 HTTP 接口經常使用的方式,針對表單提交的請求,咱們一般採用 POST 的方式。
在 JQuery 中,傳入對象框架會自動封裝成 formData 的形式,可是在 fetch 中沒有這個功能,因此咱們須要本身初始化一個 FormData 直接傳給 body (補充:FormData也能夠傳遞字節流實現上傳圖片功能)。
let formData = new FormData(); formData.append("參數", "值"); formData.append("參數", "值"); fetch(url, { method:'POST, headers:{}, body:formData, }).then((response)=>{ if (response.ok) { return response.json(); } }).then((json)=>{ alert(JSON.stringify(json)); }).catch.((error)=>{ console.error(error); })
import React, { Component } from 'react'; import { StyleSheet, Text, View, TouchableOpacity, Image, ListView, Dimensions } from 'react-native'; // 第三方 import {PullList} from 'react-native-pull'; const {width, height} = Dimensions.get('window'); // 引用外部文件 import CommunalNavBar from '../main/GDCommunalNavBar'; import CommunalHotCell from '../main/GDCommunalHotCell'; import HalfHourHot from './GDHalfHourHot'; import Search from './GDSearch'; export default class GDHome extends Component { // 構造 constructor(props) { super(props); // 初始狀態 this.state = { dataSource: new ListView.DataSource({rowHasChanged:(r1, r2) => r1 !== r2}), loaded:true, }; this.fetchData = this.fetchData.bind(this); } // 網絡請求 fetchData(resolve) { let formData = new FormData(); formData.append("count", "30"); setTimeout(() => { fetch('http://guangdiu.com/api/getlist.php', { method:'POST', headers:{}, body:formData, }) .then((response) => response.json()) .then((responseData) => { this.setState({ dataSource: this.state.dataSource.cloneWithRows(responseData.data), loaded:true, }); if (resolve !== undefined){ setTimeout(() => { resolve(); }, 1000); } }) .done(); }); } // 跳轉到近半小時熱門 pushToHalfHourHot() { this.props.navigator.push({ component: HalfHourHot, }) } // 跳轉到搜索 pushToSearch() { this.props.navigator.push({ component:Search, }) } // 返回左邊按鈕 renderLeftItem() { return( <TouchableOpacity onPress={() => {this.pushToHalfHourHot()}} > <Image source={{uri:'hot_icon_20x20'}} style={styles.navbarLeftItemStyle} /> </TouchableOpacity> ); } // 返回中間按鈕 renderTitleItem() { return( <TouchableOpacity> <Image source={{uri:'navtitle_home_down_66x20'}} style={styles.navbarTitleItemStyle} /> </TouchableOpacity> ); } // 返回右邊按鈕 renderRightItem() { return( <TouchableOpacity onPress={()=>{this.pushToSearch()}} > <Image source={{uri:'search_icon_20x20'}} style={styles.navbarRightItemStyle} /> </TouchableOpacity> ); } // 根據網絡狀態決定是否渲染 listview renderListView() { if (this.state.loaded === false) { return( <NoDataView /> ); }else { return( <PullList onPullRelease={(resolve) => this.fetchData(resolve)} dataSource={this.state.dataSource} renderRow={this.renderRow} showsHorizontalScrollIndicator={false} style={styles.listViewStyle} initialListSize={5} renderHeader={this.renderHeader} /> ); } } // 返回每一行cell的樣式 renderRow(rowData) { return( <CommunalHotCell image={rowData.image} title={rowData.title} /> ); } componentDidMount() { this.fetchData(); } render() { return ( <View style={styles.container}> {/* 導航欄樣式 */} <CommunalNavBar leftItem = {() => this.renderLeftItem()} titleItem = {() => this.renderTitleItem()} rightItem = {() => this.renderRightItem()} /> {/* 根據網絡狀態決定是否渲染 listview */} {this.renderListView()} </View> ); } } const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center', backgroundColor: 'white', }, navbarLeftItemStyle: { width:20, height:20, marginLeft:15, }, navbarTitleItemStyle: { width:66, height:20, }, navbarRightItemStyle: { width:20, height:20, marginRight:15, }, listViewStyle: { width:width, }, });
效果:
有時候咱們須要在跳轉的時候使用不一樣的跳轉動畫,好比咱們 半小時熱門 的跳轉方式在 iOS
內叫 模態跳轉,特性就是當頁面退出後會直接銷燬,多用於註冊、登陸等不須要常駐內存的界面。
react-native 中爲了方便實現這樣的功能,咱們能夠在初始化 Navigator
的時候,在 ‘configsSence’ 中進行操做;具體操做以下:
// 設置跳轉動畫 configureScene={(route) => this.setNavAnimationType(route)} // 設置Navigator跳轉動畫 setNavAnimationType(route) { if (route.animationType) { // 有值 return route.animationType; }else { return Navigator.SceneConfigs.PushFromRight; } }
// 跳轉到近半小時熱門 pushToHalfHourHot() { this.props.navigator.push({ component: HalfHourHot, animationType:Navigator.SceneConfigs.FloatFromBottom }) }
做爲跳轉動畫,可是當咱們下拉的時候,動畫中默認附帶的 **返回手勢** 會干擾咱們
ListView的滑動手勢,這個怎麼解決呢?其實很簡單,咱們只要關閉
Navigator手勢就能夠了嘛,怎麼關閉呢?其實在源碼中咱們能夠找到,手勢包含在動畫中,咱們若是不須要,只須要給其賦值爲
null` ,這樣它就不知道須要響應手勢事件了,方法以下:// 設置Navigator跳轉動畫 setNavAnimationType(route) { if (route.animationType) { // 有值 let conf = route.animationType; conf.gestures = null; // 關閉返回手勢 return conf; }else { return Navigator.SceneConfigs.PushFromRight; } }
Navigator
手勢功能。onEndReached
、onEndReachedThreshold
、renderFooter
使用loadMore() { // 數據加載操做 } renderFooter() { return ( <View style={{height: 100}}> <ActivityIndicator /> </View> ); } // 根據網絡狀態決定是否渲染 listview renderListView() { if (this.state.loaded === false) { return( <NoDataView /> ); }else { return( <PullList onPullRelease={(resolve) => this.fetchData(resolve)} dataSource={this.state.dataSource} renderRow={this.renderRow} showsHorizontalScrollIndicator={false} style={styles.listViewStyle} initialListSize={5} renderHeader={this.renderHeader} onEndReached={this.loadMore} onEndReachedThreshold={60} renderFooter={this.renderFooter} /> ); } }
到這裏,相信各位對 React-Native 有所熟悉了吧,從如今開始咱們要慢慢往實際的方向走,這邊就先從網絡請求這部分開始,在正式開發中,網絡請求通常都單獨做爲一部分,咱們在須要使用的地方只須要簡單調用一下便可,這樣作的好處是讓整個 工程 的結構更加清晰,讓組件們各司其職,只管好本身該管的事,而且後期維護成本也會相應下降。
首先,咱們要先對 fetch 的 GET
和 POST
請求方式進行一層基礎封裝,也就是要把它們單獨獨立出來,那麼這邊先來看下 GET 這邊:
var HTTPBase = {}; /** * * GET請求 * * @param url * @param params {}包裝 * @param headers * * @return {Promise} * * */ HTTPBase.get = function (url, params, headers) { if (params) { let paramsArray = []; // 獲取 params 內全部的 key let paramsKeyArray = Object.keys(params); // 經過 forEach 方法拿到數組中每一個元素,將元素與參數的值進行拼接處理,而且放入 paramsArray 中 paramsKeyArray.forEach(key => paramsArray.push(key + '=' + params[key])); // 網址拼接 if (url.search(/\?/) === -1) { url += '?' + paramsArray.join('&'); }else { url += paramsArray.join('&'); } } return new Promise(function (resolve, reject) { fetch(url, { method:'GET', headers:headers }) .then((response) => response.json()) .then((response) => { resolve(response); }) .catch((error) => { reject({status:-1}) }) .done(); }) }
fetchData(resolve) { HTTPBase.get('http://guangdiu.com/api/gethots.php') .then((responseData) => { this.setState({ dataSource: this.state.dataSource.cloneWithRows(responseData.data), loaded:true, }); if (resolve !== undefined){ setTimeout(() => { resolve(); // 關閉動畫 }, 1000); } }) .catch((error) => { }) } export default HTTPBase;
/** * * POST請求 * * @param url * @param params {}包裝 * @param headers * * @return {Promise} * * */ HTTPBase.post = function (url, params, headers) { if (params) { // 初始化FormData var formData = new FormData(); // 獲取 params 內全部的 key let paramsKeyArray = Object.keys(params); // 經過 forEach 方法拿到數組中每一個元素,將元素與參數的值進行拼接處理,而且放入 paramsArray 中 paramsKeyArray.forEach(key => formData.append(key, params[key])); } return new Promise(function (resolve, reject) { fetch(url, { method:'POST', headers:headers, body:formData, }) .then((response) => response.json()) .then((response) => { resolve(response); }) .catch((error) => { reject({status:-1}) }) .done(); }) } export default HTTPBase;
// 網絡請求 fetchData(resolve) { let params = {"count" : 5 }; HTTPBase.post('http://guangdiu.com/api/getlist.php', params) .then((responseData) => { this.setState({ dataSource: this.state.dataSource.cloneWithRows(responseData.data), loaded:true, }); if (resolve !== undefined){ setTimeout(() => { resolve(); }, 1000); } }) .catch((error) => { }) }