摘要:分別命名文件為和。上述代碼指定了當某列書籍被點擊時響應一個回調函數。當組件的值改變時例如用戶鍵入一些文本,將會調用組件,同時為組件指定一個回調函數。在調用時,回調函數和利用用戶輸入的數據將設置和屬性。
【編者按】本篇文章的作者是 Joyce Echessa——渥合數位服務創辦人,畢業于臺灣大學,近年來專注于協助客戶進行 App 軟體以及網站開發。本篇文章中,作者介紹通過 React Native 框架構建一個示例應用的開發過程,使得網絡技術和移動開發碰撞出絢麗火花!
React Native 簡介:用 JavaScript 搭建 iOS 應用 (1)
在 render()函數中,使用 TabBarIOS 組件創建一個分頁列。別忘了添加你使用的組件到解構賦值中,否則以后調用都需要使用完整名稱,比如 React.TabBarIOS。
我們創建了兩個分頁列項目。我們為每一個項目設置選中狀態,并定義一個該項目被點擊時所調用的函數。以精選標簽為例,我們之前定義的 selectedTab 狀態為「featured」,那么 selected 設置為 true,否則將被設置為 false。對于搜索標簽頁也一樣,需要檢查 selectedTab 是否為「search」。一旦項目的 selected 設置為true,將成為激活狀態標簽。我們用系統圖標表示標簽欄項目。
需要注意的是,我們使用的自定義組件標簽,和其他的組件一樣。例如,我們需要相應的模塊,并將其分配給一個變量,你可以使用變量來調用模塊。結果如同組件類的 render()函數一樣,成為文件代碼的一部分。提醒一下,作者習慣使用變量名作為各自的類名,但這并不是必須,你可以用你喜歡的名稱。
當一個標簽欄項目點擊時,會調用在組件的 onPress 屬性中定義的回調函數。函數會為 selectedTab 屬性設置數值,這個屬性將最終確定哪個是活動標簽。
調用模擬器,按下 Command-R 重載該應用。正如下圖所示。
添加導航欄下一步,我們將添加一個導航欄,并將兩個文件添加到項目中。這些都將作為相應標簽出現在導航堆棧的根視圖。分別命名文件為 BookList.js 和 SearchBooks.js。
在 BookList.js 添加以下代碼。
javascript"use strict";
var React = require("react-native");
var {
StyleSheet,
View,
Component
} = React;
var styles = StyleSheet.create({
});
class BookList extends Component {
render() {
return (
);
}
}
module.exports = BookList;
在 SearchBooks.js 中添加以下代碼。
javascript"use strict";
var React = require("react-native");
var {
StyleSheet,
View,
Component
} = React;
var styles = StyleSheet.create({
});
class SearchBooks extends Component {
render() {
return (
);
}
}
module.exports = SearchBooks;
在這兩個文件中創建空白視圖模塊,并導出該模塊。
按照以下代碼修改 Featured.js。
javascript"use strict";
var React = require("react-native");
var BookList = require("./BookList");
var {
StyleSheet,
NavigatorIOS,
Component
} = React;
var styles = StyleSheet.create({
container: {
flex: 1
}
});
class Featured extends Component {
render() {
return (
);
}
}
module.exports = Featured;
以上代碼使用 NavigatorIOS 組件來構造一個導航控制器。我們將其初始路徑設定為 BookList 組件(這意味著 BookList 為其根視圖),并設置導航欄上方的標題。
接著用以下代碼修改 Search.js。
javascript"use strict";
var React = require("react-native");
var SearchBooks = require("./SearchBooks");
var {
StyleSheet,
NavigatorIOS,
Component
} = React;
var styles = StyleSheet.create({
container: {
flex: 1
}
});
class Search extends Component {
render() {
return (
);
}
}
module.exports = Search;
正如在 Featured.js 一樣,以上代碼創建導航控制器,再設置其初始路徑,接著為它設置標題。
重載應用,你可以看到以下界面。
獲取并顯示數據現在,我們開始將數據添加到視圖中。起初,我們用虛構數據構建視圖,之后再從 API 獲取真實的數據。
在 BookList.js 中其他變量聲明的文件頂部,添加以下代碼。
javascriptvar FAKE_BOOK_DATA = [
{volumeInfo: {title: "The Catcher in the Rye", authors: "J. D. Salinger", imageLinks: {thumbnail: "http://books.google.com/books/content?id=PCDengEACAAJ&printsec=frontcover&img=1&zoom=1&source=gbs_api"}}}
];
如下圖所示修改解構賦值,以添加更多組件。
javascriptvar {
Image,
StyleSheet,
Text,
View,
Component,
} = React;
添加如下樣式。
javascriptvar styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
backgroundColor: "#F5FCFF",
padding: 10
},
thumbnail: {
width: 53,
height: 81,
marginRight: 10
},
rightContainer: {
flex: 1
},
title: {
fontSize: 20,
marginBottom: 8
},
author: {
color: "#656565"
}
});
如下圖所示,修改 BookList 類。
javascriptclass BookList extends Component {
render() {
var book = FAKE_BOOK_DATA[0];
return (
{book.volumeInfo.title}
{book.volumeInfo.authors}
);
}
}
重新加載應用,可以看到下圖界面。
在上面的代碼中,我們創建一個 JSON 對象,非常類似于從 API 調用的對象。我們為一本書的對象創建屬性和值。在類文件中,我們使用虛構數據,只為了得到第一個元素,并用它來填充我們的視圖。我們使用圖像組件來加載圖像到視圖。需要注意的是,我們在樣式表中設定其寬度和高度。如果在樣式表中指定圖像的尺寸,那么在視圖中將看不到圖像。
我們為容器指定了 flexDirection 為「row」的樣式。這樣的話,元素的子代也將繼承該風格,默認值是水平布局而不是縱向布局。請注意,我們是如何在組件內包裝其他組件的。在上面代碼中,主容器中有兩個子元素——圖像和視圖。視圖也有自己的子類——兩個文本組件。
先布局圖像組件,然后再將視圖(rightContainer)水平放置在它旁邊。我們為 rightContainer 指定的 flex 風格為1。這使得該視圖組件占據剩余空間,而不會遮擋圖像組件。如果你想看 flex 樣式的效果,可以為 rightContainer 添加以下代碼。
javascriptbackgroundColor: "red"
重新加載應用,你會看到空間被 rightContainer 樣式組件占滿。但它不會遮擋到其他組件。之所以沒有延伸到整個屏幕,是因為外容器設定了留白,而圖片也設置了右邊界。
刪除 rightContainer 的 flex 設定,再重新加載 App。現在組件只會占據適應其內容的足夠空間。
如果將 thumbnail 和 rightContainer 的 flex 樣式設置為2,它們將會占據同樣的寬度,比例為2:2(或者1:1)。你可以將其設置為任何需要的數值,比例會做出相應的改變。
你可以嘗試不同的比例以得到你想要的結果。讓我們回到之前為 rightContainer 添加紅色背景的那一步,繼續下面的步驟。
添加列表視圖React Native 有一個 ListView 組件,顯示數據的滾動行——也就是 iOS 中的表視圖。
首先,修改解構語句顯示我們添加的更多的組件。
javascriptvar {
Image,
StyleSheet,
Text,
View,
Component,
ListView,
TouchableHighlight
} = React;
添加以下代碼到樣式表中。
javascriptseparator: {
height: 1,
backgroundColor: "#ffffdffffd"
}
添加以下構造函數到 BookList 類。
javascriptconstructor(props) {
super(props);
this.state = {
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2
})
};
}
最后添加以下函數。
javascriptcomponentDidMount() {
var books = FAKE_BOOK_DATA;
this.setState({
dataSource: this.state.dataSource.cloneWithRows(books)
});
}
在構造函數中,我們創建了一個 ListView.DataSource 對象,并將其分配給 dataSource 屬性。DataSource 是一個接口,ListView 用它來確定在更新 UI 過程中哪些行發生了變化。我們提供了一個可以比較兩列是否相同的函數,用于確定數據列表是否變化。
當組件被加載到 UI 視圖時,會調用 componentDidMount()函數。該函數一旦被調用,我們用數據對象中的數據來設置 datasource 屬性。
你可以使用下面的代碼來修改 render()函數。
javascriptrender() {
return (
);
}
將下面的函數添加到 BookList 類。
javascriptrenderBook(book) {
return (
{book.volumeInfo.title}
{book.volumeInfo.authors}
);
}
以上代碼在 render()函數中創建了一個 ListView 組件。這里的 datasource 屬性與之前設定的數值一致。然后調用 renderBook()函數顯示 ListView 中的各列數據。
在 renderBook()函數中,我們使用 TouchableHighlight 組件。這是一個包裝組件,能讓視圖正確響應點擊行為。一旦點擊,該包裝組件的透明度就會降低,可以允許底層顏色透過,使得視圖變暗或變色。這樣的話,如果你點擊一個 ListView 行,你會看到高亮色,就像之前設置的選擇表格視圖單元格時的響應一樣。我們在分離器的底部添加一個樣式為 separator 的空視圖組件。這樣的設定下,視圖會出現一個灰色的水平線,便于分割每行項目。
重載該應用,你會看到只有一個單元的表格視圖。
現在將真實數據加載到應用中。
從文件中移除 FAKE_BOOK_DATA 變量,并添加以下代碼。這是加載數據的網址。
javascriptvar REQUEST_URL = "https://www.googleapis.com/books/v1/volumes?q=subject:fiction";
修改解析聲明。
javascriptvar {
Image,
StyleSheet,
Text,
View,
Component,
ListView,
TouchableHighlight,
ActivityIndicatorIOS
} = React;
添加以下樣式設定。
javascriptlistView: {
backgroundColor: "#F5FCFF"
},
loading: {
flex: 1,
alignItems: "center",
justifyContent: "center"
}
用下面的代碼修改構造函數。我們為組件的狀態對象添加另一個屬性,用來判斷視圖是否成功加載。
javascriptconstructor(props) {
super(props);
this.state = {
isLoading: true,
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2
})
};
}
按下列代碼修改 componentDidMount()函數,并添加 fetchData()函數。 fetchData()將調用 Google 圖書 API,當它響應操作時,會將獲取的數據設置為 DataSource 屬性,同時將 isLoading 設置為 true。
javascriptcomponentDidMount() {
this.fetchData();
}
fetchData() {
fetch(REQUEST_URL)
.then((response) => response.json())
.then((responseData) => {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(responseData.items),
isLoading: false
});
})
.done();
}
修改 render()函數并添加 renderLoadingView()。我們添加一個檢查 isLoading,如果它的值為 true,就回到由 renderLoadingView()返回的視圖。這個視圖顯示活動指示燈(一個轉盤),以及「正在載入書籍...」的字樣。當加載完成后,你應該看到表中的圖書清單。
javascriptrender() {
if (this.state.isLoading) {
return this.renderLoadingView();
}
return (
);
}
renderLoadingView() {
return (
Loading books...
);
}
重新加載應用,你會看到類似下圖的界面。
添加詳情視圖如果你點擊表中的一個單元格,單元格將會突出顯示,但不會響應其它操作。我們將添加一個詳情視圖,以顯示選擇當前書的詳細信息。
在項目中新建文件,并命名為 BookDetail.js。將下面的代碼貼在該文件中。
javascript"use strict";
var React = require("react-native");
var {
StyleSheet,
Text,
View,
Component,
Image
} = React;
var styles = StyleSheet.create({
container: {
marginTop: 75,
alignItems: "center"
},
image: {
width: 107,
height: 165,
padding: 10
},
description: {
padding: 10,
fontSize: 15,
color: "#656565"
}
});
class BookDetail extends Component {
render() {
var book = this.props.book;
var imageURI = (typeof book.volumeInfo.imageLinks !== "undefined") ? book.volumeInfo.imageLinks.thumbnail : "";
var description = (typeof book.volumeInfo.description !== "undefined") ? book.volumeInfo.description : "";
return (
{description}
);
}
}
module.exports = BookDetail;
上述代碼的大部分內容,我們之前已經討論過,這里不再贅述。之前沒接觸過的是 props 屬性,其用途是獲取數據。通過設置 props 屬性,將數據傳遞到這個類。在代碼中,我們先獲得數據,隨后用數據填充視圖。
需要注意的是,我們設置了容器的上邊界。如果不這樣的話,視圖將從屏幕的最頂部開始,從而導致某些元素被導航欄遮擋。
在 BookList.js 中添加以下代碼。
javascriptvar BookDetail = require("./BookDetail");
修改 BookList 類中 render()函數的 TouchableHighlight。
javascript this.showBookDetail(book)} underlayColor="#ffffdffffd">
上述代碼指定了當某列書籍被點擊時響應一個回調函數。粘貼下面的函數到該類。這將 BookDetail 視圖推送到導航堆棧中,并設置導航欄上的標題可見。然后將該選中行有關的圖書對象傳遞給 BookDetail 類。
javascriptshowBookDetail(book) {
this.props.navigator.push({
title: book.volumeInfo.title,
component: BookDetail,
passProps: {book}
});
}
重載該 App,你能看到當前選中書籍的詳細信息。
搜索現在已經完成了精選標簽的主從視圖,我們將繼續完善搜索選項卡,使用戶能夠利用 API 查詢想要的書籍。
打開 SearchBooks.js 并按下面的代碼修改。
javascript"use strict";
var React = require("react-native");
var SearchResults = require("./SearchResults");
var {
StyleSheet,
View,
Text,
Component,
TextInput,
TouchableHighlight,
ActivityIndicatorIOS
} = React;
var styles = StyleSheet.create({
container: {
marginTop: 65,
padding: 10
},
searchInput: {
height: 36,
marginTop: 10,
marginBottom: 10,
fontSize: 18,
borderWidth: 1,
flex: 1,
borderRadius: 4,
padding: 5
},
button: {
height: 36,
backgroundColor: "#f39c12",
borderRadius: 8,
justifyContent: "center",
marginTop: 15
},
buttonText: {
fontSize: 18,
color: "white",
alignSelf: "center"
},
instructions: {
fontSize: 18,
alignSelf: "center",
marginBottom: 15
},
fieldLabel: {
fontSize: 15,
marginTop: 15
},
errorMessage: {
fontSize: 15,
alignSelf: "center",
marginTop: 15,
color: "red"
}
});
class SearchBooks extends Component {
constructor(props) {
super(props);
this.state = {
bookAuthor: "",
bookTitle: "",
isLoading: false,
errorMessage: ""
};
}
render() {
var spinner = this.state.isLoading ?
( ) :
( );
return (
Search by book title and/or author
Book Title:
Author:
Search
{spinner}
{this.state.errorMessage}
);
}
bookTitleInput(event) {
this.setState({ bookTitle: event.nativeEvent.text });
}
bookAuthorInput(event) {
this.setState({ bookAuthor: event.nativeEvent.text });
}
searchBooks() {
this.fetchData();
}
fetchData() {
this.setState({ isLoading: true });
var baseURL = "https://www.googleapis.com/books/v1/volumes?q=";
if (this.state.bookAuthor !== "") {
baseURL += encodeURIComponent("inauthor:" + this.state.bookAuthor);
}
if (this.state.bookTitle !== "") {
baseURL += (this.state.bookAuthor === "") ? encodeURIComponent("intitle:" + this.state.bookTitle) : encodeURIComponent("+intitle:" + this.state.bookTitle);
}
console.log("URL: >>> " + baseURL);
fetch(baseURL)
.then((response) => response.json())
.then((responseData) => {
this.setState({ isLoading: false});
if (responseData.items) {
this.props.navigator.push({
title: "Search Results",
component: SearchResults,
passProps: {books: responseData.items}
});
} else {
this.setState({ errorMessage: "No results found"});
}
})
.catch(error =>
this.setState({
isLoading: false,
errorMessage: error
}))
.done();
}
}
module.exports = SearchBooks;
述代碼中,我們在構造函數中設置了一些屬性:bookAuthor、bookTitle、isLoading 和 errorMessage。下面簡要介紹下如何使用。
在 render()方法中,我們需要檢查 isLoading 值是否為 true,如果是則建立一個活動指示器,否則則創建一個空視圖(后面會用到)。然后,我們創建一個被用來插入查詢的搜索表單。TextInput 用于接收輸入。當組件的值改變時(例如用戶鍵入一些文本),將會調用 TextInput 組件,同時為組件指定一個回調函數。在調用時,回調函數 bookTitleInput()和 bookAuthorInput()利用用戶輸入的數據將設置 bookAuthor 和 bookTitles 屬性。當用戶按下搜索按鈕時,searchBooks()被調用。需要注意的是,React Native 沒有按鈕組件。所以,我們使用 TouchableHighlight 來代替,并用文本包裝,使它的樣式看起來像一個按鈕。當按下搜索按鈕時,根據輸入的數據構成一個 URL。用戶可以通過書名、作者或書名+作者進行搜索。如果結果成功返回,SearchResult 將被推到導航堆棧,否則提示錯誤消息。我們還將響應數據傳遞給 SearchResults 類。
創建一個文件并命名為 SearchResults.js,將下列代碼貼進去。
javascript"use strict";
var React = require("react-native");
var BookDetail = require("./BookDetail");
var {
StyleSheet,
View,
Text,
Component,
TouchableHighlight,
Image,
ListView
} = React;
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center"
},
title: {
fontSize: 20,
marginBottom: 8
},
author: {
color: "#656565"
},
separator: {
height: 1,
backgroundColor: "#ffffdffffd"
},
listView: {
backgroundColor: "#F5FCFF"
},
cellContainer: {
flex: 1,
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
backgroundColor: "#F5FCFF",
padding: 10
},
thumbnail: {
width: 53,
height: 81,
marginRight: 10
},
rightContainer: {
flex: 1
}
});
class SearchResults extends Component {
constructor(props) {
super(props);
var dataSource = new ListView.DataSource(
{rowHasChanged: (row1, row2) => row1 !== row2});
this.state = {
dataSource: dataSource.cloneWithRows(this.props.books)
};
}
render() {
return (
);
}
renderBook(book) {
var imageURI = (typeof book.volumeInfo.imageLinks !== "undefined") ? book.volumeInfo.imageLinks.thumbnail : "";
return (
this.showBookDetail(book)}
underlayColor="#ffffdffffd">
{book.volumeInfo.title}
{book.volumeInfo.authors}
);
}
showBookDetail(book) {
this.props.navigator.push({
title: book.volumeInfo.title,
component: BookDetail,
passProps: {book}
});
}
}
module.exports = SearchResults;
以上代碼之前已經討論過,也不再贅述。代碼中獲得數據,并將數據通過 props 屬性傳遞到類,同時創建填充了數據的 ListView。
作者注意到,在 API中,當你按作者搜索時,結果不一定是書的數據,而是作者自身的信息。這意味著某些行的 book.volumeInfo.imageLinks.thumbnail 和 book.volumeInfo.description 有未定義的值。所以我們稍作檢查,如果沒有圖像則顯示一個空視圖。否則,我們的應用將試圖加載不存在的圖像,這樣會容易引發崩潰。
我們使用之前用過的 BookDetail 組件,來顯示每本書的詳細信息。如圖所示,打開 BookDetail.js 并修改 render()函數。在用數據填充視圖之前,檢查傳入的數據是否有相關圖像和詳細信息。如果嘗試載入的書籍沒有詳情或圖片,對應的區域將是空白。你可以向用戶提示一個錯誤信息,在此我們省略該步驟。
javascriptrender() {
var book = this.props.book;
var imageURI = (typeof book.volumeInfo.imageLinks !== "undefined") ? book.volumeInfo.imageLinks.thumbnail : "";
var description = (typeof book.volumeInfo.description !== "undefined") ? book.volumeInfo.description : "";
return (
{description}
);
}
重載應用,會看到搜索書籍的界面。
結束語盡管仍在不斷完善,React Native 看起來很有希望成為構建移動應用的另一種選擇。它為 Web 開發人員開啟了一扇門,讓他們能夠在移動開發領域一探究竟。同時為移動開發者提供了一種簡化開發流程的新方式。
隨著項目的發展,讓我們拭目以待 React Native 和應用開發(iOS和Android ——或者別的平臺)將會碰撞出什么樣的火花。同時,如果你需要進一步確認網絡技術是否能用于實現真正的原生體驗,你可以看看這些由 React Native 構建的應用:Facebook Ads Manager(完全由 React Native 構建)以及 Facebook Groups(React Native 和 Objective-C 構建的混合應用)。
「學習一次,在任何地方應用」。單這一句足以證明學習 React Native 框架的意義。
你可以在這里下載完整示例項目。
為了更進一步了解 React Native,你可以參考下列視頻和資料。
React Native 簡介
深入 React Native
React Native 接力:將現代網頁技術推向移動開發世界
你可以在這下載 Xcode 項目,僅供參考。(完結)
React Native 簡介:用 JavaScript 搭建 iOS 應用 (1)
原文地址:http://www.appcoda.com/react-native-introduction/
本文系 OneAPM 工程師編譯整理。想閱讀更多技術文章,請訪問 OneAPM 官方博客。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/85875.html
摘要:利用來搭建代碼。雖然這不是安裝的唯一方式,但我發現,是非常好用的包管理器。終端窗口打開后,會啟動,并由服務器處理以上請求。面對這種情況時,需要關閉終端窗口,停止在上的應用,并重新運行。使用設定應用的用戶界面。命名這兩個文件為和。 【編者按】本篇文章的作者是 Joyce Echessa——渥合數位服務創辦人,畢業于臺灣大學,近年來專注于協助客戶進行 App 軟體以及網站開發。本篇文章中,...
摘要:面向對象三大特征繼承性多態性封裝性接口。第五階段封裝一個屬于自己的框架框架封裝基礎事件流冒泡捕獲事件對象事件框架選擇框架。核心模塊和對象全局對象,,,事件驅動,事件發射器加密解密,路徑操作,序列化和反序列化文件流操作服務端與客戶端。 第一階段: HTML+CSS:HTML進階、CSS進階、div+css布局、HTML+css整站開發、 JavaScript基礎:Js基礎教程、js內置對...
閱讀 3319·2021-11-23 09:51
閱讀 2436·2021-11-09 09:46
閱讀 1476·2019-08-30 15:54
閱讀 3121·2019-08-30 14:22
閱讀 2909·2019-08-29 12:40
閱讀 1629·2019-08-26 10:33
閱讀 1774·2019-08-23 17:09
閱讀 1553·2019-08-23 16:11