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
            };
        }else {
            params = {
                "mall" : mall
            };
        }


        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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM