React-Native 之 项目实战(三)

前言


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

Android启动页面


  • 从上面的效果可以看出,安卓端还没有启动页面,这边我们就通过React-Native 的方式解决。

    • 思路:新建一个组件作为 Android 的启动页,index.android.js 的初始化窗口改为 Android启动页,设置定时器,使其在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} />
                );
            }
        }
    
    
Android启动页面.gif

git使用


  • 项目的版本管理也是程序猿必须具备的一项技能,它能够让我们避免许多开发中遇到的尴尬问题。

  • 公司里面一般使用 SVNGit 两种,而现在 Git 的份额逐渐在蚕食着 SVN,这边我给大家提供了 SVNGit 的详情版,大家可以前往阅读。

  • 这小结建议观看视频,视频内有具体操作!

错误修正 —— 模态


  • 以前看官方文档竟然没有发现 React-Native 提供了 model 组件,在这里给大家道个歉,以后跪着写教程,不用让我起来,反正我感觉膝盖软软的!

  • 前几天在看官方文档的时候,无意中看见 model 组件,我嘞个天,有这东西就可以减少开发中很多功能开发难度。当初怎么没发现,还傻傻地一步一步去封装这个东西 T^T,在这告诫各位,不能太粗心!

  • 这边我们就将原本 近半小时热门 这个模块的跳转模式改成 正宗的 模态,代码如下:

    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 下载地址在上面。

模态跳转.gif
  • 关于更详细地 model 使用,可以参照官方文档 model ,当然我也给各位上了这道菜 —— React-Native 之 model介绍与使用

  • 通过查看 modal 的源码,我们不难发现 —— 其实 modal 实现原理也只是使用了 绝对定位,所以如果 modal 无法满足我们的功能,我们可以使用 绝对定位 来自己实现一下类似功能。

加载更多功能完善


  • 这边我们来完善一下 加载更多功能数据 的加载,需要注意的一点就是,拼接数组需要使用 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) => {
    
                })
        }
    
加载更多.gif

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意思就是将 uri 参数传递到 CommunalDetail 组件:

        // 跳转到详情页
        pushToDetail(value) {
            this.props.navigator.push({
                component:CommunalDetail,
                params: {
                    uri: '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
            }
        });
    
  • 按照上面的方法,我们完成一下 近半小时热门模块 的跳转详情功能。

详情页.gif

海淘半小时热门


  • 近半小时热门 效果是一样的,只是请求参数变了,所以 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'
            }
        });
    
    
海淘半小时热门.gif

海淘模块


  • 我们可以发现 海淘 这一块和 首页 是类似的,只是数据请求参数不同,所以我们还是 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,
            },
        });
    
    
海淘模块.gif

获取最新数据个数功能


  • 这里需要 cnmaxidusmaxid 参数,他们分别是最新数据中第一个元素的 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);
        }
    
获取最新数据个数.gif

注:上面使用到的 setInterval 也是个定时器,和我们之前使用的 setTimeout 不同的是,setInterval 是周期定时器,比如上面时间为 30000毫秒,意思就是每过 30000毫秒 就会执行一次里面的代码。而 setTimeout 则是会在规定的时间后 尽快 执行任务。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容

  • 前言 本文有配套视频,可以酌情观看。 文中内容因各人理解不同,可能会有所偏差,欢迎朋友们联系我。 文中所有内容仅供...
    珍此良辰阅读 1,142评论 0 6
  • 前言 本文 有配套视频,可以酌情观看。 文中内容因各人理解不同,可能会有所偏差,欢迎朋友们联系我讨论。 文中所有内...
    珍此良辰阅读 2,228评论 5 9
  • 一. 简介 一个核心组件,用于高效地显示一个可以垂直滚动的变化的数据列表。最基本的使用方式就是创建一个ListVi...
    飞奔的小马阅读 1,235评论 0 2
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,633评论 18 139
  • “啦啦啦,啦啦啦,啦啦,”鬼丫哼着小曲一边走一边跳,活脱脱的萌妹子一个,只是没人见识过她愤怒的样子,真是十分的可怕...
    雨林行者阅读 407评论 1 2