React-Native 之 項目實戰(三)

前言


  • 本文有配套視頻,能夠酌情觀看。
  • 文中內容因各人理解不一樣,可能會有所誤差,歡迎朋友們聯繫我。
  • 文中全部內容僅供學習交流之用,不可用於商業用途,如所以引發的相關法律法規責任,與我無關。
  • 如文中內容對您形成不便,煩請聯繫 277511806@qq.com 處理,謝謝。
  • 轉載麻煩註明出處,謝謝。
  • 本篇資源:連接: https://pan.baidu.com/s/1jIbW2n8 密碼: wqe4php

  • 從這篇開始咱們就將源碼託管到 github 上,須要源碼的 點我下載,喜歡的話記得 Star,謝謝!html

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使用


  • 項目的版本管理也是程序猿必須具有的一項技能,它可以讓咱們避免許多開發中遇到的尷尬問題。java

  • 公司裏面通常使用 SVNGit 兩種,而如今 Git 的份額逐漸在蠶食着 SVN,這邊我給你們提供了 SVNGit 的詳情版,你們能夠前往閱讀。
  • 這小結建議觀看視頻,視頻內有具體操做!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

模態跳轉.gif

  • 關於更詳細地 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) => {
    
                })
        }

加載更多.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意思就是將 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
            }
        });
  • 按照上面的方法,咱們完成一下 近半小時熱門模塊 的跳轉詳情功能。

詳情頁.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 則是會在規定的時間後 儘快 執行任務。

相關文章
相關標籤/搜索