1.加载更多功能完善
GDHome.js
/** * 首页 */ import React, { Component } from 'react'; import { StyleSheet, Text, View, TouchableOpacity, Image, ListView, Dimensions, ActivityIndicator, Modal, // 模态 AsyncStorage, // 缓存数据库(数据持久化) } from 'react-native'; // 引入 下拉刷新组件 import {PullList} from 'react-native-pull'; // 导航器 import CustomerComponents, { Navigator } from 'react-native-deprecated-custom-components'; // 获取屏幕宽高 const {width, height} = Dimensions.get('window'); // 引入自定义导航栏组件 import CommunalNavBar from '../main/GDCommunalNavBar'; // 引入 近半小时热门组件 import HalfHourHot from './GDHalfHourHot'; // 引入 搜索页面组件 import Search from './GDSearch'; // 引入 cell import CommunalHotCell from '../main/GDCommunalHotCell'; // 引入 空白页组件 import NoDataView from '../main/GDNoDataView'; // 引入 HTTP封装组件 import HTTPBase from '../http/HTTPBase'; 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 }; 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) => { }) } // 加载更多数据的网络请求 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) => { }) } // 加载更多数据操作 loadMore() { // 读取存储的id AsyncStorage.getItem('cnlastID') .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 // 将ListView 改为 PullList // 下拉刷新 onPullRelease={(resolve) => this.loadData(resolve)} // 数据源 通过判断dataSource是否有变化,来判断是否要重新渲染 dataSource={this.state.dataSource} renderRow={this.renderRow} // 隐藏水平线 showsHorizontalScrollIndicator={false} style={styles.listViewStyle} initialListSize={5} // 返回 listView 头部 renderHeader={this.renderHeader} // 上拉加载更多 onEndReached={this.loadMore} onEndReachedThreshold={60} renderFooter={this.renderFooter} /> ); } } // 返回每一行cell的样式 renderRow(rowData) { // 使用cell组件 return( <CommunalHotCell image={rowData.image} title={rowData.title} /> ); } // 生命周期 组件渲染完成 已经出现在dom文档里 componentDidMount() { // 请求数据 this.loadData(); } render() { return ( <View style={styles.container}> {/* 初始化模态 */} <Modal animationType='slide' // 动画 底部弹窗 transparent={false} // 透明度 visible={this.state.isModal} // 可见性 onRequestClose={() => this.onRequestClose()} // 销毁 > <HalfHourHot removeModal={(data) => this.closeModal(data)} /> </Modal> {/* 导航栏样式 */} <CommunalNavBar leftItem = {() => this.renderLeftItem()} titleItem = {() => this.renderTitleItem()} rightItem = {() => this.renderRightItem()} /> {/* 根据网络状态决定是否渲染 listView */} {this.renderListView()} </View> ); } } const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center', }, navbarLeftItemStyle: { 20, height:20, marginLeft:15, }, navbarTitleItemStyle: { 66, height:20, }, navbarRightItemStyle: { 20, height:20, marginRight:15, }, listViewStyle: { width, }, });
核心代码:
// 加载最新数据网络请求 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) => { }) } // 加载更多数据的网络请求 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) => { }) } // 加载更多数据操作 loadMore() { // 读取存储的id AsyncStorage.getItem('cnlastID') .then((value) => { // 数据加载操作 this.loadMoreData(value); }) }
2.详情页
(1)Cell 点击实现
我们回到主页这边来实现以下 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 } }) }
(2)详情页
既然我们已经保存了 id 那么就可以来做详情页了,当我们点击 cell 的时候,需要跳转到对应的 详情页 。
先来看详情页的实现:
GDCommunalDetail.js
/** * 详情页 */ import React, { Component, PropTypes } from 'react'; import { StyleSheet, WebView, View, Text, TouchableOpacity, DeviceEventEmitter, } from 'react-native'; // 导航器 import CustomerComponents, { Navigator } from 'react-native-deprecated-custom-components'; // 引入自定义导航栏组件 import CommunalNavBar from './GDCommunalNavBar'; 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() { // 向GDMain.js 发送通知 隐藏tabBar DeviceEventEmitter.emit('isHiddenTabBar', true); } componentWillUnmount() { // 向GDMain.js 发送通知 显示tabBar DeviceEventEmitter.emit('isHiddenTabBar', false); } render() { return( <View style={styles.container}> {/* 导航栏 */} <CommunalNavBar leftItem = {() => this.renderLeftItem()} /> {/* 初始化WebView */} <WebView style={styles.webViewStyle} source={{uri:this.props.url, method: 'GET' }} javaScriptEnabled={true} domStorageEnabled={true} scalesPageToFit={false} /> </View> ); } } const styles = StyleSheet.create({ container: { flex:1 }, webViewStyle: { flex: 1 } });
效果图
3. 调用
GDHome.js
/** * 首页 */ import React, { Component } from 'react'; import { StyleSheet, Text, View, TouchableOpacity, Image, ListView, Dimensions, ActivityIndicator, Modal, // 模态 AsyncStorage, // 缓存数据库(数据持久化) } from 'react-native'; // 引入 下拉刷新组件 import {PullList} from 'react-native-pull'; // 导航器 import CustomerComponents, { Navigator } from 'react-native-deprecated-custom-components'; // 获取屏幕宽高 const {width, height} = Dimensions.get('window'); // 引入自定义导航栏组件 import CommunalNavBar from '../main/GDCommunalNavBar'; // 引入 近半小时热门组件 import HalfHourHot from './GDHalfHourHot'; // 引入 搜索页面组件 import Search from './GDSearch'; // 引入 cell import CommunalHotCell from '../main/GDCommunalHotCell'; // 引入 详情页 组件 import CommunalDetail from '../main/GDCommunalDetail'; // 引入 空白页组件 import NoDataView from '../main/GDNoDataView'; // 引入 HTTP封装组件 import HTTPBase from '../http/HTTPBase'; 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 }; 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) => { }) } // 加载更多数据的网络请求 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) => { }) } // 加载更多数据操作 loadMore() { // 读取存储的id AsyncStorage.getItem('cnlastID') .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 // 将ListView 改为 PullList // 下拉刷新 onPullRelease={(resolve) => this.loadData(resolve)} // 数据源 通过判断dataSource是否有变化,来判断是否要重新渲染 dataSource={this.state.dataSource} renderRow={this.renderRow.bind(this)} // 隐藏水平线 showsHorizontalScrollIndicator={false} style={styles.listViewStyle} initialListSize={5} // 返回 listView 头部 renderHeader={this.renderHeader} // 上拉加载更多 onEndReached={this.loadMore} onEndReachedThreshold={60} renderFooter={this.renderFooter} /> ); } } // 通过id 跳转详情页 pushToDetail(value) { this.props.navigator.push({ component:CommunalDetail, params: { url: 'https://guangdiu.com/api/showdetail.php' + '?' + 'id=' + value } }) } // 返回每一行cell的样式 renderRow(rowData) { // 使用cell组件 return( <TouchableOpacity // 给每一个cell添加点击事件 onPress={() => this.pushToDetail(rowData.id)} > <CommunalHotCell image={rowData.image} title={rowData.title} /> </TouchableOpacity> ); } // 生命周期 组件渲染完成 已经出现在dom文档里 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: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> ); } } const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center', }, navbarLeftItemStyle: { 20, height:20, marginLeft:15, }, navbarTitleItemStyle: { 66, height:20, }, navbarRightItemStyle: { 20, height:20, marginRight:15, }, listViewStyle: { width, }, });
GDHalfHourHot.js
/** * 近半小时热门 */ import React, { Component } from 'react'; import { StyleSheet, Text, View, TouchableOpacity, Image, ListView, Dimensions, DeviceEventEmitter, } from 'react-native'; // 获取屏幕宽高 const {width, height} = Dimensions.get('window'); // 引入自定义导航栏组件 import CommunalNavBar from '../main/GDCommunalNavBar'; // 引入 cell import CommunalHotCell from '../main/GDCommunalHotCell'; // 引入 详情页 组件 import CommunalDetail from '../main/GDCommunalDetail'; // 引入 空白页组件 import NoDataView from '../main/GDNoDataView'; // 引入 下拉刷新组件 import {PullList} from 'react-native-pull'; // 引入 HTTP封装组件 import HTTPBase from '../http/HTTPBase'; export default class GDHalfHourHot extends Component { // 构造 constructor(props) { super(props); // 初始状态 this.state = { dataSource: new ListView.DataSource({rowHasChanged:(r1, r2) => r1 !== r2}), // 数据源 优化 loaded: false, // 用于判断是否显示空白页 }; // 绑定 this.fetchData = this.fetchData.bind(this); } // 提供参数出去,便于外部调用 static defaultProps = { removeModal:{} } // 网络请求 fetchData(resolve) { HTTPBase.get('http://guangdiu.com/api/gethots.php') .then((responseData) => { // 处理数据 // 修改dataSource的值 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 // 将ListView 改为 PullList // 下拉刷新 onPullRelease={(resolve) => this.fetchData(resolve)} // 数据源 通过判断dataSource是否有变化,来判断是否要重新渲染 dataSource={this.state.dataSource} renderRow={this.renderRow.bind(this)} // 隐藏水平线 showsHorizontalScrollIndicator={false} style={styles.listViewStyle} initialListSize={5} // 返回 listView 头部 renderHeader={this.renderHeader} /> ); } } // 返回 listView 头部 renderHeader() { return( <View style={styles.headerPromptStyle}> <Text>根据每条折扣的点击进行统计,每5分钟更新一次</Text> </View> ); } // 通过id 跳转详情页 pushToDetail(value) { this.props.navigator.push({ component:CommunalDetail, params: { url: 'http://guangdiu.com/api/showdetail.php' + '?' + 'id=' + value } }) } // 返回每一行cell的样式 renderRow(rowData) { // 使用cell组件 return( <TouchableOpacity // 给每一个cell添加点击事件 onPress={() => this.pushToDetail(rowData.id)} > <CommunalHotCell image={rowData.image} title={rowData.title} /> </TouchableOpacity> ); } componentWillMount() { // 向GDMain.js 发送通知 隐藏tabBar DeviceEventEmitter.emit('isHiddenTabBar', true); } componentWillUnmount() { // 向GDMain.js 发送通知 显示tabBar DeviceEventEmitter.emit('isHiddenTabBar', false); } // 生命周期 组件渲染完成 已经出现在dom文档里 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 }, headerPromptStyle: { height:44, width, backgroundColor:'rgba(239,239,239,0.5)', justifyContent:'center', alignItems:'center' }, listViewStyle: { width, }, });
.