本篇資源:連接: https://pan.baidu.com/s/1jIbW2n8 密碼: wqe4php
從這篇開始咱們就將源碼託管到 github 上,須要源碼的 點我下載,喜歡的話記得 Star,謝謝!html
React-Native
的方式解決。
1.5秒
後自動跳轉到 Main 組件。export default class GDLaunchPage extends Component { componentDidMount() { setTimeout(() => { this.props.navigator.replace({ component:Main }) }, 1500) } render() { return( <Image source={{uri:'launchimage'}} style={styles.imageStyle} /> ); } }
項目的版本管理也是程序猿必須具有的一項技能,它可以讓咱們避免許多開發中遇到的尷尬問題。java
這小結建議觀看視頻,視頻內有具體操做!react
之前看官方文檔居然沒有發現 React-Native 提供了 model 組件,在這裏給你們道個歉,之後跪着寫教程,不用讓我起來,反正我感受膝蓋軟軟的!android
前幾天在看官方文檔的時候,無心中看見 model 組件,我嘞個天,有這東西就能夠減小開發中不少功能開發難度。當初怎麼沒發現,還傻傻地一步一步去封裝這個東西 T^T,在這告誡各位,不能太粗心!git
這邊咱們就將本來 近半小時熱門 這個模塊的跳轉模式改爲 正宗的 模態,代碼以下:github
render() { return ( <View style={styles.container}> {/* 初始化模態 */} <Modal animationType='slide' transparent={false} visible={this.state.isModal} onRequestClose={() => this.onRequestClose()} > <Navigator initialRoute={{ name:'halfHourHot', component:HalfHourHot }} renderScene={(route, navigator) => { let Component = route.component; return <Component removeModal={(data) => this.closeModal(data)} {...route.params} navigator={navigator} /> }} /> </Modal> {/* 導航欄樣式 */} <CommunalNavBar leftItem = {() => this.renderLeftItem()} titleItem = {() => this.renderTitleItem()} rightItem = {() => this.renderRightItem()} /> {/* 根據網絡狀態決定是否渲染 listview */} {this.renderListView()} </View> ); }
注:這邊須要注意一下 逆向傳值 的方式,這裏用到最基本的逐層傳值,相似於
block
的功能,具體的代碼參考 Demo , Demo 下載地址在上面。web
關於更詳細地 model 使用,能夠參照官方文檔 model ,固然我也給各位上了這道菜 —— React-Native 之 model介紹與使用 。react-native
經過查看 modal 的源碼,咱們不難發現 —— 其實 modal 實現原理也只是使用了 絕對定位,因此若是 modal 沒法知足咱們的功能,咱們可使用 絕對定位 來本身實現一下相似功能。api
這邊咱們來完善一下 加載更多功能數據 的加載,須要注意的一點就是,拼接數組須要使用 concat
方法來拼接,它會返回一個 新的數組 給咱們使用,而不修改傳入的數組。
這邊咱們加載數據的方法分爲 2 個,代碼看起來重複性很高,可是其實這就取決於咱們的需求了,咱們分爲 2 個的好處是看起來更清晰,減小溝通成本,想象一下,若是咱們把全部邏輯都放到同一個方法內,那麼是否是這個方法內的邏輯是否是特別複雜,不方便後期維護?!因此這就是爲何分爲 2 個方法進行加載的緣由。
那來看一下加載最新數據這邊邏輯:
// 加載最新數據網絡請求 loadData(resolve) { let params = {"count" : 10 }; HTTPBase.get('https://guangdiu.com/api/getlist.php', params) .then((responseData) => { // 清空數組 this.data = []; // 拼接數據 this.data = this.data.concat(responseData.data); // 從新渲染 this.setState({ dataSource: this.state.dataSource.cloneWithRows(this.data), loaded:true, }); // 關閉刷新動畫 if (resolve !== undefined){ setTimeout(() => { resolve(); }, 1000); } // 存儲數組中最後一個元素的id let cnlastID = responseData.data[responseData.data.length - 1].id; AsyncStorage.setItem('cnlastID', cnlastID.toString()); }) .catch((error) => { }) }
再來看下加載更多這邊的邏輯:
加載更多須要在獲取 最新 數據的時候將數組中 最後一個元素
內的ID保存起來,由於不是大批量數據存儲,這邊咱們就使用 AsyncStorage 進行 id
的存儲。
接着,咱們拼接請求參數。
// 加載更多數據的網絡請求 loadMoreData(value) { let params = { "count" : 10, "sinceid" : value }; HTTPBase.get('https://guangdiu.com/api/getlist.php', params) .then((responseData) => { // 拼接數據 this.data = this.data.concat(responseData.data); this.setState({ dataSource: this.state.dataSource.cloneWithRows(this.data), loaded:true, }); // 存儲數組中最後一個元素的id let cnlastID = responseData.data[responseData.data.length - 1].id; AsyncStorage.setItem('cnlastID', cnlastID.toString()); }) .catch((error) => { }) }
咱們回到主頁這邊來實現如下 cell
的點擊,須要注意的是對 row
進行綁定操做,否則會找不到當前的 this
。
// 綁定 renderRow={this.renderRow.bind(this)}
接着來看下 renderRow
方法實現:
// 返回每一行cell的樣式 renderRow(rowData) { return( <TouchableOpacity onPress={() => this.pushToDetail(rowData.id)} > <CommunalHotCell image={rowData.image} title={rowData.title} /> </TouchableOpacity> ); }
再來看下 pushToDetail
方法實現,params意思就是將 url
參數傳遞到 CommunalDetail
組件:
// 跳轉到詳情頁 pushToDetail(value) { this.props.navigator.push({ component:CommunalDetail, params: { url: 'https://guangdiu.com/api/showdetail.php' + '?' + 'id=' + value } }) }
既然咱們已經保存了 id
那麼就能夠來作詳情頁了,當咱們點擊 cell 的時候,須要跳轉到對應的 詳情頁 。
這邊服務器返回給咱們的是個 網頁數據 ,咱們這邊就直接使用 webView組件
展現,具體使用咱們就很少作介紹了,很簡單,詳情就參考官方文檔 WebView。
先來看詳情頁的實現:
export default class GDCommunalDetail extends Component { static propTypes = { uri:PropTypes.string, }; // 返回 pop() { this.props.navigator.pop(); } // 返回左邊按鈕 renderLeftItem() { return( <TouchableOpacity onPress={() => {this.pop()}} > <Text>返回</Text> </TouchableOpacity> ); } componentWillMount() { // 發送通知 DeviceEventEmitter.emit('isHiddenTabBar', true); } componentWillUnmount() { // 發送通知 DeviceEventEmitter.emit('isHiddenTabBar', false); } render() { return( <View style={styles.container}> {/* 導航欄 */} <CommunalNavBar leftItem = {() => this.renderLeftItem()} /> {/* 初始化WebView */} <WebView style={styles.webViewStyle} source={{url:this.props.url, method: 'GET' }} javaScriptEnabled={true} domStorageEnabled={true} scalesPageToFit={false} /> </View> ); } } const styles = StyleSheet.create({ container: { flex:1 }, webViewStyle: { flex:1 } });
按照上面的方法,咱們完成一下 近半小時熱門模塊 的跳轉詳情功能。
和 近半小時熱門 效果是同樣的,只是請求參數變了,因此 Copy 而後修改下相應參數啊:
export default class GDUSHalfHourHot 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); } static defaultProps = { removeModal:{} } // 網絡請求 fetchData(resolve) { let params = { "c" : "us" }; HTTPBase.get('http://guangdiu.com/api/gethots.php', params) .then((responseData) => { this.setState({ dataSource: this.state.dataSource.cloneWithRows(responseData.data), loaded:true, }); if (resolve !== undefined){ setTimeout(() => { resolve(); // 關閉動畫 }, 1000); } }) .catch((error) => { }) } popToHome(data) { this.props.removeModal(data); } // 返回中間按鈕 renderTitleItem() { return( <Text style={styles.navbarTitleItemStyle}>近半小時熱門</Text> ); } // 返回右邊按鈕 renderRightItem() { return( <TouchableOpacity onPress={()=>{this.popToHome(false)}} > <Text style={styles.navbarRightItemStyle}>關閉</Text> </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.bind(this)} showsHorizontalScrollIndicator={false} style={styles.listViewStyle} initialListSize={5} renderHeader={this.renderHeader} /> ); } } // 返回 listview 頭部 renderHeader() { return ( <View style={styles.headerPromptStyle}> <Text>根據每條折扣的點擊進行統計,每5分鐘更新一次</Text> </View> ); } // 跳轉到詳情頁 pushToDetail(value) { this.props.navigator.push({ component:CommunalDetail, params: { url: 'https://guangdiu.com/api/showdetail.php' + '?' + 'id=' + value } }) } // 返回每一行cell的樣式 renderRow(rowData) { return( <TouchableOpacity onPress={() => this.pushToDetail(rowData.id)} > <CommunalHotCell image={rowData.image} title={rowData.title} /> </TouchableOpacity> ); } componentWillMount() { // 發送通知 DeviceEventEmitter.emit('isHiddenTabBar', true); } componentWillUnmount() { // 發送通知 DeviceEventEmitter.emit('isHiddenTabBar', false); } componentDidMount() { this.fetchData(); } render() { return ( <View style={styles.container}> {/* 導航欄樣式 */} <CommunalNavBar titleItem = {() => this.renderTitleItem()} rightItem = {() => this.renderRightItem()} /> {/* 根據網絡狀態決定是否渲染 listview */} {this.renderListView()} </View> ); } } const styles = StyleSheet.create({ container: { flex:1, alignItems: 'center', }, navbarTitleItemStyle: { fontSize:17, color:'black', marginLeft:50 }, navbarRightItemStyle: { fontSize:17, color:'rgba(123,178,114,1.0)', marginRight:15 }, listViewStyle: { width:width, }, headerPromptStyle: { height:44, width:width, backgroundColor:'rgba(239,239,239,0.5)', justifyContent:'center', alignItems:'center' } });
咱們能夠發現 海淘 這一塊和 首頁 是相似的,只是數據請求參數不一樣,因此咱們仍是 Copy 一下代碼,而後將請求參數改成以下:
export default class GDHome extends Component { // 構造 constructor(props) { super(props); // 初始狀態 this.state = { dataSource: new ListView.DataSource({rowHasChanged:(r1, r2) => r1 !== r2}), loaded:false, isModal:false }; this.data = []; this.loadData = this.loadData.bind(this); this.loadMore = this.loadMore.bind(this); } // 加載最新數據網絡請求 loadData(resolve) { let params = { "count" : 10, "country" : "us" }; HTTPBase.get('https://guangdiu.com/api/getlist.php', params) .then((responseData) => { // 拼接數據 this.data = this.data.concat(responseData.data); // 從新渲染 this.setState({ dataSource: this.state.dataSource.cloneWithRows(this.data), loaded:true, }); // 關閉刷新動畫 if (resolve !== undefined){ setTimeout(() => { resolve(); }, 1000); } // 存儲數組中最後一個元素的id let uslastID = responseData.data[responseData.data.length - 1].id; AsyncStorage.setItem('uslastID', uslastID.toString()); }) .catch((error) => { }) } // 加載更多數據的網絡請求 loadMoreData(value) { let params = { "count" : 10, "sinceid" : value, "country" : "us" }; HTTPBase.get('https://guangdiu.com/api/getlist.php', params) .then((responseData) => { // 拼接數據 this.data = this.data.concat(responseData.data); this.setState({ dataSource: this.state.dataSource.cloneWithRows(this.data), loaded:true, }); // 存儲數組中最後一個元素的id let uslastID = responseData.data[responseData.data.length - 1].id; AsyncStorage.setItem('uslastID', uslastID.toString()); }) .catch((error) => { }) } // 加載更多數據操做 loadMore() { // 讀取id AsyncStorage.getItem('uslastID') .then((value) => { // 數據加載操做 this.loadMoreData(value); }) } // 模態到近半小時熱門 pushToHalfHourHot() { this.setState({ isModal:true }) } // 跳轉到搜索 pushToSearch() { this.props.navigator.push({ component:Search, }) } // 安卓模態銷燬處理 onRequestClose() { this.setState({ isModal:false }) } // 關閉模態 closeModal(data) { this.setState({ isModal:data }) } // 返回左邊按鈕 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尾部 renderFooter() { return ( <View style={{height: 100}}> <ActivityIndicator /> </View> ); } // 根據網絡狀態決定是否渲染 listview renderListView() { if (this.state.loaded === false) { return( <NoDataView /> ); }else { return( <PullList onPullRelease={(resolve) => this.loadData(resolve)} dataSource={this.state.dataSource} renderRow={this.renderRow.bind(this)} showsHorizontalScrollIndicator={false} style={styles.listViewStyle} initialListSize={5} renderHeader={this.renderHeader} onEndReached={this.loadMore} onEndReachedThreshold={60} renderFooter={this.renderFooter} /> ); } } // 跳轉到詳情頁 pushToDetail(value) { this.props.navigator.push({ component:CommunalDetail, params: { url: 'https://guangdiu.com/api/showdetail.php' + '?' + 'id=' + value } }) } // 返回每一行cell的樣式 renderRow(rowData) { return( <TouchableOpacity onPress={() => this.pushToDetail(rowData.id)} > <CommunalHotCell image={rowData.image} title={rowData.title} /> </TouchableOpacity> ); } componentDidMount() { this.loadData(); } render() { return ( <View style={styles.container}> {/* 初始化模態 */} <Modal animationType='slide' transparent={false} visible={this.state.isModal} onRequestClose={() => this.onRequestClose()} > <Navigator initialRoute={{ name:'halfHourHot', component:USHalfHourHot }} renderScene={(route, navigator) => { let Component = route.component; return <Component removeModal={(data) => this.closeModal(data)} {...route.params} navigator={navigator} /> }} /> </Modal> {/* 導航欄樣式 */} <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, }, });
這裏須要 cnmaxid
和 usmaxid
參數,他們分別是最新數據中第一個元素的 id
,也就是咱們每次 刷新 的時候都保存一下數組中的第一個元素的 id
。
// 首頁存儲數組中第一個元素的id let cnfirstID = responseData.data[0].id; AsyncStorage.setItem('cnfirstID', cnfirstID.toString());
這個功能是從程序啓動的時候就開始 定時循環執行 ,也就是咱們須要放到 入口文件中(Main文件)。
componentDidMount() { // 註冊通知 this.subscription = DeviceEventEmitter.addListener('isHiddenTabBar', (data)=>{this.tongZhi(data)}); // 聲明變量 let cnfirstID = 0; let usfirstID = 0; // 最新數據的個數 setInterval(() => { // 取出id AsyncStorage.getItem('cnfirstID') .then((value) => { cnfirstID = parseInt(value); }); AsyncStorage.getItem('usfirstID') .then((value) => { usfirstID = parseInt(value); }); if (cnfirstID !== 0 && usfirstID !== 0) { // 參數不爲0 // 拼接參數 let params = { "cnmaxid" : cnfirstID, "usmaxid" : usfirstID }; // 請求數據 HTTPBase.get('http://guangdiu.com/api/getnewitemcount.php', params) .then((responseData) => { console.log(responseData); this.setState({ cnbadgeText:responseData.cn, usbadgeText:responseData.us }) }) } }, 30000); }
注:上面使用到的
setInterval
也是個定時器,和咱們以前使用的setTimeout
不一樣的是,setInterval
是週期定時器,好比上面時間爲30000毫秒
,意思就是每過30000毫秒
就會執行一次裏面的代碼。而setTimeout
則是會在規定的時間後儘快
執行任務。