React-Native 之 项目实战(四)

前言


  • 本文有配套视频,可以酌情观看。
  • 文中内容因各人理解不同,可能会有所偏差,欢迎朋友们联系我。
  • 文中所有内容仅供学习交流之用,不可用于商业用途,如因此引起的相关法律法规责任,与我无关。
  • 如文中内容对您造成不便,烦请联系 277511806@qq.com 处理,谢谢。
  • 转载麻烦注明出处,谢谢。

数据持久化


  • 数据持久化是移动端的一个重要部分,刚发现 Realm 原来已经支持 React-Native 了,那么这边另起一篇专门介绍两种常用的存储方式 ———— React-Native 之 数据持久化

  • 这边没有发现 官方 有将 商品数据 做本地缓存的功能,为了让大家知道怎么做,我们就简单地来实验一下,具体逻辑在每个产品中都或多或少有些差异,这个朋友们就根据所学的灵活变通一下就可以了!

  • 首先,在为了方便使用,也为了减少第三方框架对工程的 “污染”,我们需要对框架进行一次 基础 的封装。

        var RealmBase = {};
    
        import Realm from 'realm';
        
        const HomeSchame = {
            name:'HomeData',
            properties:{
                id:'int',
                title:'string',
                image:'string',
                mall:'string',
                pubtime:'string',
                fromsite:'string',
            }
        };
        
        const HTSchame = {
            name:'HTData',
            properties:{
                id:'int',
                title:'string',
                image:'string',
                mall:'string',
                pubtime:'string',
                fromsite:'string',
            }
        };
        
        // 初始化realm
        let realm = new Realm({schema:[HomeSchame, HTSchame]});
        
        // 增加
        RealmBase.create = function (schame, data) {
            realm.write(() => {
                for (let i = 0; i<data.length; i++) {
                    let temp = data[i];
                    realm.create(schame, {id:temp.id, title:temp.title, image:temp.image, mall:temp.mall, pubtime:temp.pubtime, fromsite:temp.fromsite});
                }
            })
        }
        
        // 查询全部数据
        RealmBase.loadAll = function (schame) {
            return realm.objects(schame);
        }
        
        // 条件查询
        RealmBase.filtered = function (schame, filtered) {
            // 获取对象
            let objects = realm.objects(schame);
            // 筛选
            let object = objects.filtered(filtered);
        
            if (object) {   // 有对象
                return object;
            }else {
                return '未找到数据';
            }
        }
        
        // 删除所有数据
        RealmBase.removeAllData = function (schame) {
            realm.write(() => {
                // 获取对象
                let objects = realm.objects(schame);
                // 删除表
                realm.delete(objects);
            })
        }
        
        global.RealmBase = RealmBase;
    
  • 经过简单封装后,我们还需要引用一下框架,引用框架,我们就放到 main 文件内,这样我们就可以确保 全局变量 是在我们使用它之前就被调用过,避免找不到对象的错误(我们也同时将 HTTPBase 修改为全局,方便使用)。

        import RealmStorage from '../storage/realmStorage';
    
  • 现在我们就来做下 本地持久化 实验,这边我们的逻辑就是,当网络出现问题的时候,每次都会进到 catch 中返回错误 code 告诉我们,出现了什么问题(既然进到这里了,是不是数据无论如何都不会加载成功?),那么我们就可以在这里 取出 本地数据,然后展示出来,那么数据的应该在哪里保存呢?这个我们只需要在每次 刷新 完成并且渲染完成后,保存一下数据就可以了。

        // 加载最新数据网络请求
        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());
    
                    // 存储数组中第一个元素的id
                    let cnfirstID = responseData.data[0].id;
                    AsyncStorage.setItem('cnfirstID', cnfirstID.toString());
    
                    // 清楚本地存储的数据
                    RealmBase.removeAllData('HomeData');
    
                    // 存储数据到本地
                    RealmBase.create('HomeData', responseData.data);
                })
                .catch((error) => {
                    // 拿到本地存储的数据,展示出来,如果没有存储,那就显示无数据页面
                    this.data = RealmBase.loadAll('HomeData');
    
                    // 重新渲染
                    this.setState({
                        dataSource: this.state.dataSource.cloneWithRows(this.data),
                        loaded:true,
                    });
                })
        }
    
  • 到这里,我们就完成了 数据本地持久化 并且成功将数据取出,这边数据持久化的实验就完成了。

