【稀飯】react native 實戰系列教程之完成首頁

首頁功能

前面,我們已經完成了影視信息組件的開發,接下來,我們要用該組件來完成首頁界面功能的開發,如下圖

首頁

可以看到,首頁頂部一個標題欄,下面是‘最新’、‘最熱’兩個選項卡。我們要完成的有標題欄、選項卡、以及選項卡切換的內容。

標題欄

這裏的標題欄,我們使用的是ToolbarAndroid,看名稱我們就知道這個是android下特有的組件view,所以就立馬想到,這個組件是ios、android不能通用的。因此,我們定義一個TitleBarComponent,方便以後重複使用和ios適配。這裏,先提一下關於組件適配的一些問題。

組件平臺適配

不同平臺使用不同的組件,React Native 提供了以下四種解決方案

  • 最直接的方案就是把組件放置到不同的文件夾下:
/common/components/   
/android/components/   
/ios/components/
  • 根據平臺不同在組件的文件命名上加以區分,如下:
BigButtonIOS.js
BigButtonAndroid.js
  • 使用擴展名
BigButton.ios.js
BigButton.android.js

以上三種方案,再引用的時候去掉平臺標識,如下

import BigButton from './components/BigButton';
  • Platform.select()
import React, {Component,Platform} from 'react';

var Component = Platform.select({
  ios: () => require('ComponentIOS'),
  android: () => require('ComponentAndroid'),
});

Platform.OS在iOS上會返回ios,而在Android設備或模擬器上則會返回android。

創建標題欄

根據上面的方案,我們這裏使用的是使用擴展名的方案來適配平臺的。在js/component下創建TitleBarComponent.android.js文件。

標題欄總共有標題、副標題和左邊的返回按鈕icon,返回按鈕只有在子頁面(二級頁面)纔有,因此我們定義如下屬性

//初始化props
static defaultProps = {
    title:'',//標題
    subtitle:'',//副標題
    subScene:true,//是否是子頁面
};

然後,在render返回一個ToolbarAndroid

render() {
    return(
        <ToolbarAndroid 
            title={this.props.title}
            navIcon={this.props.subScene?require('../../img/ic_actionbar_back.png'):null}
            titleColor='white'
            subtitle={this.props.subtitle}
            subtitleColor='#ebf0f6'
            actions={actions}
            onActionSelected={this._onActionClick.bind(this)}
            onIconClicked={this._onIconClick.bind(this)}
            style={styles.toolbar}
        />
        );
    }

    //返回按鈕事件
    _onIconClick(){

    }

這裏幾個屬性說明下

  • title 就是標題
  • titleColor 設置標題顏色
  • subtitle 就是副標題
  • subtitleColor 設置副標題顏色
  • actions 瞭解android的都知道Toolbar右邊還可以設置一些動作按鈕(我們這裏沒有就不設置該屬性)

它的格式如下,可以設置多個

const actions = [
    {title:'全部',show:'always',icon:require('../../img/icon_all.png'),showWithText:true},
]
  • onActionSelected 動作按鈕被觸發時的回調(我們這裏沒有就不設置該屬性)
  • onIconClicked 標題欄左邊的圖標被點擊後的回調(我們這裏是返回按鈕,返回圖標可以到github上得到)
  • style 設置整個標題欄的樣式,高度、背景等。

TitleBarComponent的完整代碼如下

import React,{Component} from 'react';
import {
    ToolbarAndroid,
    DeviceEventEmitter,
    StyleSheet,
    Text,
    TouchableOpacity,
}from 'react-native';

export default class TitleBarComponent extends Component {
    constructor(props) {
        super(props);
    }

    //初始化props
    static defaultProps = {
        title:'',//標題
        subtitle:'',//副標題
        subScene:true,//是否是子頁面
    };

    render() {
        return(
            <ToolbarAndroid
                title={this.props.title}
                navIcon={this.props.subScene?require('../../img/ic_actionbar_back.png'):null}
                titleColor='white'
                subtitle={this.props.subtitle}
                subtitleColor='#ebf0f6'
                onIconClicked={this._onIconClick.bind(this)}
                style={styles.toolbar}
            />
        );
    }

    //返回按鈕事件
    _onIconClick(){

    }
}

const styles = StyleSheet.create({
    toolbar:{
        height:56,
        backgroundColor:'#ff5722',
    },
});

這樣我們就完成了標題欄的設計。

創建首頁Scene

添加標題

接下來我們需要創建一個首頁Scene,來展示首頁功能。在js文件夾新建HomeScene.js文件,併爲首頁添加一個標題欄。

import React,{Component} from 'react';
import {
    View,
    Text,
    StyleSheet,
    TouchableOpacity
} from 'react-native';

import TitleBar from './component/TitleBarComponent'

