今天 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}
];
当分类排序过后 需要把分类排序后的数据 再次存入通用的分类数据结构.
图例:
下面是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 方法里面 进行排序存储
下面是效果:
首页实时更新 使用 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中 这样当点击返回箭头的时候可以进行判断
看下最终效果图