数据持久化.gif

译注:

  • 有关 realm 的配置,可转到 React-Native 之 数据持久化 查看,这边不多赘述。

  • 当我们完全配置完 realm 后,会发现整个工程 多出将近500M+,不用担心,这些只不过是 realm 的依赖库,等我们后面 打包 后就不会有这么多东西了。

  • 我们已经使用了 git 进行对代码进行托管,那么,如果从仓库拉取最新数据时,是不是还是要重新配置 node_modules 文件夹,这个就很麻烦了,我们可以备份一下 node_modules 文件夹,将它 拷贝 进工程就可以使用了。

.gitignore 语法


  • gitignore 文件内包含了需要忽略或保留的文件的一些配置,灵活使用可以减少我们的工作量,但是里面的内容是什么意思呢?这边也给大家说下:

    • /:表示目录
    • *:为通配多个字符
    • ?:通配单个字符
    • []:包含单个字符的匹配列表
    • !:表示不忽略匹配到的文件和目录
    • // 示例
    • // 忽略ios文件夹下的所有内容
  • 知道语法后,我们是不是就能看懂 工程中 gitignore 文件的内容了,举个栗子:

        # Xcode                 // 注释,说明这是 Xcode 配置
        #
        build/                  // 忽略 build 文件夹下所有内容
        *.pbxuser               // 忽略以 .pbxuser 为后缀的文件
        !default.pbxuser        // 除 default.pbxuser 文件外
        *.mode1v3               // 忽略以 .mode1v3  为后缀的文件
        !default.mode1v3        // 除 default.mode1v3     文件外
    
        # node.js               // 注释,说明这是 node 配置
        #
        node_modules/           // 忽略 node_modules 文件夹内所有内容
        npm-debug.log           // 忽略 npm-debug.log 
        yarn-error.log          // 忽略 yarn-error.log
    
  • 好了,就介绍到这里,希望可以帮到有需要的朋友,需要学习更多关于 git 的内容,可以到 git介绍与使用 查看学习。