export default class HomeScene extends Component{
    constructor(props){
        super(props);
    }

    render(){
        return(
            <View style={{flex:1}}>
                <TitleBar title="首頁" subtitle="看韓劇,上稀飯" subScene={false}/>
            </View>
        );
    };
}

然後將index.android.js使用HomeScene

import HomeScene from './js/HomeScene';

class XiFan extends Component {

  render(){
    return(
        <HomeScene/>
    );
  }
}

AppRegistry.registerComponent('XiFan', () => XiFan);

執行代碼,就可以看到如下效果

首頁

添加選項卡

這樣首頁的標題有了,我們接下來要添加‘最新’、‘最熱’兩個選項卡

在HomeScene內添加如下代碼

state增加一個tabIndex屬性,標識當前選中的tab項

constructor(props){
    super(props);
    this.state = {
        tabIndex:0,
    };
}

接着就是繪製Tab了

//tab切換
_onTabPress(index){
    this.setState({
        tabIndex:index,
    });
}

render(){
    return(
        <View style={{flex:1}}>
            <TitleBar title="首頁" subtitle="看韓劇,上稀飯" subScene={false}/>
            <View style={{height:35,flexDirection:'row',justifyContent:'center',alignItems:'center',backgroundColor:'#ff5722'}}>
                <View style={{flex:1}}>
                    <TouchableOpacity style={{flex:1,justifyContent:'center'}} activeOpacity={0.6} onPress={this._onTabPress.bind(this,0)}>
                        <Text style={this.state.tabIndex===0?styles.TabSelect:styles.TabUnSelect}>最新</Text>
                    </TouchableOpacity>
                    <View style={this.state.tabIndex===0?styles.TabUnderlineSelect:styles.TabUnderlineUnSelect}/>
                </View>
                <View style={{flex:1}}>
                    <TouchableOpacity style={{flex:1,justifyContent:'center'}} activeOpacity={0.6} onPress={this._onTabPress.bind(this,1)}>
                        <Text style={this.state.tabIndex===0?styles.TabUnSelect:styles.TabSelect}>最熱</Text>
                    </TouchableOpacity>
                    <View style={this.state.tabIndex===0?styles.TabUnderlineUnSelect:styles.TabUnderlineSelect}/>
                </View>
            </View>
        </View>
    );
};

該段代碼的核心主要是根據tabIndex是否被選中項,動態修改View的樣式

var styles = StyleSheet.create({
    TabSelect:{
        flex:1,
        textAlign:'center',
        color:'white',
    },
    TabUnderlineSelect:{
        backgroundColor:'white',
        height:2,
    },
    TabUnSelect:{
        flex:1,
        textAlign:'center',
        color:'#d5d5d5',
    },
    TabUnderlineUnSelect:{
        height:0,
    },
});

現在的效果是這樣的

首頁

添加選項卡內容

應用的功能就是像在堆積木一樣,一點一點疊起來。現在給選項卡下方添加對應的內容。選項卡切換時,底下切換到對應的內容,我們這裏使用的是ViewPagerAndroid。

//ViewPager 頁面發生切換時調用,修改tabIndex
_onPageSelected(event){
    const position = event.nativeEvent.position;
    this.setState({
        tabIndex:position,
    });
}
_onPageScrollStateChanged(status){
    //idle 空閒,意味着當前沒有交互。

    //dragging 拖動中,意味着當前頁面正在被拖動。

    //settling 處理中,意味着當前頁面發生過交互,且正在結束開頭或收尾的動畫。
}

render(){
    return(
        <View style={{flex:1}}>
            ...//省略其它代碼

            <ViewPagerAndroid
                style={{flex:1}}
                initialPage={0}
                onPageSelected={this._onPageSelected.bind(this)}
                scrollEnabled={true}
                pageMargin={0}
                onPageScrollStateChanged={this._onPageScrollStateChanged}
                keyboardDismissMode='on-drag'
                ref={(viewPager)=>{this.viewPager = viewPager}}
            >
                <View style={{flex:1}}>
                    <DramaComponent url='/hanju/new/'/>
                </View>
                <View style={{flex:1}}>
                    <DramaComponent url='/hanju/renqi/'/>
                </View>
            </ViewPagerAndroid>
        </View>
    );
};

主要說幾個屬性

  • initialPage 初始顯示哪個頁面
  • onPageSelected頁面選中時的回調函數
  • onPageScrollStateChanged 滾動狀態發生變化時調用(目前沒用到)
  • ref 定義該組件的實例對象,這裏我們將ViewPagerAndroid實例對象聲明爲viewPager,然後我們就可以在這個頁面內使用該對象,比如,前面的_onTabPress方法,在tab切換時需要下面的內容也切換到對應的內容,所以我們對_onTabPress方法添加如下代碼:
//tab切換
_onTabPress(index){
    this.viewPager.setPage(index);
    this.setState({
        tabIndex:index,
    });
}

調用了viewPager對象setPage方法,進行頁面切換。

還有另一種定義ref方式,如下:

<ViewPagerAndroid 
    ...//省略其它代碼
    ref="viewPage">
    ...//省略其它代碼
</ViewPagerAndroid>

然後使用對象時

//tab切換
_onTabPress(index){
    this.refs.viewPage.setPage(index);
    this.setState({
        tabIndex:index,
    });
}

再看下上面的代碼,我們在ViewPagerAndroid內部塞了兩個View,這兩個View實際上就是要顯示的內容了,它們就是我們之前自定義的DramaComponent,傳入了不同的url,一個是最新的地址,一個是最熱的人氣,這樣解析顯示出來就是對應的數據了。

關於ViewPagerAndroid更多信息,可以查看ViewPagerAndroid

最後上一下本節的完成的成果效果圖:

效果圖

HomeScene.js的所有代碼

import React,{Component} from 'react';
import {
    View,
    Text,
    StyleSheet,
    TouchableOpacity,
    ViewPagerAndroid
} from 'react-native';

import TitleBar from './component/TitleBarComponent'
import DramaComponent from './component/DramaComponent';

export default class HomeScene extends Component{
    constructor(props){
        super(props);
        this.state = {
            tabIndex:0,
        };
    }

    //tab切換
    _onTabPress(index){
        this.viewPager.setPage(index);
        this.setState({
            tabIndex:index,
        });
    }
    //ViewPager 頁面發生切換時調用
    _onPageSelected(event){
        const position = event.nativeEvent.position;
        this.setState({
            tabIndex:position,
        });
    }

    _onPageScrollStateChanged(status){
        //idle 空閒,意味着當前沒有交互。

        //dragging 拖動中,意味着當前頁面正在被拖動。

        //settling 處理中,意味着當前頁面發生過交互,且正在結束開頭或收尾的動畫。
    }

    render(){
        return(
            <View style={{flex:1}}>
                <TitleBar title="首頁" subtitle="看韓劇,上稀飯" subScene={false}/>
                <View style={{height:35,flexDirection:'row',justifyContent:'center',alignItems:'center',backgroundColor:'#ff5722'}}>
                    <View style={{flex:1}}>
                        <TouchableOpacity style={{flex:1,justifyContent:'center'}} activeOpacity={0.6} onPress={this._onTabPress.bind(this,0)}>
                            <Text style={this.state.tabIndex===0?styles.TabSelect:styles.TabUnSelect}>最新</Text>
                        </TouchableOpacity>
                        <View style={this.state.tabIndex===0?styles.TabUnderlineSelect:styles.TabUnderlineUnSelect}/>
                    </View>
                    <View style={{flex:1}}>
                        <TouchableOpacity style={{flex:1,justifyContent:'center'}} activeOpacity={0.6} onPress={this._onTabPress.bind(this,1)}>
                            <Text style={this.state.tabIndex===0?styles.TabUnSelect:styles.TabSelect}>最熱</Text>
                        </TouchableOpacity>
                        <View style={this.state.tabIndex===0?styles.TabUnderlineUnSelect:styles.TabUnderlineSelect}/>
                    </View>
                </View>

                <ViewPagerAndroid
                    style={{flex:1}}
                    initialPage={0}
                    onPageSelected={this._onPageSelected.bind(this)}
                    scrollEnabled={true}
                    pageMargin={0}
                    onPageScrollStateChanged={this._onPageScrollStateChanged}
                    keyboardDismissMode='on-drag'
                    ref={(viewPager)=>{this.viewPager = viewPager}}
                >
                    <View style={{flex:1}}>
                        <DramaComponent url='/hanju/new/'/>
                    </View>
                    <View style={{flex:1}}>
                        <DramaComponent url='/hanju/renqi/'/>
                    </View>
                </ViewPagerAndroid>
            </View>
        );
    };
}
var styles = StyleSheet.create({
    TabSelect:{
        flex:1,
        textAlign:'center',
        color:'white',
    },
    TabUnderlineSelect:{
        backgroundColor:'white',
        height:2,
    },
    TabUnSelect:{
        flex:1,
        textAlign:'center',
        color:'#d5d5d5',
    },
    TabUnderlineUnSelect:{
        height:0,
    },
});

總結

這節,我們完成了首頁功能的開發,主要涉及到了標題欄、選項卡切換的功能,細節上提到了組件的平臺適配、ViewPager的使用等。下一節,我們將開發主界面的功能,包括底部TabBar的開發以及Navigator實現頁面的跳轉,而頁面的跳轉將是最主要的一個功能,下一節我們再詳細來述說。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章