摘要:首先我們打開(kāi)命令行,切換到項(xiàng)目根目錄下,輸入安裝完成后,請(qǐng)注意,需要把目錄下的所有字體文件拷貝到目錄下,如果沒(méi)有該目錄,請(qǐng)自行創(chuàng)建。
????????看過(guò)我前面文章的朋友們現(xiàn)在應(yīng)該能正常運(yùn)行自己的第一個(gè)RN應(yīng)用了,那都是小兒科,現(xiàn)在我們來(lái)做點(diǎn)進(jìn)階一點(diǎn)的東西。這篇文章有一些屬于干貨性的東西,請(qǐng)仔細(xì)閱讀。特別需要注意我加粗的部分。
????????首先我們來(lái)看下js文件結(jié)構(gòu),在項(xiàng)目初始化成功后,根目錄下有2個(gè)js文件,index.android.js和index.ios.js,這2個(gè)文件分別是android和ios的入口文件。這里我簡(jiǎn)單說(shuō)下RN對(duì)js文件的命名約束,如果你開(kāi)發(fā)的文件只用于android系統(tǒng),就需要存成.android.js文件,如果是只用于ios系統(tǒng),就需要存成.ios.js文件。如果是2個(gè)系統(tǒng)通用的,就只要存成.js就行了,系統(tǒng)再編譯時(shí)會(huì)根據(jù)你的編譯選項(xiàng),只打包對(duì)應(yīng)的js文件。由于我現(xiàn)在只作安卓應(yīng)用,所以我寫的js文件,不管是不是安卓專用的,我都保存成了.js文件,就不再加android前綴了,請(qǐng)大家注意。而且我新建了一個(gè)src目錄,我自己寫的js組件都放在此目錄下。整個(gè)項(xiàng)目目錄結(jié)構(gòu)如下:
HelloWorld -__tests__ -android -ios -node_modules -src -images icon.png -components -CustomText.js -app.js -index.js -home.js -find.js -user.js -.babelrc -.buckconfig -.flowconfig -.gitattributes -.gitignore -.watchmanconfig -app.json -index.android.js -index.ios.js -package.json
先修改下index.android.js,將內(nèi)容改成:
require("./src/app");
并把原來(lái)的index.android.js中的代碼拷貝到src/app.js中。接下來(lái)我們所有的js代碼編寫都將在src目錄下進(jìn)行。另外開(kāi)發(fā)過(guò)程中我們時(shí)刻要時(shí)刻關(guān)注下package server是否報(bào)錯(cuò)停止,如果停止就在窗口中運(yùn)行react-native start以重新啟動(dòng)改服務(wù)。
自定義組件????????reactjs之所以大受歡迎,其中一個(gè)很重要的原因就是其組件化設(shè)計(jì)思想,雖然angularjs通過(guò)指令也可以實(shí)現(xiàn)其組件化設(shè)計(jì)思想,但還是沒(méi)有reactjs來(lái)的優(yōu)雅(原諒我有點(diǎn)逼格的用了這個(gè)詞匯)。RN源自于reactjs,自然也繼承了其組件化的設(shè)計(jì)。其實(shí)自定義組件本來(lái)很簡(jiǎn)單,沒(méi)什么特別要講的,不過(guò)我這里有個(gè)特殊用途 所以就多帶帶拿出來(lái)說(shuō)一下吧。
????????RN中是沒(méi)有全局字體屬性可以設(shè)置的,所以如果我們要統(tǒng)一設(shè)定字體屬性,還是比較麻煩的,網(wǎng)上也有一些方案大家可以搜搜,我這里就用一個(gè)自定義Text組件來(lái)實(shí)現(xiàn)全局修改字體屬性,主要就是fontSize屬性和fontFamily屬性。我們將這個(gè)組件命名為CustomText.js,存放在components目錄下。
CustomText.js
import React, { Component } from "react"; import { Text } from "react-native"; class CustemText extends Component { constructor(props){ super(props); } render(){ let styles = { fontSize:12, color:"#000" } for(let item in this.props){ if(item !== "label"){ styles[item] = this.props[item]; } } return ({this.props.label} ) } } export default CustemText
????????在app.js中使用,請(qǐng)注意,如果屬性為數(shù)字或者bool值,需要寫在大括號(hào)中,比如fontSize屬性,如果為字符串,則直接書寫即可,比如color和label屬性。
... import CustomText from "./components/CustomText"; ... export default class HelloWorld extends Component { render() { return (使用自定義字體文件) } ...
????????這里我們結(jié)合你可能會(huì)用到的矢量字體庫(kù)react-native-vector-icons來(lái)講。首先我們打開(kāi)命令行,切換到項(xiàng)目根目錄下,輸入:
npm install --save-dev react-native-vector-icons
????????安裝完成后,請(qǐng)注意,需要把node_modules
eact-native-vector-iconsFonts目錄下的所有字體文件拷貝到androidappsrcmainassetsfonts目錄下,如果沒(méi)有該目錄,請(qǐng)自行創(chuàng)建。所有你需要使用自定義的字體都需要拷貝到該目錄下。
使用該模塊很簡(jiǎn)單,比如我們需要加載FontAwesome矢量字體,則這么引用:
... import Icon from "react-native-vector-icons/FontAwesome"; ... export default class HelloWorld extends Component { render() { return (使用本地圖片) } } ...
????????使用網(wǎng)絡(luò)圖片比較簡(jiǎn)單,直接引用URI地址即可,使用本地圖片則需要特別說(shuō)明下,因?yàn)榫W(wǎng)上很多資料是錯(cuò)誤的。引用本地圖片有2種方式:
1:根據(jù)facebook的建議,本地圖片建議放到j(luò)s文件相對(duì)目錄下,比如你可以在src目錄下再建一個(gè)images目錄,然后把你的圖片放到該目錄下。引用的話比較簡(jiǎn)單,比如你在app.js中引用images目錄下的icon.png文件,你可以這么寫:
... import Icon from "react-native-vector-icons/FontAwesome"; ... export default class HelloWorld extends Component { render() { return () } } ...
這么做的優(yōu)點(diǎn)就是不需要考慮不同操作系統(tǒng)的問(wèn)題,統(tǒng)一進(jìn)行處理。但是在打包時(shí),根據(jù)一些朋友的反饋,在android系統(tǒng)下,圖片文件會(huì)被編譯到androidappsrcmain
es目錄下,并且自動(dòng)更名為icon_images.png,可能會(huì)導(dǎo)致找不到圖片,不過(guò)我編譯后沒(méi)有這個(gè)現(xiàn)象,也許可能是RN版本問(wèn)題。
2:有代碼潔癖的人可能不愿意在js目錄中混入圖片,那可以采用這種方法。在androidappsrcmain
es目錄下,新建一個(gè)drawable目錄,然后把icon.png文件拷貝到該目錄下,注意這個(gè)目錄下同名文件不同格式的文件只能有一個(gè),比如你有了icon.png就不能再有icon.jpg了,否則會(huì)報(bào)錯(cuò)。然后在代碼中引用:
請(qǐng)注意source的寫法,新版RN的寫法不是require("image!icon") ,而是require("icon"),不要加后綴.png。我在項(xiàng)目中就是使用這種方法加載本地圖片的。
使用導(dǎo)航控件????????在項(xiàng)目中多多少少會(huì)使用導(dǎo)航控件,這樣界面組織比較直觀,這一節(jié)我們就來(lái)學(xué)習(xí)如何使用Navigator控件。首先需要安裝依賴模塊,命令行下切換到項(xiàng)目所在目錄里,運(yùn)行:
npm install --save-dev react-native-tab-navigator
照著樣子寫就行,具體API請(qǐng)查詢官方文檔或RN中文網(wǎng),這里就不再詳說(shuō)了:
app.js
import React, { Component } from "react"; import { AppRegistry, Navigator, View } from "react-native"; import Index from "./index";//導(dǎo)航首頁(yè) export default class HelloWorld extends Component { render(){ let defaultName = "Index"; let defaultComponent = Index; return ({ Navigator.SceneConfigs.HorizontalSwipeJump.gestures=null;//不允許滑動(dòng)返回 return Navigator.SceneConfigs.HorizontalSwipeJump; }} renderScene={(route, navigator) => { let Component = route.component; return }} /> ) } } AppRegistry.registerComponent("HelloWorld", () => HelloWorld);
index.js
import React, { Component } from "react"; import { BackAndroid, StyleSheet, View, TouchableHighlight, Navigator } from "react-native"; import TabNavigator from "react-native-tab-navigator"; import Ionicons from "react-native-vector-icons/Ionicons"; import Home from "./home"; import Find from "./find"; import User from "./user"; class Index extends Component{ constructor(props) { super(props); this.state = { selectedTab:"home", index:0 }; } componentDidMount() { const { navigator } = this.props; //注冊(cè)點(diǎn)擊手機(jī)上的硬返回按鈕事件 BackAndroid.addEventListener("hardwareBackPress", () => { return this.onBackAndroid(navigator) }); } componentWillUnmount() { BackAndroid.removeEventListener("hardwareBackPress"); } onBackAndroid(navigator){ const routers = navigator.getCurrentRoutes(); if (routers.length > 1) { navigator.pop(); return true; } return false; } changeTab(tab){//改變導(dǎo)航時(shí) this.setState({ selectedTab:tab}); } render(){ return () } } export default Index; } renderSelectedIcon={() => } selected={ this.state.selectedTab === "home" } onPress={() => this.changeTab("home")}> } renderSelectedIcon={() => } selected={this.state.selectedTab=="find"} onPress={() => this.changeTab("find")} > } renderSelectedIcon={() => } selected={this.state.selectedTab =="user"} onPress={() => this.changeTab("user")}>
然后你自己分別實(shí)現(xiàn)home.js,find.js以及user.js即可,這里就不再詳述了。在這里需要說(shuō)明以下onPress箭頭函數(shù)(ES6語(yǔ)法),新版的RN用箭頭函數(shù)來(lái)執(zhí)行方法,而不是this.changeTab.bind(this),用箭頭函數(shù)有個(gè)很大的好處是你不用擔(dān)心上下文中this的指向問(wèn)題,它永遠(yuǎn)指向當(dāng)前的組件對(duì)象。
????????RN中自帶的圖片處理組件CameraRoll并不好用,我這里用react-native-image-picker這個(gè)工具,同樣在命令行下運(yùn)行npm install --save-dev react-native-image-picker,一般情況下會(huì)報(bào)錯(cuò),提示缺少fs依賴,所以我們要先運(yùn)行npm install --save-dev fs,然后再運(yùn)行npm install --save-dev react-native-image-picker。詳細(xì)的配置步驟請(qǐng)參考官方安裝手冊(cè),有個(gè)特別的地方需要注意的事官方手冊(cè)沒(méi)有提到,請(qǐng)打開(kāi)node_modules eact-native-image-pickerandroiduild.gradle文件,然后修改buildToolsVersion為你實(shí)際build tools版本。。直接上代碼,代碼比較長(zhǎng),我就不直接解釋了,自己慢慢看慢慢查資料吧,有什么問(wèn)題可以在評(píng)論里問(wèn)我。CustomButton是自定義的一個(gè)按鈕組件,代碼實(shí)現(xiàn)比較簡(jiǎn)單,這里就不再貼出了。
user.js
import React, { Component } from "react"; import { StyleSheet, View, Image, TouchableOpacity, ToastAndroid, Dimensions, PanResponder, ImageEditor, ImageStore } from "react-native"; import Icon from "react-native-vector-icons/FontAwesome"; import Ionicons from "react-native-vector-icons/Ionicons"; import CustomButton from "./components/CustomButton"; import ImagePicker from "react-native-image-picker"; let {height, width} = Dimensions.get("window"); class User extends Component{ constructor(props) { super(props); this.unmounted = false; this.camera = null; this._clipWidth = 200; this._boxWidth = 20; this._maskResponder = {}; this._previousLeft = 0; this._previousTop = 0; this._previousWidth = this._clipWidth; this._backStyles = { style: { left: this._previousLeft, top: this._previousTop } }; this._maskStyles = { style: { left: -(width-this._clipWidth)/2, top: -(width-this._clipWidth)/2 } }; this.state = { token:null, username:null, photo:null, switchIsOn: true, uploading:false, uploaded:false, changePhoto:false, scale:1, width:0, height:0 } } componentWillMount() { this._maskResponder = PanResponder.create({ onStartShouldSetPanResponder: ()=>true, onMoveShouldSetPanResponder: ()=>true, onPanResponderGrant: ()=>false, onPanResponderMove: (e, gestureState)=>this._maskPanResponderMove(e, gestureState), onPanResponderRelease: (e, gestureState)=>this._maskPanResponderEnd(e, gestureState), onPanResponderTerminate: (e, gestureState)=>this._maskPanResponderEnd(e, gestureState), }); } _updateNativeStyles() { this._maskStyles.style.left = -(width-this._clipWidth)/2+this._backStyles.style.left; this._maskStyles.style.top = -(width-this._clipWidth)/2+this._backStyles.style.top; this.refs["BACK_PHOTO"].setNativeProps(this._backStyles); this.refs["MASK_PHOTO"].setNativeProps(this._maskStyles); } _maskPanResponderMove(e, gestureState){ let left = this._previousLeft + gestureState.dx; let top = this._previousTop + gestureState.dy; this._backStyles.style.left = left; this._backStyles.style.top = top; this._updateNativeStyles(); } _maskPanResponderEnd(e, gestureState) { this._previousLeft += gestureState.dx; this._previousTop += gestureState.dy; } componentWillUnMount() { this.unmounted = true; } _saveImage(){ let photoURI=this.state.photo.uri; let left = -Math.floor(this._backStyles.style.left)+(width-this._clipWidth)/2; let top = -Math.floor(this._backStyles.style.top)+(width-this._clipWidth)/2; if(left<0 || top<0 || left+this._clipWidth>width || top+this._clipWidth>height){ ToastAndroid.show("超出裁剪區(qū)域,請(qǐng)重新選擇", ToastAndroid.SHORT); return; } this.setState({uploading:true}); ImageEditor.cropImage( photoURI, {offset:{x:left,y:top},size:{width:this._clipWidth, height:this._clipWidth}}, (croppedURI)=>{ ImageStore.getBase64ForTag( croppedURI, (base64)=>{ //這里即可獲得base64編碼的字符串,將此字符串上傳帶服務(wù)器處理,保存后并生成圖片地址返回即可,詳細(xì)代碼后面結(jié)合node.js再做講解。 }, (err)=>true ); }, (err)=>true ); } _fromGallery() { let options = { storageOptions: { skipBackup: true, path: "images" }, maxWidth:width, mediaType: "photo", // "photo" or "video" videoQuality: "high", // "low", "medium", or "high" durationLimit: 10, // video recording max time in seconds allowsEditing: true // 當(dāng)用戶選擇過(guò)照片之后是否允許再次編輯圖片 }; console.log(ImagePicker); ImagePicker.launchImageLibrary(options, (response) => { if (!(response.didCancel||response.error)) { Image.getSize(response.uri, (w, h)=>{ this.setState({ changePhoto:true, photo: response, width: w, height: w*h/width }); this._updateNativeStyles(); }) } }); } _fromCamera() { let options = { storageOptions: { skipBackup: true, path: "images" }, maxWidth:width, mediaType: "photo", // "photo" or "video" videoQuality: "high", // "low", "medium", or "high" durationLimit: 10, // video recording max time in seconds allowsEditing: true // 當(dāng)用戶選擇過(guò)照片之后是否允許再次編輯圖片 }; ImagePicker.launchCamera(options, (response) => { if (!(response.didCancel||response.error)) { Image.getSize(response.uri, (w, h)=>{ this.setState({ changePhoto:true, photo: response, width:w, height:w*h/width }); this._updateNativeStyles(); }) } }); } render() { let Photo,Uploading; if(this.state.photo){ if(this.state.changePhoto){ Photo=其它}else{ Photo= ; } } return ( ); } } var styles = StyleSheet.create({ wrap:{ flex:1, flexDirection:"column", backgroundColor:"whitesmoke", alignItems:"stretch", justifyContent:"center" }, body:{ flex:1, flexDirection:"column", alignItems:"stretch", justifyContent:"flex-start" }, row:{ flex:0, flexDirection:"row", alignItems:"center", backgroundColor:"#fff" }, row1:{ flex:0, padding:10, flexDirection:"row", backgroundColor:"#fff", alignItems:"stretch", justifyContent:"center" } }); export default User {(()=> this.state.changePhoto? {Photo} : this._saveImage()}/> )()} this._fromGallery()}/> this._fromCamera()}/>
????????1:修改應(yīng)用程序名稱,請(qǐng)修改androidappsrcmain esvaluesstrings.xm文件,然后將HelloWorld改成你喜歡的名稱,可以是中文,你安裝到手機(jī)上的應(yīng)用名稱就是這里定義的。
????????2:修改應(yīng)用程序名稱,請(qǐng)修改androidappsrcmain es下以mipmap-開(kāi)頭的所有文件夾下的ic_launcher.png文件,覆蓋它即可,注意你要先刪除手機(jī)上的應(yīng)用程序,然后再編譯才會(huì)生效。
????????好了,碼了這么多字,希望對(duì)大家有所幫助,喜歡的就支持下,呵呵。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/18992.html
摘要:這里是目錄一步一步開(kāi)發(fā)安卓下的應(yīng)用系列之環(huán)境搭建篇一步一步開(kāi)發(fā)安卓下的應(yīng)用系列之第一個(gè)應(yīng)用一步一步開(kāi)發(fā)安卓下的應(yīng)用系列之進(jìn)階篇怎么開(kāi)發(fā)原生模塊打包分發(fā)你的實(shí)現(xiàn)在線升級(jí),包括熱更新篇篇篇 ????????公司今年效益慘淡,手頭上沒(méi)什么事可作,于是琢磨著自己做點(diǎn)什么,想了想,如今RN那么火熱,那就整個(gè)APP出來(lái)玩玩吧。因?yàn)橹皼](méi)怎么學(xué)過(guò)reactjs,更沒(méi)有安卓系統(tǒng)開(kāi)發(fā)經(jīng)驗(yàn),所以從過(guò)完年開(kāi)...
摘要:閉上眼睛,心中默念一百遍遍馬力馬力轟,再睜開(kāi)眼,如果你是安卓及以上系統(tǒng),你就能在你手機(jī)上看到你第一個(gè)應(yīng)用了圖,如果是以下,嘿嘿,一個(gè)血紅血紅的界面,不過(guò)沒(méi)關(guān)系,我們來(lái)糾正它。 ????????前期準(zhǔn)備工作已經(jīng)完成,接下來(lái)將正式進(jìn)入開(kāi)發(fā)了,請(qǐng)深呼吸下,呵呵。我們首先寫個(gè)Hello World工程來(lái)練練手。????????在命令行上點(diǎn)右鍵,選擇以管理員身份運(yùn)行。建議每次運(yùn)行命令行的時(shí)候都用...
摘要:探索專為而設(shè)計(jì)的將探討進(jìn)行了何種改進(jìn),以及這些改進(jìn)背后的原因。關(guān)于最友好的文章進(jìn)階前言之前就寫過(guò)一篇關(guān)于最友好的文章反響很不錯(cuò),由于那篇文章的定位就是簡(jiǎn)單友好,因此盡可能的摒棄復(fù)雜的概念,只抓住關(guān)鍵的東西來(lái)講,以保證大家都能看懂。 周月切換日歷 一個(gè)可以進(jìn)行周月切換的日歷,左右滑動(dòng)的切換月份,上下滑動(dòng)可以進(jìn)行周,月不同的視圖切換,可以進(jìn)行事件的標(biāo)記,以及節(jié)假日的顯示,功能豐富 Andr...
摘要:引言給迷失在如何學(xué)習(xí)區(qū)塊鏈技術(shù)的同學(xué)一個(gè)指引,區(qū)塊鏈技術(shù)是隨比特幣誕生,因此要搞明白區(qū)塊鏈技術(shù),應(yīng)該先了解下比特幣。但區(qū)塊鏈技術(shù)不單應(yīng)用于比特幣,還有非常多的現(xiàn)實(shí)應(yīng)用場(chǎng)景,想做區(qū)塊鏈應(yīng)用開(kāi)發(fā),可進(jìn)一步閱讀以太坊系列。 本文始發(fā)于深入淺出區(qū)塊鏈社區(qū), 原文:區(qū)塊鏈技術(shù)學(xué)習(xí)指引 原文已更新,請(qǐng)讀者前往原文閱讀 本章的文章越來(lái)越多,本文是一個(gè)索引帖,方便找到自己感興趣的文章,你也可以使用左側(cè)...
閱讀 3265·2021-11-17 09:33
閱讀 3320·2021-11-15 11:37
閱讀 2980·2021-10-19 11:47
閱讀 3227·2019-08-29 15:32
閱讀 1034·2019-08-29 15:27
閱讀 1553·2019-08-29 13:15
閱讀 958·2019-08-29 12:47
閱讀 2053·2019-08-29 11:30