自定义详情cell


  • 到这边可能有人会想,前面不是已经自定义了 cell 了,为什么不直接在前面自定义的 cell 里面再添加一些操作,使所有的 cell 共用同一套组件?其实考虑到下面几点原因:

    • 可以看到 半小时热门的cell三大模块的cell 区别在于少了 优惠平台和数据提供平台 这一栏的2个控件,其他地方是一样的,如果我们做到一起那也是可以的,但是这样会造成一个组件里面担负过多业务逻辑,在 数据量少 是没关系,但是 数据量一多 那么需要渲染的成本就会增加,也就会严重影响到性能。

    • 如果我们分开使用,那么只是增加了组件,而且组件内部处理的业务逻辑变少了,这样也减少了我们后期的维护成本。

    • 从结构上来说,这样也更为的清晰,减少开发人员之间的沟通成本。

  • 首先,还是一样,我们先来创建 GDCommunalCell 文件,并且我们将前面 自定义cell 里面的内容 copy 一下,放到这个文件中,并进行相应修改:

        export default class GDCommunalCell extends Component {
        
            static propTypes = {
                image:PropTypes.string,
                title:PropTypes.string,
                mall:PropTypes.string,
                pubTime:PropTypes.string,
                fromSite:PropTypes.string,
            };
        
            renderDate(pubTime, fromSite) {
        
                // 时间差的计算
                let minute = 1000 * 60;     // 1分钟
                let hour = minute * 60;     // 1小时
                let day = hour * 24;        // 1天
                let week = day * 7;         // 1周
                let month = day * 30;       // 1个月
        
                // 计算时间差
                let now = new Date().getTime();     // 获取当前时间
                let diffValue = now - Date.parse(pubTime.replace(/-/gi, "/"));
        
                if (diffValue < 0) return;
        
                let monthC = diffValue/month;   // 相差了几个月
                let weekC = diffValue/week;     // 相差几周
                let dayC = diffValue/day;       // 相差几天
                let hourC = diffValue/hour      // 相差几小时
                let minuteC = diffValue/minute; // 相差几分钟
        
                let result;
        
                if (monthC >= 1) {
                    result = parseInt(monthC) + "月前";
                }else if (weekC >= 1) {
                    result = parseInt(weekC) + "周前";
                }else if (dayC >= 1) {
                    result = parseInt(dayC) + "天前";
                }else if (hourC >= 1) {
                    result = parseInt(hourC) + "小时前";
                }else if (minuteC >= 1) {
                    result = parseInt(minuteC) + "分钟前";
                }else result = "刚刚";
        
                return result + ' · ' + fromSite;
        
            }
        
            render() {
                return (
                    <View style={styles.container}>
                        {/* 左边图片 */}
                        <Image source={{uri:this.props.image === '' ? 'defaullt_thumb_83x83' : this.props.image}} style={styles.imageStyle} />
                        {/* 中间 */}
                        <View style={styles.centerViewStyle}>
                            {/* 标题 */}
                            <View>
                                <Text numberOfLines={3} style={styles.titleStyle}>{this.props.title}</Text>
                            </View>
                            {/* 详情 */}
                            <View style={styles.detailViewStyle}>
                                {/* 平台 */}
                                <Text style={styles.detailMallStyle}>{this.props.mall}</Text>
                                {/* 时间 + 来源 */}
                                <Text style={styles.timeStyle}>{this.renderDate(this.props.pubTime, this.props.fromSite)}</Text>
                            </View>
        
                        </View>
                        {/* 右边的箭头 */}
                        <Image source={{uri:'icon_cell_rightArrow'}} style={styles.arrowStyle} />
                    </View>
                );
            }
        }
        
        const styles = StyleSheet.create({
            container: {
                flexDirection:'row',
                alignItems:'center',
                justifyContent:'space-between',
                backgroundColor:'white',
                height:100,
                width:width,
                borderBottomWidth:0.5,
                borderBottomColor:'gray',
                marginLeft:15
        
            },
        
            imageStyle: {
                width:70,
                height:70,
            },
        
            centerViewStyle: {
                height:70,
                justifyContent:'space-around',
            },
        
            titleStyle: {
                width:width * 0.65,
            },
        
            detailViewStyle: {
                flexDirection:'row',
                justifyContent:'space-between',
                alignItems:'center'
            },
            detailMallStyle: {
                fontSize:12,
                color:'green',
            },
            timeStyle: {
                fontSize:12,
                color:'gray',
            },
        
            arrowStyle: {
                width:10,
                height:10,
                marginRight:30,
            }
        });
    
    
  • OK,这边完成了,我们到首页中试一下是不是好使的。

详情页.gif

译注:

  • 这边需要注意的是时间的转化,方式有很多,这边就以最直接的方式来计算。

  • 还有需要注意的是在 JAVA 中,获取到的月份是和我们现在的月份少 1个月的,这是因为 JAVA 的月份是从 0 开始,Javascript 也是一样的。

