react_native 项目实战 (5) DeviceEventEmitter 使用 ,webview 使用 react navigation进行参数传递

今天 2017.10.26 我把android studio升级成了3.0 结果RN项目 各种问题

其他问题我用android studio 3.0 把项目打开一遍就好了 但是使用rn run android的时候 有个gradle 3.0一直下载不下来

说缺少3.0

解决:

buildscript {
    repositories {
        jcenter()
        maven { url 'https://maven.google.com' }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.0'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

我是翻墙了的 不翻墙 不清楚能不能下载

分类排序数据存取

分类选择页面和首页的数据获取,存取的数据结构是:

[
  {"name":"Android","checked":true},
  {"name":"IOS","checked":false},
  {"name":"React","checked":true},
  {"name":"Java","checked":true},
  {"name":"JS","checked":true}
]

分类排序的页面存取的数据结构是

这里取出的值 checked 肯定是true

“name”:“Android”
“name”:“IOS”
“name”:“IOS”

举个例子

//原始数组
var originalArray = [
    {name:'Android',checked:true},
    {name:'IOS',checked:false},
    {name:'React',checked:true},
    {name:'Java',checked:false},
    {name:'JS',checked:true},
    {name:'Python',checked:false}
];

//排序后的数组
var sortedArray = [
    {name:'JS',checked:true},
    {name:'React',checked:true},
    {name:'Android',checked:true}
];

当分类排序过后 需要把分类排序后的数据 再次存入通用的分类数据结构.

图例:

mark

下面是js代码

/**
 * Created by liuml on 2017/10/22.
 */
import React, {Component} from 'react';
import {
    AppRegistry,
    StyleSheet,
    Text,
    View,
    Image,
    TouchableOpacity,
    TouchableHighlight,
    AsyncStorage,

} from 'react-native';

import NavigationBar from "../compoent/NavigationBar";
import SortableListView from "react-native-sortable-listview";
import popular_def_lans from "../../res/data/popular_def_lans.json"
import Toast from "react-native-easy-toast"

/**
 * 分类排序页面
 */
export default class SortKeyPage extends Component {

    // 构造
    constructor(props) {
        super(props);
        // 初始状态
        this.state = {
            originData: popular_def_lans,
            data: [],
        };
        //初始化数据 把所有选中的数据放入进去
        this.state.originData.forEach((item => {
            if (item.checked) {
                this.state.data.push(item)
            }
        }))
    }


    doBack = () => {

        this.props.navigation.goBack();
    }
    //保存
    doSave = () => {

        //原始数组
        var originArray = this.state.originData;
        //排序后的数组
        var sortedArray = this.state.data;
        //要保存的数组
        var savedArray = [];

        //i用来遍历originalArray
        //j用来遍历sortedArray
        for (var i = 0, j = 0; i < originArray.length; i++) {
            var item = originArray[i];
            if (item.checked) {
                savedArray[i] = sortedArray[j];
                j++;
            } else {
                savedArray[i] = item;
            }
        }

        console.log("保存读取: ");
        console.log(savedArray);
        AsyncStorage.setItem("custom_key", JSON.stringify(savedArray))
            .then(() => {
                this.refs.toast.show("保存成功");
                this.doBack();
            })
    }
    //保存
    handleSave = () => {
        this.doSave();
    }

    getLeftBtn = () => {
        return <View style={{flexDirection: 'row', alignItems: 'center'}}>
            <TouchableOpacity
                activeOpacity={0.7}
                onPress={this.doBack}>
                <Image source={require('../../res/images/ic_arrow_back_white_36pt.png')}
                       style={{width: 24, height: 24}}/>
            </TouchableOpacity>
        </View>;
    }


    getRightBtn = () => {
        return <View style={{flexDirection: 'row', alignItems: 'center'}}>
            <TouchableOpacity
                onPress={this.handleSave}
                activeOpacity={0.7}>
                <View style={{marginRight: 10}}>
                    <Text style={{fontSize: 16, color: '#FFF'}}>保存</Text>
                </View>
            </TouchableOpacity>
        </View>;
    }

    componentDidMount() {
        AsyncStorage.getItem("custom_key")
            .then(value => {
                console.log("进入读取: ");
                console.log(value);
                if (value != null) {
                    //只获取checked为true语言,进行排序  forEach 不会返回一个数组 而map会返回一个数组
                    let d = [];
                    let origin = JSON.parse(value);
                    origin.forEach((item) => {
                        if (item.checked) {
                            d.push(item);
                        }
                    })
                    // this.setState({data: d});
                    // var myorder = Object.keys(this.state.data); //Array of keys
                    this.setState({originData: origin, data: d});
                }
            })
    }


    render() {

        //http://www.w3school.com.cn/jsref/jsref_splice.asp   splice方法的使用
        return <View style={styles.container}>
            <NavigationBar
                title="语言分类排序"
                rightButton={this.getRightBtn()}
                leftButton={this.getLeftBtn()}/>

            <SortableListView
                data={this.state.data}
                order={Object.keys(this.state.data)}
                rowActivationTime="10"
                renderRow={row => <RowComponent data={row}/>}
                onRowMoved={e => {
                    this.state.data.splice(e.to, 0, this.state.data.splice(e.from, 1)[0]);
                    this.forceUpdate();
                }}
            />
            <Toast ref="toast"/>
        </View>
    }
}

//https://github.com/deanmcpherson/react-native-sortable-listview/blob/master/Sortable/example.js 这里是SortableListView github地址 示例代码

class RowComponent extends Component {
    static defaultProps = {
        data: {name: ""}
    };

    render() {
        return <TouchableHighlight
            underlayColor="#EEE"
            style={styles.item}
            {...this.props.sortHandlers}>
            <View style={{flexDirection: 'row', paddingLeft: 10}}>
                <Image source={require('../../res/images/ic_sort.png')} style={styles.image}/>
                <Text>{this.props.data.name}</Text>
            </View>

        </TouchableHighlight>
    }
}
const styles = StyleSheet.create({
    container: {
        flex: 1
    },
    item: {
        backgroundColor: '#F8F8F8',
        borderBottomWidth: 1,
        borderColor: '#EEE',
        height: 50,
        justifyContent: 'center'
    },
    image: {
        width: 16,
        height: 16,
        marginRight: 10,
        tintColor: '#63B8FF'
    }

});

可以看到 我主要的代码 就在返回保存的时候doSave 方法里面 进行排序存储

下面是效果:

mark

首页实时更新 使用 DeviceEventEmitter 发送接收事件

现在首页是我通过手动刷新js 才更新界面 现在需要 自动刷新

DeviceEventEmitter 使用方法

第一步 添加监听

DeviceEventEmitter.addListener(‘名称’,(events) ={使用数据events});

第二部 发送数据

DeviceEventEmitter.emit(‘自定义名称’,发送数据);

这样就可以了

那么现在我写一下 在首页刷新数据的监听

//路由
const resetAction = NavigationActions.reset({
    index: 0,
    actions: [
        NavigationActions.navigate({ routeName: 'Home'})
    ]
})

 componentDidMount() {
        this.listener = DeviceEventEmitter.addListener('HOMEPAGE_RELOAD', (n) => {
            //主页重新加载数据
            console.log("主页重新加载数据");
            //跳转到新的场景,并且重置整个路由栈
            this.props.navigation.dispatch(resetAction);

            // this.props.navigation.resetTo({
            //     component:Homepage
            // })
        })
    }


    componentWillUnmount() {
        //移除监听
        this.listener.remove();
    }

然后在保存的时候发送数据


    //保存
    handleSave = () => {
        //http://lib.csdn.net/article/reactnative/43540   JSON.stringify 字符串转JSON
        //AsyncStorage是一个简单的、异步的、持久化的Key-Value存储系统
        AsyncStorage.setItem('custom_key', JSON.stringify(this.state.data))
            .then(() => this.refs.toast.show("保存成功"));
        // console.log(JSON.stringify(this.state.data));
        this.doBack();
        DeviceEventEmitter.emit("HOMEPAGE_RELOAD","HomePage重新加载");
    }

但是发现有个bug 如果在我的页面 跳转后 返回重新加载会报错 说是和 测量的问题有关…这个就先放放.

webview使用 项目详情页面

新建ProjectDetails.js 作为项目详情页面

在PapularPage页面 点击listview 的item 跳转 把item数据内的 title 和url 传到项目详情页面


    //渲染ListView的每一行
    renderRow = (obj) => {
        return <ProjectRow item={obj} onSelect={() => this.handleProjectSelect(obj)}>
        </ProjectRow>
    }

    handleProjectSelect = (obj) => {
        navigation('ProjectDetails', {
            params: {
                title: obj.full_name,
                url: obj.html_url
            },
        });
    }

注意这里是一个比较经典的 父控件 把方法传入 子控件 子控件 回调父控件.

上面在PapularPage页面 这个onSelect 属性传入子控件ProjectRow

render() {
        var item = this.props.item;
        return <TouchableOpacity
            activeOpacity={0.5}
            onPress={this.props.onSelect}>
            <View style={styles.container}>
                <Text style={styles.title}>{item.full_name}</Text>
                <Text style={styles.description}>{item.description}</Text>
                <View style={styles.bottom}>
                    <View style={styles.bottomTextWrapper}>
                        <Text>作者:</Text>
                        {/* {console.log(item.owner.avatar_url)}*/}
                        <Image style={{width: 22, height: 22}} source={{uri: item.owner.avatar_url}}/>
                    </View>
                    <View style={styles.bottomTextWrapper}>
                        <Text>star:</Text>
                        <Text>{item.stargazers_count}</Text>
                    </View>
                    <Image source={require("../../res/images/ic_unstar_transparent.png")}
                           style={{width: 22, height: 22}}/>
                </View>
            </View>
        </TouchableOpacity>
    }

在子控件中 onPress={this.props.onSelect} 进行回调这样就有了点击事件

而且还防止了一个问题 . 我测试过 直接在外面父控件中 使用onpress调用

this.handleProjectSelect(obj)

这个方法 会直接调用 没有点击就会调用 因为 带上了括号 所有导致这个问题 .如果我使用子控件回调的方式 也就防止了这个问题…

下面写ProjectDetails.js 这个是详情页面 用了webveiw


/**
 * Created by liuml on 2017/10/26.
 */
import React, {Component} from 'react';
import {
    AppRegistry,
    StyleSheet,
    Text,
    View,
    Image,
    TouchableOpacity,
    WebView
} from 'react-native';

import NavigationBar from "../compoent/NavigationBar";


export default class ProjectDetails extends Component {

    // 构造
    constructor(props) {
        super(props);

        // 初始状态
        this.state = {
            canGoBack: false
        };
    }

    handleBack = () => {
        //如果网页能够返回 先返回网页
        if (this.state.canGoBack) {
            this.refs.webview.goBack();
        } else {
            this.doBack();
        }
    }
    doBack = () => {
        this.props.navigation.goBack();
    }
    getLeftBtn = () => {
        return <View style={{flexDirection: 'row', alignItems: 'center'}}>
            <TouchableOpacity
                activeOpacity={0.7}
                onPress={this.handleBack}>
                <Image source={require('../../res/images/ic_arrow_back_white_36pt.png')}
                       style={{width: 24, height: 24}}/>
            </TouchableOpacity>
        </View>;
    }

    //监听状态改变 是否能够返回
    handleNavStateChange = (s) => {
        this.setState({canGoBack: s.canGoBack});
    }

    render() {
        const {state} = this.props.navigation;
        let title = state.params.params.title;
        let url = state.params.params.url;
        console.log("打印" + url);
        return (
            <View style={styles.container}>
                <NavigationBar
                    title={title}
                    leftButton={this.getLeftBtn()}/>
                <WebView
                    ref="webview"
                    startInLoadingState={true}
                    source={{uri: url}}
                    onNavigationStateChange={this.handleNavStateChange}
                />
            </View>
        );
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1
    },

})


有几个点

  • 一个是使用react navigation 传递参数
  • 一个是从react navigation 接受参数
  • 一个是监听webveiw 是否可以返回

第一个点: 传递参数在上面 单独的点已经有了

第二个点: 接受参数

 const {state} = this.props.navigation;
        let title = state.params.params.title;
        let url = state.params.params.url;

从props里面取出navigation 然后取出他的state 两层params 最后才是数据

第三个点: 监听webview是否返回 这里webview有个自己的属性onNavigationStateChange 我在属性上面添加了一个方法

 //监听状态改变 是否能够返回
    handleNavStateChange = (s) => {
        this.setState({canGoBack: s.canGoBack});
    }

把是否可以返回加入state中 这样当点击返回箭头的时候可以进行判断

看下最终效果图

mark

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值