前言
- 本文有配套視頻,可以酌情觀看。
- 文中內容因各人理解不同,可能會有所偏差,歡迎朋友們聯系我。
- 文中所有內容僅供學習交流之用,不可用于商業用途,如因此引起的相關法律法規責任,與我無關。
- 如文中內容對您造成不便,煩請聯系 277511806@qq.com 處理,謝謝。
- 轉載麻煩注明出處,謝謝。
本篇資源:鏈接: https://pan.baidu.com/s/1pKActHh 密碼: n6ee
源碼托管到 github 上,需要源碼的 點我下載,喜歡的話記得 Star,謝謝!
數據持久化
數據持久化是移動端的一個重要部分,剛發現
Realm
原來已經支持React-Native
了,那么這邊另起一篇專門介紹兩種常用的存儲方式 ———— React-Native 之 數據持久化這邊沒有發現
官方
有將商品數據
做本地緩存的功能,為了讓大家知道怎么做,我們就簡單地來實驗一下,具體邏輯在每個產品中都或多或少有些差異,這個朋友們就根據所學的靈活變通一下就可以了!-
首先,在為了方便使用,也為了減少第三方框架對工程的 “污染”,我們需要對框架進行一次
基礎
的封裝。var RealmBase = {}; import Realm from 'realm'; const HomeSchame = { name:'HomeData', properties:{ id:'int', title:'string', image:'string', mall:'string', pubtime:'string', fromsite:'string', } }; const HTSchame = { name:'HTData', properties:{ id:'int', title:'string', image:'string', mall:'string', pubtime:'string', fromsite:'string', } }; // 初始化realm let realm = new Realm({schema:[HomeSchame, HTSchame]}); // 增加 RealmBase.create = function (schame, data) { realm.write(() => { for (let i = 0; i<data.length; i++) { let temp = data[i]; realm.create(schame, {id:temp.id, title:temp.title, image:temp.image, mall:temp.mall, pubtime:temp.pubtime, fromsite:temp.fromsite}); } }) } // 查詢全部數據 RealmBase.loadAll = function (schame) { return realm.objects(schame); } // 條件查詢 RealmBase.filtered = function (schame, filtered) { // 獲取對象 let objects = realm.objects(schame); // 篩選 let object = objects.filtered(filtered); if (object) { // 有對象 return object; }else { return '未找到數據'; } } // 刪除所有數據 RealmBase.removeAllData = function (schame) { realm.write(() => { // 獲取對象 let objects = realm.objects(schame); // 刪除表 realm.delete(objects); }) } global.RealmBase = RealmBase;
-
經過簡單封裝后,我們還需要引用一下框架,引用框架,我們就放到
main
文件內,這樣我們就可以確保全局變量
是在我們使用它之前就被調用過,避免找不到對象的錯誤(我們也同時將HTTPBase
修改為全局,方便使用)。import RealmStorage from '../storage/realmStorage';
-
現在我們就來做下
本地持久化
實驗,這邊我們的邏輯就是,當網絡出現問題的時候,每次都會進到 catch 中返回錯誤 code 告訴我們,出現了什么問題(既然進到這里了,是不是數據無論如何都不會加載成功?),那么我們就可以在這里取出
本地數據,然后展示出來,那么數據的應該在哪里保存呢?這個我們只需要在每次刷新
完成并且渲染完成后,保存一下數據就可以了。// 加載最新數據網絡請求 loadData(resolve) { let params = {"count" : 10 }; HTTPBase.get('https://guangdiu.com/api/getlist.php', params) .then((responseData) => { // 清空數組 this.data = []; // 拼接數據 this.data = this.data.concat(responseData.data); // 重新渲染 this.setState({ dataSource: this.state.dataSource.cloneWithRows(this.data), loaded:true, }); // 關閉刷新動畫 if (resolve !== undefined){ setTimeout(() => { resolve(); }, 1000); } // 存儲數組中最后一個元素的id let cnlastID = responseData.data[responseData.data.length - 1].id; AsyncStorage.setItem('cnlastID', cnlastID.toString()); // 存儲數組中第一個元素的id let cnfirstID = responseData.data[0].id; AsyncStorage.setItem('cnfirstID', cnfirstID.toString()); // 清楚本地存儲的數據 RealmBase.removeAllData('HomeData'); // 存儲數據到本地 RealmBase.create('HomeData', responseData.data); }) .catch((error) => { // 拿到本地存儲的數據,展示出來,如果沒有存儲,那就顯示無數據頁面 this.data = RealmBase.loadAll('HomeData'); // 重新渲染 this.setState({ dataSource: this.state.dataSource.cloneWithRows(this.data), loaded:true, }); }) }
到這里,我們就完成了
數據本地持久化
并且成功將數據取出,這邊數據持久化的實驗就完成了。
譯注:
有關
realm
的配置,可轉到 React-Native 之 數據持久化 查看,這邊不多贅述。當我們完全配置完
realm
后,會發現整個工程多出將近500M+
,不用擔心,這些只不過是realm
的依賴庫,等我們后面打包
后就不會有這么多東西了。我們已經使用了
git
進行對代碼進行托管,那么,如果從倉庫拉取最新數據時,是不是還是要重新配置node_modules
文件夾,這個就很麻煩了,我們可以備份一下node_modules
文件夾,將它拷貝
進工程就可以使用了。
.gitignore 語法
-
gitignore
文件內包含了需要忽略或保留的文件的一些配置,靈活使用可以減少我們的工作量,但是里面的內容是什么意思呢?這邊也給大家說下:- /:表示目錄
- *:為通配多個字符
- ?:通配單個字符
- []:包含單個字符的匹配列表
- !:表示不忽略匹配到的文件和目錄
- // 示例
- // 忽略ios文件夾下的所有內容
-
知道語法后,我們是不是就能看懂 工程中
gitignore
文件的內容了,舉個栗子:# Xcode // 注釋,說明這是 Xcode 配置 # build/ // 忽略 build 文件夾下所有內容 *.pbxuser // 忽略以 .pbxuser 為后綴的文件 !default.pbxuser // 除 default.pbxuser 文件外 *.mode1v3 // 忽略以 .mode1v3 為后綴的文件 !default.mode1v3 // 除 default.mode1v3 文件外 # node.js // 注釋,說明這是 node 配置 # node_modules/ // 忽略 node_modules 文件夾內所有內容 npm-debug.log // 忽略 npm-debug.log yarn-error.log // 忽略 yarn-error.log
好了,就介紹到這里,希望可以幫到有需要的朋友,需要學習更多關于
git
的內容,可以到 git介紹與使用 查看學習。
自定義詳情cell
-
到這邊可能有人會想,前面不是已經自定義了
cell
了,為什么不直接在前面自定義的cell
里面再添加一些操作,使所有的cell
共用同一套組件?其實考慮到下面幾點原因:可以看到
半小時熱門的cell
和三大模塊的cell
區別在于少了優惠平臺和數據提供平臺
這一欄的2個控件,其他地方是一樣的,如果我們做到一起那也是可以的,但是這樣會造成一個組件里面擔負過多業務邏輯,在數據量少
是沒關系,但是數據量一多
那么需要渲染的成本就會增加,也就會嚴重影響到性能。如果我們分開使用,那么只是增加了組件,而且組件內部處理的業務邏輯變少了,這樣也減少了我們后期的維護成本。
從結構上來說,這樣也更為的清晰,減少開發人員之間的溝通成本。
-
首先,還是一樣,我們先來創建
GDCommunalCell
文件,并且我們將前面自定義cell
里面的內容copy
一下,放到這個文件中,并進行相應修改:export default class GDCommunalCell extends Component { static propTypes = { image:PropTypes.string, title:PropTypes.string, mall:PropTypes.string, pubTime:PropTypes.string, fromSite:PropTypes.string, }; renderDate(pubTime, fromSite) { // 時間差的計算 let minute = 1000 * 60; // 1分鐘 let hour = minute * 60; // 1小時 let day = hour * 24; // 1天 let week = day * 7; // 1周 let month = day * 30; // 1個月 // 計算時間差 let now = new Date().getTime(); // 獲取當前時間 let diffValue = now - Date.parse(pubTime.replace(/-/gi, "/")); if (diffValue < 0) return; let monthC = diffValue/month; // 相差了幾個月 let weekC = diffValue/week; // 相差幾周 let dayC = diffValue/day; // 相差幾天 let hourC = diffValue/hour // 相差幾小時 let minuteC = diffValue/minute; // 相差幾分鐘 let result; if (monthC >= 1) { result = parseInt(monthC) + "月前"; }else if (weekC >= 1) { result = parseInt(weekC) + "周前"; }else if (dayC >= 1) { result = parseInt(dayC) + "天前"; }else if (hourC >= 1) { result = parseInt(hourC) + "小時前"; }else if (minuteC >= 1) { result = parseInt(minuteC) + "分鐘前"; }else result = "剛剛"; return result + ' · ' + fromSite; } render() { return ( <View style={styles.container}> {/* 左邊圖片 */} <Image source={{uri:this.props.image === '' ? 'defaullt_thumb_83x83' : this.props.image}} style={styles.imageStyle} /> {/* 中間 */} <View style={styles.centerViewStyle}> {/* 標題 */} <View> <Text numberOfLines={3} style={styles.titleStyle}>{this.props.title}</Text> </View> {/* 詳情 */} <View style={styles.detailViewStyle}> {/* 平臺 */} <Text style={styles.detailMallStyle}>{this.props.mall}</Text> {/* 時間 + 來源 */} <Text style={styles.timeStyle}>{this.renderDate(this.props.pubTime, this.props.fromSite)}</Text> </View> </View> {/* 右邊的箭頭 */} <Image source={{uri:'icon_cell_rightArrow'}} style={styles.arrowStyle} /> </View> ); } } const styles = StyleSheet.create({ container: { flexDirection:'row', alignItems:'center', justifyContent:'space-between', backgroundColor:'white', height:100, width:width, borderBottomWidth:0.5, borderBottomColor:'gray', marginLeft:15 }, imageStyle: { width:70, height:70, }, centerViewStyle: { height:70, justifyContent:'space-around', }, titleStyle: { width:width * 0.65, }, detailViewStyle: { flexDirection:'row', justifyContent:'space-between', alignItems:'center' }, detailMallStyle: { fontSize:12, color:'green', }, timeStyle: { fontSize:12, color:'gray', }, arrowStyle: { width:10, height:10, marginRight:30, } });
OK,這邊完成了,我們到首頁中試一下是不是好使的。
譯注:
這邊需要注意的是時間的轉化,方式有很多,這邊就以最直接的方式來計算。
還有需要注意的是在
JAVA
中,獲取到的月份是和我們現在的月份少1個月的
,這是因為JAVA
的月份是從0
開始,Javascript
也是一樣的。
小時風云榜
這個模塊和首頁、海淘請求數據方面是類似的,不同在于這里需要我們根據不同的時間段來進行相對應的請求,這邊參考視頻吧,直接上完整代碼。
還是
copy
首頁或者海淘的代碼,修改請求這部分代碼:
export default class GDHourList extends Component {
// 構造
constructor(props) {
super(props);
// 初始狀態
this.state = {
dataSource: new ListView.DataSource({rowHasChanged:(r1, r2) => r1 !== r2}),
loaded:false,
prompt:'',
};
this.nexthourhour = '';
this.nexthourdate = '';
this.lasthourhour = '';
this.lasthourdate = '';
this.loadData = this.loadData.bind(this);
}
// 加載最新數據網絡請求
loadData(resolve, date, hour) {
let params = {};
if (date) {
params = {
"date" : date,
"hour" : hour
}
}
HTTPBase.get('http://guangdiu.com/api/getranklist.php', params)
.then((responseData) => {
// 重新渲染
this.setState({
dataSource: this.state.dataSource.cloneWithRows(responseData.data),
loaded:true,
prompt:responseData.displaydate + responseData.rankhour + '點檔' + '(' + responseData.rankduring + ')'
});
// 關閉刷新動畫
if (resolve !== undefined){
setTimeout(() => {
resolve();
}, 1000);
}
// 暫時保留一些數據
this.nexthourhour = responseData.nexthourhour;
this.nexthourdate = responseData.nexthourdate;
this.lasthourhour = responseData.lasthourhour;
this.lasthourdate = responseData.lasthourdate;
})
.catch((error) => {
})
}
// 跳轉到設置
pushToSettings() {
this.props.navigator.push({
component:Settings,
})
}
// 返回中間標題
renderTitleItem() {
return(
<Image source={{uri:'navtitle_rank_106x20'}} style={styles.navbarTitleItemStyle} />
);
}
// 返回右邊按鈕
renderRightItem() {
return(
<TouchableOpacity
onPress={()=>{this.pushToSettings()}}
>
<Text style={styles.navbarRightItemStyle}>設置</Text>
</TouchableOpacity>
);
}
// 根據網絡狀態決定是否渲染 listview
renderListView() {
if (this.state.loaded === false) {
return(
<NoDataView />
);
}else {
return(
<PullList
onPullRelease={(resolve) => this.loadData(resolve)}
dataSource={this.state.dataSource}
renderRow={this.renderRow.bind(this)}
showsHorizontalScrollIndicator={false}
style={styles.listViewStyle}
initialListSize={5}
/>
);
}
}
// 跳轉到詳情頁
pushToDetail(value) {
this.props.navigator.push({
component:CommunalDetail,
params: {
url: 'https://guangdiu.com/api/showdetail.php' + '?' + 'id=' + value
}
})
}
// 返回每一行cell的樣式
renderRow(rowData) {
return(
<TouchableOpacity
onPress={() => this.pushToDetail(rowData.id)}
>
<CommunalCell
image={rowData.image}
title={rowData.title}
mall={rowData.mall}
pubTime={rowData.pubtime}
fromSite={rowData.fromsite}
/>
</TouchableOpacity>
);
}
componentDidMount() {
this.loadData();
}
lastHour() {
this.loadData(undefined, this.lasthourdate, this.lasthourhour);
}
nextHour() {
this.loadData(undefined, this.nexthourdate, this.nexthourhour);
}
render() {
return (
<View style={styles.container}>
{/* 導航欄樣式 */}
<CommunalNavBar
titleItem = {() => this.renderTitleItem()}
rightItem = {() => this.renderRightItem()}
/>
{/* 提醒欄 */}
<View style={styles.promptViewStyle}>
<Text>{this.state.prompt}</Text>
</View>
{/* 根據網絡狀態決定是否渲染 listview */}
{this.renderListView()}
{/* 操作欄 */}
<View style={styles.operationViewStyle}>
<TouchableOpacity
onPress={() => this.lastHour()}
>
<Text style={{marginRight:10, fontSize:17, color:'green'}}>{"< " + "上1小時"}</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => this.nextHour()}
>
<Text style={{marginLeft:10, fontSize:17, color:'green'}}>{"下1小時" + " >"}</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
backgroundColor: 'white',
},
navbarTitleItemStyle: {
width:106,
height:20,
marginLeft:50
},
navbarRightItemStyle: {
fontSize:17,
color:'rgba(123,178,114,1.0)',
marginRight:15,
},
promptViewStyle: {
width:width,
height:44,
alignItems:'center',
justifyContent:'center',
backgroundColor:'rgba(251,251,251,1.0)',
},
operationViewStyle: {
width:width,
height:44,
flexDirection:'row',
justifyContent:'center',
alignItems:'center',
},
});
首頁篩選功能
從圖中,我們可以看出篩選的下拉菜單類似
九宮格
,這個我們在 React-Native 之 ListView使用 中有這樣的案例,不清楚的可以再回去看一下,所以這邊我們也使用ListView
實現。在做之前,我們需要先配置一下,將壓縮包內的
HomeSiftData文件
和HTSiftData
文件放到工程內。接著我們就要來完成這個篩選組件,代碼如下:
export default class GDCommunalSiftMenu extends Component {
static defaultProps = {
removeModal:{},
loadSiftData:{}
};
static propTypes = {
data:PropTypes.array,
};
// 構造
constructor(props) {
super(props);
// 初始狀態
this.state = {
dataSource: new ListView.DataSource({rowHasChanged:(r1, r2) => r1 !== r2})
};
}
// 退出
popToHome(data) {
this.props.removeModal(data);
}
// 點擊事件
siftData(mall, cate) {
this.props.loadSiftData(mall, cate);
this.popToHome(false);
}
// 處理數據
loadData() {
let data = [];
for (let i = 0; i<this.props.data.length; i++) {
data.push(this.props.data[i]);
}
// 重新渲染
this.setState({
dataSource: this.state.dataSource.cloneWithRows(data),
})
}
renderRow(rowData) {
return(
<View style={styles.itemViewStyle}>
<TouchableOpacity
onPress={() => this.siftData(rowData.mall, rowData.cate)}
>
<View style={styles.itemViewStyle}>
<Image source={{uri:rowData.image}} style={styles.itemImageStyle} />
<Text>{rowData.title}</Text>
</View>
</TouchableOpacity>
</View>
)
}
componentDidMount() {
this.loadData();
}
render() {
return(
<TouchableOpacity
onPress={() => this.popToHome(false)}
activeOpacity={1}
>
<View style={styles.container}>
{/* 菜單內容 */}
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow.bind(this)}
contentContainerStyle={styles.contentViewStyle}
initialListSize={16}
/>
</View>
</TouchableOpacity>
)
}
}
const styles = StyleSheet.create({
container: {
width:width,
height:height
},
contentViewStyle: {
flexDirection:'row',
flexWrap:'wrap',
width: width,
top:Platform.OS === 'ios' ? 64 : 44,
},
itemViewStyle: {
width:width * 0.25,
height:70,
backgroundColor:'rgba(249,249,249,1.0)',
justifyContent:'center',
alignItems:'center'
},
itemImageStyle: {
width:40,
height:40
}
});
- 我們點擊某個平臺,就要進行相應的請求,然后重新渲染 首頁的ListView ,方式如下:
// 加載最新數據網絡請求
loadSiftData(mall, cate) {
let params = {};
if (mall === "" && cate === "") { // 全部
this.loadData(undefined);
return;
}
if (mall === "") { // cate 有值
params = {
"cate" : cate,
"country" : "us"
};
}else {
params = {
"mall" : mall,
"country" : "us"
};
}
HTTPBase.get('https://guangdiu.com/api/getlist.php', params)
.then((responseData) => {
// 清空數組
this.data = [];
// 拼接數據
this.data = this.data.concat(responseData.data);
// 重新渲染
this.setState({
dataSource: this.state.dataSource.cloneWithRows(this.data),
loaded:true,
});
// 存儲數組中最后一個元素的id
let cnlastID = responseData.data[responseData.data.length - 1].id;
AsyncStorage.setItem('cnlastID', cnlastID.toString());
})
.catch((error) => {
})
}
- 至此,首頁的篩選功能也完成了,在
海淘模塊
內也使用一下就可以了。
搜索模塊
點擊 首頁或者海淘 右側按鈕,我們跳轉到搜索模塊,解析圖奉上:
-
根據解析圖我們添加相應子組件。
export default class GDHome extends Component { // 構造 constructor(props) { super(props); // 初始狀態 this.state = { dataSource: new ListView.DataSource({rowHasChanged:(r1, r2) => r1 !== r2}), loaded:false, isModal:false }; this.data = []; this.changeText = ''; this.loadData = this.loadData.bind(this); this.loadMore = this.loadMore.bind(this); } // 加載最新數據網絡請求 loadData(resolve) { if (!this.changeText) return; let params = { "q" : this.changeText }; HTTPBase.get('http://guangdiu.com/api/getresult.php', params) .then((responseData) => { // 清空數組 this.data = []; // 拼接數據 this.data = this.data.concat(responseData.data); // 重新渲染 this.setState({ dataSource: this.state.dataSource.cloneWithRows(this.data), loaded:true, }); // 關閉刷新動畫 if (resolve !== undefined){ setTimeout(() => { resolve(); }, 1000); } // 存儲數組中最后一個元素的id let searchLastID = responseData.data[responseData.data.length - 1].id; AsyncStorage.setItem('searchLastID', searchLastID.toString()); }) .catch((error) => { }) } // 加載更多數據的網絡請求 loadMoreData(value) { let params = { "q" : this.changeText, "sinceid" : value }; HTTPBase.get('http://guangdiu.com/api/getresult.php', params) .then((responseData) => { // 拼接數據 this.data = this.data.concat(responseData.data); this.setState({ dataSource: this.state.dataSource.cloneWithRows(this.data), loaded:true, }); // 存儲數組中最后一個元素的id let searchLastID = responseData.data[responseData.data.length - 1].id; AsyncStorage.setItem('searchLastID', searchLastID.toString()); }) .catch((error) => { }) } // 加載更多數據操作 loadMore() { // 讀取id AsyncStorage.getItem('searchLastID') .then((value) => { // 數據加載操作 this.loadMoreData(value); }) } // 返回 pop() { // 回收鍵盤 dismissKeyboard(); this.props.navigator.pop(); } // 返回左邊按鈕 renderLeftItem() { return( <TouchableOpacity onPress={() => {this.pop()}} > <View style={{flexDirection:'row', alignItems:'center'}}> <Image source={{uri:'back'}} style={styles.navbarLeftItemStyle} /> <Text>返回</Text> </View> </TouchableOpacity> ); } // 返回中間按鈕 renderTitleItem() { return( <Text style={styles.navbarTitleItemStyle}>搜索全網折扣</Text> ); } // ListView尾部 renderFooter() { return ( <View style={{height: 100}}> <ActivityIndicator /> </View> ); } // 根據網絡狀態決定是否渲染 listview renderListView() { if (this.state.loaded === false) { return( <NoDataView /> ); }else { return( <PullList onPullRelease={(resolve) => this.loadData(resolve)} dataSource={this.state.dataSource} renderRow={this.renderRow.bind(this)} showsHorizontalScrollIndicator={false} style={styles.listViewStyle} initialListSize={5} renderHeader={this.renderHeader} onEndReached={this.loadMore} onEndReachedThreshold={60} renderFooter={this.renderFooter} /> ); } } // 跳轉到詳情頁 pushToDetail(value) { this.props.navigator.push({ component:CommunalDetail, params: { url: 'https://guangdiu.com/api/showdetail.php' + '?' + 'id=' + value } }) } // 返回每一行cell的樣式 renderRow(rowData) { return( <TouchableOpacity onPress={() => this.pushToDetail(rowData.id)} > <CommunalCell image={rowData.image} title={rowData.title} mall={rowData.mall} pubTime={rowData.pubtime} fromSite={rowData.fromsite} /> </TouchableOpacity> ); } render() { return ( <View style={styles.container}> {/* 導航欄樣式 */} <CommunalNavBar leftItem = {() => this.renderLeftItem()} titleItem = {() => this.renderTitleItem()} /> {/* 頂部工具欄 */} <View style={styles.toolsViewStyle} > {/* 左邊 */} <View style={styles.inputViewStyle} > <Image source={{uri:'search_icon_20x20'}} style={styles.searchImageStyle} /> <TextInput style={styles.textInputStyle} keyboardType="default" placeholder="請輸入搜索商品關鍵字" placeholderTextColor='gray' autoFocus={true} clearButtonMode="while-editing" onChangeText={(text) => {this.changeText = text}} onEndEditing={() => this.loadData()} /> </View> {/* 右邊 */} <View style={{marginRight:10}}> <TouchableOpacity onPress={() => this.pop()} > <Text style={{color:'green'}}>取消</Text> </TouchableOpacity> </View> </View> {/* 根據網絡狀態決定是否渲染 listview */} {this.renderListView()} </View> ); } } const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center', backgroundColor: 'white', }, navbarLeftItemStyle: { width:20, height:20, marginLeft:15, }, navbarTitleItemStyle: { fontSize:17, color:'black', marginRight:50 }, navbarRightItemStyle: { width:20, height:20, marginRight:15, }, toolsViewStyle: { width:width, height:44, flexDirection:'row', alignItems:'center', justifyContent:'space-between', }, inputViewStyle: { height:35, flexDirection:'row', alignItems:'center', justifyContent:'center', backgroundColor:'rgba(239,239,241,1.0)', marginLeft:10, borderRadius:5 }, searchImageStyle: { width:15, height:15, marginLeft:8 }, textInputStyle: { width:width * 0.75, height:35, marginLeft:8 }, listViewStyle: { width:width, }, });
設置
在 小時風云榜 模塊,我們還有設置模塊沒有做,這邊也快速來做一下
-
從圖中可以看出,這又是不一樣的 cell樣式 ,不過通過前面的經驗,知道怎么來自定義了吧:
export default class GDSettingsCell extends Component { static propTypes = { leftTitle:PropTypes.string, isShowSwitch:PropTypes.bool, }; // 構造 constructor(props) { super(props); // 初始狀態 this.state = { isOn:false, }; } // 返回需要的組件 renderRightContent() { let component; if (this.props.isShowSwitch) { // 顯示 Switch 按鈕 component = <Switch value={this.state.isOn} onValueChange={() => {this.setState({isOn: !this.state.isOn})}} /> }else { component = <Image source={{uri:'icon_cell_rightArrow'}} style={styles.arrowStyle} /> } return( component ) } render() { return( <View style={styles.container}> {/* 左邊 */} <View> <Text>{this.props.leftTitle}</Text> </View> {/* 右邊 */} <View style={styles.rightViewStyle}> {this.renderRightContent()} </View> </View> ) } } const styles = StyleSheet.create({ container: { flex:1, flexDirection:'row', height:Platform.OS === 'ios' ? 44 : 36, justifyContent:'space-between', alignItems:'center', borderBottomColor:'gray', borderBottomWidth:0.5, marginLeft:15, }, rightViewStyle:{ marginRight:15, }, arrowStyle: { width:10, height:10, } });
-
自定義完成,來試下好不好用:
export default class GDSettings extends Component { // 返回 pop() { this.props.navigator.pop(); } // 返回左邊按鈕 renderLeftItem() { return( <TouchableOpacity onPress={() => {this.pop()}} > <View style={{flexDirection:'row', alignItems:'center'}}> <Image source={{uri:'back'}} style={styles.navbarLeftItemStyle} /> <Text>返回</Text> </View> </TouchableOpacity> ); } // 返回中間按鈕 renderTitleItem() { return( <Text style={styles.navbarTitleItemStyle}>設置</Text> ); } render() { return( <View style={styles.container}> {/* 導航欄樣式 */} <CommunalNavBar leftItem = {() => this.renderLeftItem()} titleItem = {() => this.renderTitleItem()} /> {/* 內容 */} <ScrollView style={styles.scollViewStyle} > {/* 第一個cell */} <SettingsCell leftTitle="淘寶天貓快捷下單" isShowSwitch={true} /> {/* 第二個cell */} <SettingsCell leftTitle="清理圖片緩存" isShowSwitch={false} /> </ScrollView> </View> ) } } const styles = StyleSheet.create({ container: { flex:1 }, navbarLeftItemStyle: { width:20, height:20, marginLeft:15, }, navbarTitleItemStyle: { fontSize:17, color:'black', marginRight:50 }, scollViewStyle: { backgroundColor:'white', }, });