小时风云榜


  • 这个模块和首页、海淘请求数据方面是类似的,不同在于这里需要我们根据不同的时间段来进行相对应的请求,这边参考视频吧,直接上完整代码。

  • 还是 copy 首页或者海淘的代码,修改请求这部分代码:

    export default class GDHourList extends Component {
    
        // 构造
        constructor(props) {
            super(props);
            // 初始状态
            this.state = {
                dataSource: new ListView.DataSource({rowHasChanged:(r1, r2) => r1 !== r2}),
                loaded:false,
                prompt:'',
            };
    
            this.nexthourhour = '';
            this.nexthourdate = '';
            this.lasthourhour = '';
            this.lasthourdate = '';
            this.loadData = this.loadData.bind(this);
        }
    
        // 加载最新数据网络请求
        loadData(resolve, date, hour) {
            let params = {};
    
            if (date) {
                params = {
                    "date" : date,
                    "hour" : hour
                }
            }
    
            HTTPBase.get('http://guangdiu.com/api/getranklist.php', params)
                .then((responseData) => {
    
                    // 重新渲染
                    this.setState({
                        dataSource: this.state.dataSource.cloneWithRows(responseData.data),
                        loaded:true,
                        prompt:responseData.displaydate + responseData.rankhour + '点档' + '(' + responseData.rankduring + ')'
                    });
    
                    // 关闭刷新动画
                    if (resolve !== undefined){
                        setTimeout(() => {
                            resolve();
                        }, 1000);
                    }
    
                    // 暂时保留一些数据
                    this.nexthourhour = responseData.nexthourhour;
                    this.nexthourdate = responseData.nexthourdate;
                    this.lasthourhour = responseData.lasthourhour;
                    this.lasthourdate = responseData.lasthourdate;
                })
                .catch((error) => {
    
                })
        }
    
        // 跳转到设置
        pushToSettings() {
            this.props.navigator.push({
                component:Settings,
            })
        }
    
        // 返回中间标题
        renderTitleItem() {
            return(
                <Image source={{uri:'navtitle_rank_106x20'}} style={styles.navbarTitleItemStyle} />
            );
        }
    
        // 返回右边按钮
        renderRightItem() {
            return(
                <TouchableOpacity
                    onPress={()=>{this.pushToSettings()}}
                >
                    <Text style={styles.navbarRightItemStyle}>设置</Text>
                </TouchableOpacity>
            );
        }
    
        // 根据网络状态决定是否渲染 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}
                    />
                );
            }
        }
    
        // 跳转到详情页
        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)}
                >
                    <CommunalCell
                        image={rowData.image}
                        title={rowData.title}
                        mall={rowData.mall}
                        pubTime={rowData.pubtime}
                        fromSite={rowData.fromsite}
                    />
                </TouchableOpacity>
            );
        }
    
        componentDidMount() {
            this.loadData();
        }
    
        lastHour() {
            this.loadData(undefined, this.lasthourdate, this.lasthourhour);
        }
    
        nextHour() {
            this.loadData(undefined, this.nexthourdate, this.nexthourhour);
        }
    
        render() {
            return (
                <View style={styles.container}>
                    {/* 导航栏样式 */}
                    <CommunalNavBar
                        titleItem = {() => this.renderTitleItem()}
                        rightItem = {() => this.renderRightItem()}
                    />
    
                    {/* 提醒栏 */}
                    <View style={styles.promptViewStyle}>
                        <Text>{this.state.prompt}</Text>
                    </View>
    
                    {/* 根据网络状态决定是否渲染 listview */}
                    {this.renderListView()}
    
                    {/* 操作栏 */}
                    <View style={styles.operationViewStyle}>
                        <TouchableOpacity
                            onPress={() => this.lastHour()}
                        >
                            <Text style={{marginRight:10, fontSize:17, color:'green'}}>{"< " + "上1小时"}</Text>
                        </TouchableOpacity>
    
                        <TouchableOpacity
                            onPress={() => this.nextHour()}
                        >
                            <Text style={{marginLeft:10, fontSize:17, color:'green'}}>{"下1小时" + " >"}</Text>
                        </TouchableOpacity>
                    </View>
                </View>
            );
        }
    }
    
    const styles = StyleSheet.create({
        container: {
            flex: 1,
            alignItems: 'center',
            backgroundColor: 'white',
        },
    
        navbarTitleItemStyle: {
            width:106,
            height:20,
            marginLeft:50
        },
        navbarRightItemStyle: {
            fontSize:17,
            color:'rgba(123,178,114,1.0)',
            marginRight:15,
        },
    
        promptViewStyle: {
            width:width,
            height:44,
            alignItems:'center',
            justifyContent:'center',
            backgroundColor:'rgba(251,251,251,1.0)',
        },
    
        operationViewStyle: {
            width:width,
            height:44,
            flexDirection:'row',
            justifyContent:'center',
            alignItems:'center',
        },
    });
小时风云榜.gif

