React-Native 之 项目实战(四)

react

前言


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

  • 本篇资源:链接: https://pan.baidu.com/s/1pKActHh 密码: n6ee

  • 源码托管到 github 上,需要源码的 点我下载,喜欢的话记得 Star,谢谢!

数据持久化


  • 数据持久化是移动端的一个重要部分,刚发现 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,

    });

    })

    }

  • 到这里,我们就完成了 数据本地持久化 并且成功将数据取出,这边数据持久化的实验就完成了。

译注:

  • 有关 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,这边完成了,我们到首页中试一下是不是好使的。

译注:

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

  • 还有需要注意的是在 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',

},

});

首页筛选功能


  • 从图中,我们可以看出筛选的下拉菜单类似 九宫格,这个我们在 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) => {

})

}

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

搜索模块


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

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

    	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,

    },

    });

设置


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

  • 从图中可以看出,这又是不一样的 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',

    },

    });

以上是 React-Native 之 项目实战(四) 的全部内容, 来源链接: utcz.com/z/382349.html

回到顶部