react-native官方文檔
react-native中文文檔html
如下步驟官網早有詳細步驟,在此沒必要贅述。
1.Chocolatey
Chocolatey的官網
使用管理員打開cmd,輸入node
@"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command "iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"
2.安裝Python 2python
在cmd中輸入react
choco install python2
輸入y,不要直接回車。android
3.安裝nodeios
choco install nodejs.install
在此以前我已經用nvm安裝過node了。git
4.安裝jdk8github
choco install jdk8
在此以前我已經去官網下載jdk了。
注意:jdk版本需爲8。shell
5.安裝Yarn、React Native的命令行工具npm
npm install -g yarn react-native-cli
設置鏡像源
yarn config set registry https://registry.npm.taobao.org --global yarn config set disturl https://npm.taobao.org/dist --global
6.安裝Android Studio
按照官網的配置進行配置便可。(雖然我以爲很麻煩>﹏<)
7.初始化項目
react-native init AwesomeProject cd AwesomeProject react-native run-android
而後bug就來了,出現紅色的提示
個人理解就是模擬器沒有關聯並啓動所致使的。(多是錯誤的)
而後打算使用Android Studio直接運行項目。
在初步安裝完成後按照官網進行配置以完成所有的安裝。
打開已存在的項目,注意選擇的是項目下的Android文件夾
稍等幾分鐘運行按鈕由灰色變成綠色,可是模擬器並無跑起來。
緣由是 「Vt-x is disabled in BIOS」
遇到問題,首先是想到偉大的網友們。
華碩主板BIOS UEFI BIOS開啓VT步驟
期間還嘗試使用其餘模擬器來跑項目,例如"夜神模擬器",可是怕領導覺得我在打遊戲,就決定仍是用Android Studio,看起來專業一點。
0.完成效果預覽
晃眼的騷粉 哇咔咔
1.爲了達到真正的入坑,而不是在坑前停留,我會故意把TodoList寫得複雜一丟丟,好比添加導航功能,實現數據存儲、組件通訊的功能等。
TodoList 流程圖:
(圖片使用processOn製做,好醜)
2.安裝所需的插件。
npm install react-native-vector-icons react-navigation --save
安卓沒有navigation,官方推薦 react-navigation,react-native-vector-icons 是一個超級好用的圖標庫,只要複製了圖標的名字就能夠隨心所欲。
3.根據本身的習慣建立文件夾,我是以下建立的:
├── android ├── ios ├── src │ ├── todolist │ │ ├── utils │ │ │ └── utils.js │ │ ├── add.js │ │ ├── all.js │ │ ├── completed.js │ │ ├── incomplete.js │ │ └── todoItem.js │ ├── index.js │ ├── router.js ├── index.js
這些是用到的文件,其餘保持生成時默認的就行了。
導入所需的文件,並編寫項目的總體結構,從代碼能夠看出,底部的導航能夠切換全部的事項,未完成的事項,已完成的事項三個頁面。樣式什麼的就自由發揮吧。
//router.js import React from 'react'; import { createStackNavigator, createBottomTabNavigator } from 'react-navigation'; import Ionicons from 'react-native-vector-icons/Ionicons'; import AllList from './todolist/all' import AddTodo from './todolist/add' import Completed from './todolist/completed' import Incomplete from './todolist/incomplete' export const TodolistTabs = createBottomTabNavigator({ All: {screen: AllList }, Incomplete: {screen: Incomplete}, Completed: {screen: Completed}, }, { navigationOptions:({ navigation }) => ({ tabBarIcon: ({ focused, tintColor}) => { const { routeName } = navigation.state; let iconName; if (routeName === 'All'){ iconName = `ios-list-box${focused ? '' : '-outline'}` } else if(routeName === 'Completed'){ iconName = `ios-flag${focused ? '' : '-outline'}` }else if(routeName === 'Incomplete'){ iconName = `ios-create${focused ? '' : '-outline'}` } return <Ionicons name={iconName} size={25} color={tintColor} /> } }), tabBarOptions: { activeTintColor: '#FF4081', inactiveTintColor: '#455A64', labelStyle:{ fontSize: 13, }, style:{ backgroundColor: '#fff', } }, }) export const TodolistStack = createStackNavigator({ TodoTab: { screen: TodolistTabs, navigationOptions:{ title:'Todo list' } }, Add: { screen: AddTodo, navigationOptions:{ title:'Add', } } }, { navigationOptions: ({ navigation }) => { return{ headerStyle:{ backgroundColor: '#FF4081', borderBottomColor: 'transparent', borderWidth: 0, elevation: 0, shadowOpacity: 0, }, headerTintColor: '#fff', headerTitleStyle: { flex: 1, textAlign: 'center' } } } })
// src/index.js import React,{Component} from 'react'; import { TodolistStack } from './router'; export default class App extends Component { render(){ return <TodolistStack /> } }
// AwesomeProject/index,js import { AppRegistry } from 'react-native'; import App from './src/index' AppRegistry.registerComponent('AwesomeProject', () => App);
import React, {Component} from 'react'; import { StyleSheet, View, FlatList, TouchableOpacity, AsyncStorage, DeviceEventEmitter } from 'react-native'; import TodoItem from './todoItem' import Ionicons from 'react-native-vector-icons/Ionicons'; import Utils from './utils/utils'; export default class AllScreen extends Component { constructor(props){ super(props); this.state = { todolistData: [], } } componentDidMount() { this._getTodolistData(); this.listener = DeviceEventEmitter.addListener('todolist_add',()=>{ this._getTodolistData(); }) } componentWillReceiveProps(){ this._getTodolistData(); } componentWillUnmount(){ if(this.listener){ this.listener.remove(); } } _getTodolistData(){ AsyncStorage.getItem('todolistData', (err, result) => { if(err){ return; } let todoListArr = (result != null) ? JSON.parse(result) :[]; this.setState({ todolistData: todoListArr }) }) } _checkTodo(item){ let todolistData = this.state.todolistData; for(let i = 0 , len = todolistData.length; i<len; i++){ if(item.id === todolistData[i].id){ todolistData[i].isComplete = !todolistData[i].isComplete } } AsyncStorage.setItem('todolistData', JSON.stringify(todolistData), ()=>{ }); } _navigationAdd = () => { this.props.navigation.navigate('Add'); }; render() { let todoList = this.state.todolistData return ( <View style={styles.wrapper}> <FlatList data={todoList} keyExtractor = {(item, index) => item.id.toString()} renderItem={({item,index}) => <TodoItem dataItem={item} key={item.id} checkTodo={(item)=>this._checkTodo(item)} /> } /> <TouchableOpacity style={styles.addBtn} onPress={this._navigationAdd}> <Ionicons name="md-add" size={38} color="#fff" /> </TouchableOpacity> </View> ) } } const styles = StyleSheet.create({ wrapper: { flex: 1, backgroundColor: "#fff", padding: 10, }, addBtn:{ position: "relative", left: 10, bottom: 10, width: 60, height: 60, borderRadius: 60, backgroundColor: "#FF4081", alignItems: 'center', alignContent: 'center', justifyContent: 'center' } })
由於All、Incomplete、 Complete頁面都須要事項的頁面,因此將他分離出來作一個單獨的組件使用。
import React, {Component} from 'react'; import { Alert, StyleSheet, View, Text, TouchableHighlight, TouchableOpacity } from 'react-native'; import Ionicons from 'react-native-vector-icons/Ionicons'; class TodoItem extends Component{ constructor(props) { super(props); this.state = { switchValue: this.props.dataItem.isComplete, } } componentWillReceiveProps(nextProps){ this.setCompleteState(nextProps.dataItem.isComplete) } setCompleteState(isComplete){ this.setState({ switchValue: isComplete }) } _onSwitch(){ this.setCompleteState(!this.state.switchValue) this.props.checkTodo(this.props.dataItem) } render (){ return ( <View style={styles.todoitem}> <TouchableHighlight underlayColor="#90CAF9" onPress={ this._onSwitch.bind(this) }> <Ionicons name={this.state.switchValue? "md-checkbox":"md-square-outline"} size={34} style={this.state.switchValue? styles.colorOff:styles.colorOn} /> </TouchableHighlight> <TouchableOpacity> <Text style={this.state.switchValue? styles.itemtxtOff:styles.itemtxt}>{this.props.dataItem.content}</Text> </TouchableOpacity> </View> ) } } export default TodoItem; const styles = StyleSheet.create({ todoitem: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", alignContent: "center", padding: 10, }, colorOn:{ color:"#F50057" }, colorOff:{ color:"#e0e0e0" }, itemtxt:{ fontSize: 30, color: "#000" }, itemtxtOff:{ fontSize: 30, color: "#666" } })
Incomplete、Complete頁面和All頁面基本同樣,只是在渲染頁面前將數據過濾了。
// incomplete.js import React, {Component} from 'react'; import { StyleSheet, View, FlatList, AsyncStorage } from 'react-native'; import TodoItem from './todoItem' import Utils from './utils/utils' export default class IncompleteScreen extends Component { constructor(props) { super(props); this.state = { todolistData:[], incompleteData: [] } } componentDidMount() { this._getTodolistData(); } componentWillReceiveProps(){ this._getTodolistData(); } _getTodolistData(){ AsyncStorage.getItem('todolistData', (err, result) => { if(err){ return; } let todoListArr = (result != null) ? JSON.parse(result) :[]; this.setState({ todolistData: todoListArr }) let todoListFilter= Utils.filterArr(todoListArr); this.setState({ incompleteData: todoListFilter[false] }) }) } _checkTodo(item){ let todolistData = this.state.todolistData; for(let i = 0 , len = todolistData.length; i<len; i++){ if(item.id === todolistData[i].id){ todolistData[i].isComplete = !todolistData[i].isComplete AsyncStorage.setItem('todolistData', JSON.stringify(todolistData), ()=>{ }); } } } render() { let todoList = this.state.incompleteData return ( <View style={styles.wrapper}> <FlatList data={todoList} keyExtractor = {(item, index) => item.id} renderItem={({item, index}) => <TodoItem dataItem={item} checkTodo={(item)=>this._checkTodo(item)} /> } /> </View> ); } } const styles = StyleSheet.create({ wrapper: { flex: 1, backgroundColor: "#fff", padding: 10, } })
// utils.js export default class Utils { static filterArr(dataArr) { var list = dataArr, data = []; for (var i = 0; i < list.length; i++) { if (!data[list[i].isComplete]) { var arr = []; arr.push(list[i]); data[list[i].isComplete] = arr; } else { data[list[i].isComplete].push(list[i]) } } return data; }
Complete頁面和Incomplete徹底同樣,除了將todoListFilter[false]改成todoListFilter[true]。
Add頁面中的保存按鈕是寫在navigationOptions裏,若是直接使用this._saveTodo是拿不到這個方法的,須要在navigation中的內置函數setParams中聲明,方可以使用。
import React, {Component} from 'react'; import { StyleSheet, View, TextInput, TouchableOpacity, AsyncStorage, DeviceEventEmitter } from 'react-native'; import Ionicons from 'react-native-vector-icons/Ionicons'; import Utils from './utils/utils' class Add extends Component { constructor(props) { super(props); this.state = { todo: { id: "", content: "", isComplete: false }, todoArr:[] }; } static navigationOptions = ({ navigation }) => { const params = navigation.state.params || {}; return { headerRight: ( <TouchableOpacity style={{width: 56, height: 36, paddingRight:20}} onPress={params.saveTodo}> <Ionicons name="md-checkmark" size={36} color="#fff" /> </TouchableOpacity> ), }; }; componentWillMount() { this.props.navigation.setParams({ saveTodo: this._saveTodo }) AsyncStorage.getItem('todolistData', (err, result) => { if(err){ return; } let todoArr = (result != null) ? JSON.parse(result) :[] this.setState({ todoArr: todoArr }) }) } _saveTodo = () => { const todoVal = this.state.todo this.setState({todoArr: this.state.todoArr.push(todoVal)}) AsyncStorage.setItem('todolistData', JSON.stringify(this.state.todoArr), ()=>{ }); DeviceEventEmitter.emit('todolist_add') this.props.navigation.goBack(); } _changeTodo(text){ let _id = Utils.uniqueId() this.setState({ todo:{ id:_id, content:text, isComplete:false } }) } render(){ return ( <View style={styles.wrapper}> <TextInput style={styles.textInput} underlineColorAndroid="transparent" onChangeText={(text)=> {this._changeTodo(text)} } value={this.state.todo.content} multiline = {true} numberOfLines = {4} textAlignVertical="top" placeholder="add a todo" autoFocus={true} /> </View> ) } } export default Add; const styles = StyleSheet.create({ wrapper: { flex: 1, backgroundColor: "#fff", padding: 10, }, textInput: { color: "#333", padding: 0, fontSize: 18 } })
在utils.js中添加生成惟一ID的靜態函數。
// utils.js // 這個方法來自網絡 static uniqueId(){ let a=Math.random,b=parseInt; return Number(new Date()).toString()+b(10*a())+b(10*a())+b(10*a()); }
到此,添加一個事項,再切換其是否完成的狀態均可以實現了。可是咱們會發現代碼有不少重複的地方,咱們嘗試將它們都分離出來。
在三個主頁面中都出現了這個函數,咱們將其分離出來,寫到utils.js中
// utils.js static _checkTodo(item, todolistData){ for(let i = 0 , len = todolistData.length; i<len; i++){ if(item.id === todolistData[i].id){ todolistData[i].isComplete = !todolistData[i].isComplete } } AsyncStorage.setItem('todolistData', JSON.stringify(todolistData), ()=>{ }); }
那麼在頁面中,調用函數的方式以下,將this.state.todolistData做爲參數傳遞過去。
<TodoItem dataItem={item} key={item.id} checkTodo={(item)=>Utils._checkTodo(item,this.state.todolistData)} />
添加了太多事項,不得有刪除功能嗎?
這個方法和切換完成狀態是相似的。
onLongPress是指長按,長按以後彈出模態框點擊ok則執行刪除函數。
// todoitem.js <TouchableOpacity onLongPress={()=>{ this._showModel(this.props.dataItem)}} > <Text style={this.state.switchValue? styles.itemtxtOff:styles.itemtxt}>{this.props.dataItem.content}</Text> </TouchableOpacity>
// todoitem.js _showModel(todo){ Alert.alert( 'Delete it ?', todo.content, [ {text: 'Cancel', onPress: () => console.log('Cancel Pressed'), style: 'cancel'}, {text: 'OK', onPress: () => this.props.deleteTodo(todo)}, ], { cancelable: false } ) }
// utils.js static _deleteTodo(item, todolistData){ for(let i = todolistData.length-1; i>=0; i-- ){ if(item.id === todolistData[i].id){ todolistData.splice(i, 1) AsyncStorage.setItem('todolistData', JSON.stringify(todolistData), ()=>{ }); } } }
刪除事項以後須要從新獲取數據,否則頁面上仍是會顯示已經被殘忍刪掉的數據。
<TodoItem dataItem={item} key={item.id} checkTodo={(item)=>Utils._checkTodo(item,this.state.todolistData)} deleteTodo={(item) => {Utils._deleteTodo(item, this.state.todolistData); this._getTodolistData()}} />
收工!!!
還有修改的功能,心情好再寫。
完整的項目在這裏GitHub Todolist。
這不是一篇教程,而是一份記念品。
共勉!