首页筛选功能


  • 从图中,我们可以看出筛选的下拉菜单类似 九宫格,这个我们在 React-Native 之 ListView使用 中有这样的案例,不清楚的可以再回去看一下,所以这边我们也使用 ListView 实现。

  • 在做之前,我们需要先配置一下,将压缩包内的 HomeSiftData文件HTSiftData 文件放到工程内。

  • 接着我们就要来完成这个筛选组件,代码如下:

    export default class GDCommunalSiftMenu extends Component {
    
        static defaultProps = {
            removeModal:{},
            loadSiftData:{}
        };
    
        static propTypes = {
            data:PropTypes.array,
        };
    
        // 构造
          constructor(props) {
            super(props);
            // 初始状态
            this.state = {
                dataSource: new ListView.DataSource({rowHasChanged:(r1, r2) => r1 !== r2})
            };
          }
    
        // 退出
        popToHome(data) {
            this.props.removeModal(data);
        }
    
        // 点击事件
        siftData(mall, cate) {
            this.props.loadSiftData(mall, cate);
            this.popToHome(false);
        }
    
        // 处理数据
        loadData() {
            let data = [];
    
            for (let i = 0; i<this.props.data.length; i++) {
                data.push(this.props.data[i]);
            }
    
            // 重新渲染
            this.setState({
                dataSource: this.state.dataSource.cloneWithRows(data),
            })
        }
    
        renderRow(rowData) {
            return(
                <View style={styles.itemViewStyle}>
                    <TouchableOpacity
                        onPress={() => this.siftData(rowData.mall, rowData.cate)}
                    >
                        <View style={styles.itemViewStyle}>
                            <Image source={{uri:rowData.image}} style={styles.itemImageStyle} />
                            <Text>{rowData.title}</Text>
                        </View>
                    </TouchableOpacity>
                </View>
            )
        }
    
        componentDidMount() {
            this.loadData();
        }
    
        render() {
            return(
                <TouchableOpacity
                    onPress={() => this.popToHome(false)}
                    activeOpacity={1}
                >
                    <View style={styles.container}>
                        {/* 菜单内容 */}
                        <ListView
                            dataSource={this.state.dataSource}
                            renderRow={this.renderRow.bind(this)}
                            contentContainerStyle={styles.contentViewStyle}
                            initialListSize={16}
                        />
                    </View>
                </TouchableOpacity>
            )
        }
    }
    
    const styles = StyleSheet.create({
        container: {
            width:width,
            height:height
        },
    
        contentViewStyle: {
            flexDirection:'row',
            flexWrap:'wrap',
            width: width,
            top:Platform.OS === 'ios' ? 64 : 44,
        },
    
        itemViewStyle: {
            width:width * 0.25,
            height:70,
            backgroundColor:'rgba(249,249,249,1.0)',
            justifyContent:'center',
            alignItems:'center'
        },
    
        itemImageStyle: {
            width:40,
            height:40
        }
    });
  • 我们点击某个平台,就要进行相应的请求,然后重新渲染 首页的ListView ,方式如下:
    // 加载最新数据网络请求
    loadSiftData(mall, cate) {

        let params = {};

        if (mall === "" && cate === "") {   // 全部
            this.loadData(undefined);
            return;
        }

        if (mall === "") {  // cate 有值
            params = {
                "cate" : cate,
                "country" : "us"
            };
        }else {
            params = {
                "mall" : mall,
                "country" : "us"
            };
        }


        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,
                });

                // 存储数组中最后一个元素的id
                let cnlastID = responseData.data[responseData.data.length - 1].id;
                AsyncStorage.setItem('cnlastID', cnlastID.toString());

            })
            .catch((error) => {

            })
    }
  • 至此,首页的筛选功能也完成了,在 海淘模块 内也使用一下就可以了。
筛选功能.gif

