閒暇之餘,寫了一個React Native的demo,能夠做爲你們的入門學習參考。html
GitHub:https://github.com/xujianfu/ElmApp.git前端
GitHub:https://github.com/xujianfu/React-Native-CarProject.gitreact
項目截圖以下:git
React Navigation 源於 React Native 社區對一個可擴展且易於使用的導航解決方案的需求,它徹底使用 JavaScript 編寫(所以你能夠閱讀並理解全部源碼)。支持iOS/Android.github
yarn add react-navigation # or with npm # npm install --save react-navigation
而後,安裝 react-native-gesture-handler。 若是你正在使用 Expo managed workflow,那麼你什麼都不須要作, SDK 中已經包含了這些. 不然: npm
yarn add react-native-gesture-handler # or with npm # npm install --save react-native-gesture-handler
最後進行Link 全部的原生依賴react-native
react-native link react-native-gesture-handler
爲某個模塊建立StackNavigator導航前端框架
const HomeStack = createStackNavigator( { Home:{ screen:HomeScreen, navigationOptions:()=>({ headerBackTitle: null, }) }, //添加多個路由 CarLoans:CarLoansScreen, CheckRules:CheckRulesScreen, }, ) ......
將多個模塊添加到TabNavigator上app
const TabNavigator = createBottomTabNavigator( { Home:{ screen:HomeStack, navigationOptions:({navigation}) => ({ tabBarLabel:'首頁', tabBarIcon:({focused}) => ( <Image source={{uri:focused ? 'ic_tab_home_h':'ic_tab_home_n.png'}} style={styles.iconStyle}/> ), }), }, Mall:{ screen:MallStack, navigationOptions:({navigation}) => ({ tabBarLabel:'商城', tabBarIcon:({focused}) => ( <Image source={{uri:focused ? 'ic_tab_mall_h':'ic_tab_mall_n.png'}} style={styles.iconStyle}/> ) }), }, Publish:{ screen:PublishStack, navigationOptions:({navigation}) => ({ tabBarLabel:'發佈', tabBarIcon:({focused}) => ( <Image source={{uri:focused ? 'ic_tab_release_h':'ic_tab_release_n.png'}} style={styles.iconStyle}/> ) }), }, Discover:{ screen:DiscoverStack, navigationOptions:({navigation}) => ({ tabBarLabel:'發現', tabBarIcon:({focused}) => ( <Image source={{uri:focused ? 'ic_tab_find_h':'ic_tab_find_n.png'}} style={styles.iconStyle}/> ) }), }, Mine:{ screen:MineStack, navigationOptions:({navigation}) => ({ tabBarLabel:'個人', tabBarIcon:({focused}) => ( <Image source={{uri:focused ? 'ic_tab_my_h':'ic_tab_my_n.png'}} style={styles.iconStyle}/> ) }), }, }, { defaultNavigationOptions: ({ navigation }) => { let tabBarVisible = true; if (navigation.state.index > 0) { tabBarVisible = false; } return { tabBarVisible, }; }, tabBarPosition:'bottom', tabBarOptions: { activeTintColor: 'blue', //選中tabbar的文字顏色 inactiveTintColor: 'gray', showIcon:true, }, } ); export default createAppContainer(TabNavigator);
(1)引入react-native-image-picker框架
yarn add react-native-image-picker
react-native link react-native-image-picker
(2)在項目中使用react-native-image-picker
import ImagePicker from 'react-native-image-picker'; //選擇圖片 selectPhotoTapped() { const options = { // 彈窗標題 title: '選擇圖片', cancelButtonTitle: '取消', takePhotoButtonTitle: '拍照', chooseFromLibraryButtonTitle: '選擇照片', // 自定義按鈕 customButtons: [ {name: 'fb', title: 'Choose Photo from Facebook'}, ], // 相機類型'front' 或者 'back' cameraType: 'back', // 圖片或視頻:'photo','video' mediaType: 'photo', // 視頻質量 videoQuality: 'high', //最大視頻錄製時間 durationLimit: 10, //最長寬 maxWidth: 300, //最長高, maxHeight: 300, //圖片質量 quality: 0.8, angle: 0, //是否能夠編輯 allowsEditing: false, //若是爲真,則禁用data生成的base64字段 noData: false, // 若是提供此密鑰,該圖像將被保存在Documents iOS 應用程序的目錄中,或者保存在PicturesAndroid上的應用程序目錄(而不是臨時目錄) storageOptions: { skipBackup: true } }; ImagePicker.showImagePicker(options, (response) => { console.log('Response = ', response); if (response.didCancel) { console.log('User cancelled photo picker'); } else if (response.error) { console.log('ImagePicker Error: ', response.error); } else if (response.customButton) { console.log('User tapped custom button: ', response.customButton); } else { let source = { uri: response.uri }; // You can also display the image using data: // let source = { uri: 'data:image/jpeg;base64,' + response.data }; this.setState({ avatarSource: source }); } }); } //選擇視頻 selectVideoTapped() { const options = { title: '選擇視頻', cancelButtonTitle: '取消', takePhotoButtonTitle: '錄製視頻', chooseFromLibraryButtonTitle: '選擇視頻', mediaType: 'video', videoQuality: 'medium' }; ImagePicker.showImagePicker(options, (response) => { console.log('Response = ', response); if (response.didCancel) { console.log('User cancelled video picker'); } else if (response.error) { console.log('ImagePicker Error: ', response.error); } else if (response.customButton) { console.log('User tapped custom button: ', response.customButton); } else { this.setState({ videoSource: response.uri }); } }); }
導入react-native-scrollable-tab-view
npm install react-native-scrollable-tab-view --save
項目中引入
//引用插件 import ScrollableTabView, { ScrollableTabBar, DefaultTabBar } from 'react-native-scrollable-tab-view'; <ScrollableTabView initialPage={0} renderTabBar={() => <ScrollableTabBar style={{borderBottomWidth: 0,height: 44}}/>} tabBarTextStyle={{fontSize:16}} tabBarActiveTextColor={'#fdd000'} tabBarInactiveTextColor={'#999999'} tabBarUnderlineStyle={{backgroundColor:'#fdd000'}} > { label.map((item,index) =>{ if (index === 0) { return <AllBusinessScreen tabLabel={item} key={index}/> } else { return <NearByBusinessScreen tabLabel={item} key={index}/> } }) } </ScrollableTabView>
Modal組件能夠用來覆蓋包含React Native根視圖的原生視圖(如UIViewController,Activity)。在嵌入React Native的混合應用中可使用Modal。Modal可使你應用中RN編寫的那部份內容覆蓋在原生視圖上顯示。
<Modal animationType={"slide"} transparent={true} visible={this.state.modalVisible} onRequestClose={()=>{alert('modal has been closed')}} > <View style={styles.modalStyle}> <View style={styles.coverStyle}> {this.renderItem()} </View> </View> </Modal> ...... renderItem(){ let itemTitleArr = ['京','滬','浙','蘇','粵','魯','晉','冀', '豫','川','渝','遼','吉','黑','皖','鄂', '湘','贛','閩','陝','甘','寧','蒙','津', '貴','雲','桂','瓊','青','新','藏'];; var itemArr = []; for (var i = 0; i < itemTitleArr.length; i++) { itemArr.push( <TouchableHighlight onPress={this.callBack.bind(this,itemTitleArr[i])} key={i}> <View style={styles.chooseItemStyle} > <Text style={styles.chooseTitleStyle}>{itemTitleArr[i]}</Text> </View> </TouchableHighlight> ) } return itemArr; }
import React, {Component} from 'react'; import {View, Text, Image, TouchableOpacity, ScrollView, Animated, Easing, StyleSheet} from 'react-native'; import PropTypes from 'prop-types'; class DropdownMenu extends Component { constructor(props, context) { super(props, context); var selectIndex = new Array(this.props.data.length); for (var i = 0; i < selectIndex.length; i++) { selectIndex[i] = 0; } this.state = { activityIndex: -1, selectIndex: selectIndex, rotationAnims: props.data.map(() => new Animated.Value(0)) }; this.defaultConfig = { bgColor: '#f5f5f5', tintColor: '#fdd000', activityTintColor: "red", arrowImg: 'ic_nav_down', checkImage: 'ic_nav_down' }; } renderChcek(index, title) { var activityIndex = this.state.activityIndex; if (this.state.selectIndex[activityIndex] === index) { var checkImage = this.props.checkImage ? this.props.checkImage : this.defaultConfig.checkImage; return ( <View style={{flex: 1, justifyContent: 'space-between', alignItems: "center", paddingHorizontal: 15, flexDirection: 'row'}} > <Text style={[ styles.item_text_style, this.props.optionTextStyle, {color: this.props.activityTintColor ? this.props.activityTintColor : this.defaultConfig.activityTintColor} ]} > {title} </Text> <Image source={checkImage} style={{tintColor: this.props.activityTintColor ? this.props.activityTintColor : this.defaultConfig.activityTintColor}} /> </View> ); } else { return ( <View style={{flex: 1, justifyContent: 'space-between', alignItems: "center", paddingHorizontal: 15, flexDirection: 'row'}} > <Text style={[ styles.item_text_style, this.props.optionTextStyle, {color: this.props.tintColor ? this.props.tintColor : this.defaultConfig.tintColor} ]} >{title}</Text> </View> ); } } renderActivityPanel() { if (this.state.activityIndex >= 0) { var currentTitles = this.props.data[this.state.activityIndex]; var heightStyle = {}; if (this.props.maxHeight && this.props.maxHeight < currentTitles.length * 44) { heightStyle.height = this.props.maxHeight; } return ( <View style={{position: 'absolute', left: 0, right: 0, top: 40, bottom: 0}}> <TouchableOpacity onPress={() => this.openOrClosePanel(this.state.activityIndex)} activeOpacity={1} style={{position: 'absolute', left: 0, right: 0, top: 0, bottom: 0}}> <View style={{opacity: 0.4, backgroundColor: 'black', flex: 1 }} /> </TouchableOpacity> <ScrollView style={[{position: 'absolute', top: 0, left: 0, right: 0, backgroundColor: 'white'}, heightStyle]} > { currentTitles.map((title, index) => <TouchableOpacity key={index} activeOpacity={1} style={{flex: 1, height: 44}} onPress={this.itemOnPress.bind(this, index)} > {this.renderChcek(index, title)} <View style={{backgroundColor: '#F6F6F6', height: 1, marginLeft: 15}} /> </TouchableOpacity> ) } </ScrollView> </View> ); } else { return (null); } } openOrClosePanel(index) { this.props.bannerAction ? this.props.bannerAction() : null; // var toValue = 0.5; if (this.state.activityIndex == index) { this.closePanel(index); this.setState({ activityIndex: -1, }); // toValue = 0; } else { if (this.state.activityIndex > -1) { this.closePanel(this.state.activityIndex); } this.openPanel(index); this.setState({ activityIndex: index, }); // toValue = 0.5; } // Animated.timing( // this.state.rotationAnims[index], // { // toValue: toValue, // duration: 300, // easing: Easing.linear // } // ).start(); } openPanel(index) { Animated.timing( this.state.rotationAnims[index], { toValue: 0.5, duration: 300, easing: Easing.linear } ).start(); } closePanel(index) { Animated.timing( this.state.rotationAnims[index], { toValue: 0, duration: 300, easing: Easing.linear } ).start(); } itemOnPress(index) { if (this.state.activityIndex > -1) { var selectIndex = this.state.selectIndex; selectIndex[this.state.activityIndex] = index; this.setState({ selectIndex: selectIndex }); if (this.props.handler) { this.props.handler(this.state.activityIndex, index); } } this.openOrClosePanel(this.state.activityIndex); } renderDropDownArrow(index) { var icon = this.props.arrowImg ? this.props.arrowImg : this.defaultConfig.arrowImg; return ( <Animated.Image source={{uri:icon}} style={{ width:6, height:4, marginLeft: 8, tintColor: (index === this.state.activityIndex) ? (this.props.activityTintColor ? this.props.activityTintColor : this.defaultConfig.activityTintColor) : (this.props.tintColor ? this.props.tintColor : this.defaultConfig.tintColor), transform: [{ rotateZ: this.state.rotationAnims[index].interpolate({ inputRange: [0, 1], outputRange: ['0deg', '360deg'] }) }] }} /> ); } render() { return ( <View style={{flexDirection: 'column', flex: 1}} > <View style={{ flexDirection: 'row', backgroundColor: this.props.bgColor ? this.props.bgColor : this.defaultConfig.bgColor}} > { this.props.data.map((rows, index) => <TouchableOpacity activeOpacity={1} onPress={this.openOrClosePanel.bind(this, index)} key={index} style={{flex: 1, height: 48, alignItems: "center", justifyContent: "center"}} > <View style={{flexDirection: 'row', alignItems: "center", justifyContent: "center"}} > <Text style={[ styles.title_style, this.props.titleStyle, {color: (index === this.state.activityIndex) ? (this.props.activityTintColor ? this.props.activityTintColor : this.defaultConfig.activityTintColor) : (this.props.tintColor ? this.props.tintColor : this.defaultConfig.tintColor)} ]} > {rows[this.state.selectIndex[index]]} </Text> {this.renderDropDownArrow(index)} </View> </TouchableOpacity> ) } </View> {this.props.children} {this.renderActivityPanel()} </View> ); } } DropdownMenu.propTypes = { bgColor: PropTypes.string, tintColor: PropTypes.string, activityTintColor: PropTypes.string, arrowImg: PropTypes.number, checkImage: PropTypes.number, data: PropTypes.array, bannerAction: PropTypes.func, optionTextStyle: PropTypes.object, titleStyle: PropTypes.object, maxHeight: PropTypes.number } const styles = StyleSheet.create({ title_style: { fontSize: 16 }, item_text_style: { color: '#fdd000', fontSize: 16 } }); export default DropdownMenu;
如何使用?
render() { var data = [["分類", "分類", "分類", "分類"], ["價格", "價格"], ["篩選", "篩選"]]; return ( <View style={{flex: 1}}> <View style={styles.dropMenu}/> <DropMenu style={{flex:1}} bgColor={'white'} tintColor={'#666666'} activityTintColor={'#fdd000'} // arrowImg={} // checkImage={} // optionTextStyle={{color: '#333333'}} // titleStyle={{color: '#333333'}} // maxHeight={300} handler={(selection, row) => this.setState({text: data[selection][row]})} data={data} > <ListView style={styles.listViewStyle} dataSource={this.state.dataSource} renderRow={this.renderRow} /> </DropMenu> </View> ); }
項目中ScrollView嵌套ListView會形成手勢滑動衝突,可使用「A+ListView」或「ListView + B」的樣式進行搭建,
經過:ListView的header或footer來實現。
項目中使用的經過jsp API接入到高德地圖。
React Native是React在移動端的跨平臺方案。若是想更快地理解和掌握React Native開發,就必須先了解React。
React是FaceBook開源的一個前端框架,它起源於 Facebook 的內部項目,並於 2013 年 5 月開源。由於React 擁有較高的性能,代碼邏輯很是簡單,因此愈來愈多的人已開始關注和使用它,目前該框架在Github上已經有7萬+star。
React採用組件化的方式開發,經過將view構建成組件,使得代碼更加容易獲得複用,可以很好的應用在大項目的開發中。有一句話說的很形象:在React中,構建應用就像搭積木同樣。
在React Native裏,組件所持有的數據分爲兩種:
一、屬性(props):組件的props是不可變的,它只能從其餘的組件(例如父組件)傳遞過來。
二、狀態(state):組建的state是可變的,它負責處理與用戶的交互。在經過用戶點擊事件等操做之後,若是使得當前組件的某個state發生了改變,那麼當前組件就會觸發render()方法刷新本身。
props:
因爲props是從其父組件傳遞過來的,那麼可想而知,props的聲明應該是當前組件的父組件來作。
請參考組件的生命週期
請參考學習:React Navigation的應用
組件間通訊分爲兩大類;
一、有直接關係或間接關係的組件之間通訊
二、無直接關係或間接關係的組件之間通訊