搜索模块


  • 点击 首页或者海淘 右侧按钮,我们跳转到搜索模块,解析图奉上:

  • 根据解析图我们添加相应子组件。

        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.changeText = '';
                this.loadData = this.loadData.bind(this);
                this.loadMore = this.loadMore.bind(this);
            }
        
            // 加载最新数据网络请求
            loadData(resolve) {
        
                if (!this.changeText) return;
        
                let params = {
                    "q" : this.changeText
                };
        
                HTTPBase.get('http://guangdiu.com/api/getresult.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 searchLastID = responseData.data[responseData.data.length - 1].id;
                        AsyncStorage.setItem('searchLastID', searchLastID.toString());
        
                    })
                    .catch((error) => {
        
                    })
            }
        
            // 加载更多数据的网络请求
            loadMoreData(value) {
        
                let params = {
                    "q" : this.changeText,
                    "sinceid" : value
                };
        
                HTTPBase.get('http://guangdiu.com/api/getresult.php', params)
                    .then((responseData) => {
        
                        // 拼接数据
                        this.data = this.data.concat(responseData.data);
        
                        this.setState({
                            dataSource: this.state.dataSource.cloneWithRows(this.data),
                            loaded:true,
                        });
        
                        // 存储数组中最后一个元素的id
                        let searchLastID = responseData.data[responseData.data.length - 1].id;
                        AsyncStorage.setItem('searchLastID', searchLastID.toString());
                    })
                    .catch((error) => {
        
                    })
            }
        
            // 加载更多数据操作
            loadMore() {
                // 读取id
                AsyncStorage.getItem('searchLastID')
                    .then((value) => {
                        // 数据加载操作
                        this.loadMoreData(value);
                    })
        
            }
        
            // 返回
            pop() {
                // 回收键盘
                dismissKeyboard();
        
                this.props.navigator.pop();
            }
        
            // 返回左边按钮
            renderLeftItem() {
                return(
                    <TouchableOpacity
                        onPress={() => {this.pop()}}
                    >
                        <View style={{flexDirection:'row', alignItems:'center'}}>
                            <Image source={{uri:'back'}} style={styles.navbarLeftItemStyle} />
                            <Text>返回</Text>
                        </View>
        
                    </TouchableOpacity>
                );
            }
        
            // 返回中间按钮
            renderTitleItem() {
                return(
                    <Text style={styles.navbarTitleItemStyle}>搜索全网折扣</Text>
                );
            }
        
            // 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)}
                    >
                        <CommunalCell
                            image={rowData.image}
                            title={rowData.title}
                            mall={rowData.mall}
                            pubTime={rowData.pubtime}
                            fromSite={rowData.fromsite}
                        />
                    </TouchableOpacity>
                );
            }
        
            render() {
                return (
                    <View style={styles.container}>
                        {/* 导航栏样式 */}
                        <CommunalNavBar
                            leftItem = {() => this.renderLeftItem()}
                            titleItem = {() => this.renderTitleItem()}
                        />
        
                        {/* 顶部工具栏 */}
                        <View style={styles.toolsViewStyle} >
                            {/* 左边 */}
                            <View style={styles.inputViewStyle} >
                                <Image source={{uri:'search_icon_20x20'}} style={styles.searchImageStyle} />
                                <TextInput
                                    style={styles.textInputStyle}
                                    keyboardType="default"
                                    placeholder="请输入搜索商品关键字"
                                    placeholderTextColor='gray'
                                    autoFocus={true}
                                    clearButtonMode="while-editing"
                                    onChangeText={(text) => {this.changeText = text}}
                                    onEndEditing={() => this.loadData()}
                                />
                            </View>
        
                            {/* 右边 */}
                            <View style={{marginRight:10}}>
                                <TouchableOpacity
                                    onPress={() => this.pop()}
                                >
                                    <Text style={{color:'green'}}>取消</Text>
                                </TouchableOpacity>
                            </View>
                        </View>
        
                        {/* 根据网络状态决定是否渲染 listview */}
                        {this.renderListView()}
                    </View>
                );
            }
        }
        
        const styles = StyleSheet.create({
            container: {
                flex: 1,
                alignItems: 'center',
                backgroundColor: 'white',
            },
        
            navbarLeftItemStyle: {
                width:20,
                height:20,
                marginLeft:15,
            },
            navbarTitleItemStyle: {
                fontSize:17,
                color:'black',
                marginRight:50
            },
            navbarRightItemStyle: {
                width:20,
                height:20,
                marginRight:15,
            },
        
            toolsViewStyle: {
                width:width,
                height:44,
                flexDirection:'row',
                alignItems:'center',
                justifyContent:'space-between',
            },
        
            inputViewStyle: {
                height:35,
                flexDirection:'row',
                alignItems:'center',
                justifyContent:'center',
                backgroundColor:'rgba(239,239,241,1.0)',
                marginLeft:10,
                borderRadius:5
            },
            searchImageStyle: {
                width:15,
                height:15,
                marginLeft:8
            },
            textInputStyle: {
                width:width * 0.75,
                height:35,
                marginLeft:8
            },
        
            listViewStyle: {
            width:width,
            },
        });
    
    
搜索页面.gif

设置


  • 小时风云榜 模块,我们还有设置模块没有做,这边也快速来做一下

  • 从图中可以看出,这又是不一样的 cell样式 ,不过通过前面的经验,知道怎么来自定义了吧:

        export default class GDSettingsCell extends Component {
    
            static propTypes = {
                leftTitle:PropTypes.string,
                isShowSwitch:PropTypes.bool,
            };
        
            // 构造
              constructor(props) {
                super(props);
                // 初始状态
                this.state = {
                    isOn:false,
                };
              }
        
            // 返回需要的组件
            renderRightContent() {
                let component;
        
                if (this.props.isShowSwitch) {  // 显示 Switch 按钮
        
                    component = <Switch value={this.state.isOn} onValueChange={() => {this.setState({isOn: !this.state.isOn})}} />
                }else {
                    component = <Image source={{uri:'icon_cell_rightArrow'}} style={styles.arrowStyle} />
                }
        
                return(
                    component
                )
            }
        
            render() {
                return(
                    <View style={styles.container}>
                        {/* 左边 */}
                        <View>
                            <Text>{this.props.leftTitle}</Text>
                        </View>
        
                        {/* 右边 */}
                        <View style={styles.rightViewStyle}>
                            {this.renderRightContent()}
                        </View>
                    </View>
                )
            }
        }
        
        const styles = StyleSheet.create({
            container: {
                flex:1,
                flexDirection:'row',
                height:Platform.OS === 'ios' ? 44 : 36,
                justifyContent:'space-between',
                alignItems:'center',
                borderBottomColor:'gray',
                borderBottomWidth:0.5,
                marginLeft:15,
            },
        
            rightViewStyle:{
                marginRight:15,
            },
        
            arrowStyle: {
                width:10,
                height:10,
            }
        });
    
  • 自定义完成,来试下好不好用:

        export default class GDSettings extends Component {
            // 返回
            pop() {
                this.props.navigator.pop();
            }
        
            // 返回左边按钮
            renderLeftItem() {
                return(
                    <TouchableOpacity
                        onPress={() => {this.pop()}}
                    >
                        <View style={{flexDirection:'row', alignItems:'center'}}>
                            <Image source={{uri:'back'}} style={styles.navbarLeftItemStyle} />
                            <Text>返回</Text>
                        </View>
        
                    </TouchableOpacity>
                );
            }
        
            // 返回中间按钮
            renderTitleItem() {
                return(
                    <Text style={styles.navbarTitleItemStyle}>设置</Text>
                );
            }
        
            render() {
                return(
                    <View style={styles.container}>
                        {/* 导航栏样式 */}
                        <CommunalNavBar
                            leftItem = {() => this.renderLeftItem()}
                            titleItem = {() => this.renderTitleItem()}
                        />
        
                        {/* 内容 */}
                        <ScrollView
                            style={styles.scollViewStyle}
                        >
                            {/* 第一个cell */}
                            <SettingsCell
                                leftTitle="淘宝天猫快捷下单"
                                isShowSwitch={true}
                            />
        
                            {/* 第二个cell */}
                            <SettingsCell
                                leftTitle="清理图片缓存"
                                isShowSwitch={false}
                            />
                        </ScrollView>
                    </View>
                )
            }
        }
        
        const styles = StyleSheet.create({
            container: {
                flex:1
            },
        
            navbarLeftItemStyle: {
                width:20,
                height:20,
                marginLeft:15,
            },
        
            navbarTitleItemStyle: {
                fontSize:17,
                color:'black',
                marginRight:50
            },
        
            scollViewStyle: {
                backgroundColor:'white',
            },
        });
    
设置.gif
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容