RN實現商城案例

案例說明

  1. 案例界面
    項目
    項目由搜索框+輪播圖+一個可以下拉的列表組成;不涉及後臺接口(均爲模擬數據)
  2. 案例所用技術
    需要使用react native腳手架+expo;主要是對react native的組件進行學習
  3. 文檔
    https://reactnative.cn/docs/tutorial/

使用腳手架創建空的一個項目

# 沒有腳手架自行安裝腳手架
create-react-native-app myStore
# 打開項目並啓動
cd myStore
expo start

在這裏插入圖片描述

安裝模擬器

啓動模擬器,將我們的expo軟件拖動進去,此時它會幫我們安裝這個軟件;安裝完成後打開
軟件地址
我們回到項目,按下a鍵會自動編譯到模擬器中(如果報錯可能是我們的版本問題;打開我們的app.json-- 更改 “sdkVersion”: “31.0.0”,)

首頁架子

React Native採用的是伸縮佈局;而且它屏幕不會因爲像瀏覽器那樣超出會出現滾動條…所以我們可以使用flex屬性來使其佔滿達到我們想要的效果

首頁組件抽取(在項目根目錄下創建components)
  • /component/Searchbar.js
import React from 'react'
import {View,Text,StyleSheet} from 'react-native'
export default class Searchbar extends React.Component{
    render(){
        return <View style={styles.searchbar}>
            <Text>搜素</Text>
        </View>
    }
}
const styles = StyleSheet.create({
    searchbar:{
        height:40,
        backgroundColor: 'red'
    }
})
  • /component/Adverticement.js
import React from 'react'
import {View,Text,StyleSheet} from 'react-native'
export default class Adverticement extends React.Component{
    render(){
        return <View style={styles.adverticement}>
            <Text>廣告</Text>
        </View>
    }
}
const styles = StyleSheet.create({
    adverticement:{
        height:200,
        backgroundColor: 'green'
    }
})
  • /component/Products.js
import React from 'react'
import {View,Text,StyleSheet} from 'react-native'
export default class Products extends React.Component{
    render(){
        return <View style={styles.products}>
            <Text>產品</Text>
        </View>
    }
}
const styles = StyleSheet.create({
    products:{
        flex: 1,
        backgroundColor: 'blue'
    }
})

導入到我們的App.js

import React from 'react';
import { StyleSheet, Text, View } from 'react-native';

// 導入組件
import Adverticement from './components/Adverticement.js'
import Products from './components/Products.js'
import SearchBar from './components/SearchBar.js'
export default class App extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <SearchBar></SearchBar>
        <Adverticement></Adverticement>
        <Products></Products>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff'
  },
});

佈局

狀態欄的配置

我們可以看到頂部狀態欄遮擋住我們的頁面;我們此時可以使用StatusBar控制應用狀態欄的組件進行配置。

由於StatusBar可以在任意視圖中加載,且後加載的設置會覆蓋先前的設置。因此在配合導航器使用時,請務必考慮清楚StatusBar的放置順序。

      <View style={styles.container}>
        <StatusBar
          hidden={false}
          animated={true}
          backgroundColor="#ccc"
          barStyle="light-content"
          translucent={false}
        ></StatusBar>
        <SearchBar></SearchBar>
        <Adverticement></Adverticement>
        <Products></Products>
      </View>

searchBar頁面

我們從圖片中可以看出,它是由一個搜索框TextInput和一個按鈕組成Button;我們佈局的話使用伸縮佈局,而按鈕的背景色需要在props給的屬性裏面設置也就是給color屬性一個值,搜索框flex:1使其佔滿自適應

import React from 'react'
import {View,Button,StyleSheet,TextInput,Alert} from 'react-native'
export default class Searchbar extends React.Component{
    // 文本框改變處理函數
    constructor(props){
        super(props)
        this.state = {
            searchVal:''
        }
    }
    // 文本框改變時,改變內容狀態
    changeTextHandle = (newSearchVal)=>{
        this.setState({
            searchVal:newSearchVal
        })
    }
    searchHandle = () =>{
        Alert.alert(this.state.searchVal)
    }
    render(){
        return <View style={styles.searchbar}>
            <TextInput
            style={styles.input}
            placeholder='你的白豬王子登門造訪'
            value={this.state.value}
            onChangeText={this.changeTextHandle}
            ></TextInput>
            <Button
            style={styles.button}
            onPress={this.searchHandle}
            title="搜索"
            ></Button>
        </View>
    }
}
const styles = StyleSheet.create({
    searchbar:{
        flexDirection: "row",
        justifyContent: "center",
        alignItems: 'center',
        paddingHorizontal: 10,
        height: 40,
    },
    input: {
        flex: 1,
        marginRight: 10,
        paddingLeft: 6,
        height: 30,
        borderWidth: 2,
        borderColor: "#ccc",
        borderRadius: 5,
        lineHeight: 12,
        fontSize: 12
    },
    button: {
    }
})

我們使用inputText需要綁定數據;然後通過操作狀態數據來改變其內容(可以理解爲模擬雙向數據綁定);我們點擊按鈕發送請求(因爲我們這裏不涉及真正的接口,所以給一個彈框);最後測試一下各個方法有沒有寫錯
結果如圖

Adverticement頁面

Adverticement部分是一個輪播圖
如果只是開發安卓的話可以使用react-native-swiper;我們這裏使用scrollView來兼容平臺

  1. 我們需要使用Dimensions來獲取屏幕的寬度
  2. 自己準備圖片;使用Image;引入圖片;給圖片設置寬度
  3. 使用scrollView實現輪播圖:隱藏滾動條、水平分佈排列、使滾動條分頁滑動
    明白以上三點;再使用魔法能量;一個輪播圖就產生了…
import React from 'react'
import {View,Dimensions,StyleSheet,ScrollView,Image} from 'react-native'
export default class Adverticement extends React.Component{
    // 這是模擬數據
    constructor(props) {
        super(props);
        this.state = {
            currentPage: 0,
            advertisements: [
                {
                    uri: require("../assets/double-11.png")
                },
                {
                    uri: require("../assets/eyes.png")
                },
                {
                    uri: require("../assets/five-year.png")
                }
            ]
        }
    }
    render(){
        return <View style={styles.adverticement}>
            <ScrollView
            horizontal={true}
            showsHorizontalScrollIndicator={false}
            pagingEnabled={true}
            ref="scrollView"
            >
            {this.state.advertisements.map((item)=>{
                return <View style={styles.itemCon} key={item.uri}>
                    <Image
                    source={item.uri}
                    style={styles.image}
                    resizeMode="cover"
                    ></Image>
                </View>
            })}
            </ScrollView>
        </View>
    }
}
const styles = StyleSheet.create({
    adverticement:{
        height:200,
    },
    itemCon:{
        width: Dimensions.get("window").width,
        height: 200,
        backgroundColor: 'green'
    },
    image:{
        width:'100%',
        height:'100%'
    }
})

當然,我們還需要設置自動輪播;使用其方法scrollTo();來實現自動輪播

    // 啓動定時器實現輪播
    componentDidMount(){
        this.startTimerHandler()
    }
    startTimerHandler = ()=>{
        this.timerId = setInterval(()=>{
            // 每次都切換一張圖片
            let nextPage = this.state.currentPage + 1
            // 當切換到最後一張,跳回到第一張
            nextPage = nextPage>=this.state.advertisements.length?0:nextPage
            this.setState({
                currentPage:nextPage
            })
            let offsetX = Dimensions.get("window").width * this.state.currentPage
            this.refs.scrollView.scrollTo({x: offsetX, y: 0, animated: true})
        },3000)
    }

當然輪播圖也可以深入,比如做一下分頁器或則做成緩動動畫

Products

我們已經將屏幕剩下的高度佔滿,但我們的內容需要滾動,此時則需要使用FlatList實現上拉加載;這是一個懶加載的.

  1. 我們首先定義一個模擬數據,綁定到該組件FlatList的data上
  2. 使用renderItem渲染我們的數據;此時記得渲染時的參數是解構賦值
  3. keyExtractor綁定唯一的key值
  4. 接着寫樣式觀看一下效果
  5. 這裏使用onRefresh、refreshing模擬一下下拉刷新的功能
import React from 'react'
import {View,Text,StyleSheet,FlatList,Image} from 'react-native'
export default class Products extends React.Component{
    constructor(props) {
        super(props);
        this.state = {
            products: [
                {
                    id: "1",
                    title: "小米MIX3",
                    subTitle: "滑蓋手機,咔咔咔",
                    image: "",
                    uri:{uri:'https://img1.360buyimg.com/pop/s590x470_jfs/t1/37180/6/6998/68136/5ccaaefeE884199bf/89f7a9047ab2fffa.jpg!q90!cc_590x470.webp'}
                },
                {
                    id: "2",
                    title: "華爲Mate20",
                    subTitle: "黑科技,牛逼牛逼",
                    image: "",
                    uri:{uri:'https://img11.360buyimg.com/n7/jfs/t1/38769/11/2224/318485/5cbfcfaaEfe8197e5/f67f128aef875b28.jpg'}
                },
                {
                    id: "3",
                    title: "魅族",
                    subTitle: "漂亮無需多言",
                    image: "",
                    uri:{uri:'https://img1.360buyimg.com/pop/s590x470_jfs/t1/37180/6/6998/68136/5ccaaefeE884199bf/89f7a9047ab2fffa.jpg!q90!cc_590x470.webp'}
                },
                {
                    id: "4",
                    title: "錘子",
                    subTitle: "漂亮的不像實力派",
                    image: "",
                    uri:{uri:'https://imgcps.jd.com/ling/100000770620/6JCl5YW75L-d5YGl/5q-P5ruhMzAw5YePNDA/t-5bd95d4f8e34e21f3ff67e71/65275632.jpg'}
                },
                {
                    id: "5",
                    title: "三星",
                    subTitle: "我的電池絕對靠譜",
                    image: "",
                    uri:{uri:'https://img1.360buyimg.com/pop/s590x470_jfs/t1/54899/5/1500/99514/5cf4dd53E4e65595d/ec452f29a3874f16.jpg!q90!cc_590x470.webp'}
                },
                {
                    id: "6",
                    title: "蘋果",
                    subTitle: "我的價格是真的不貴",
                    image: "",
                    uri:{uri:'https://img1.360buyimg.com/pop/s590x470_jfs/t1/40249/38/7444/64209/5ceb5c2bE89cadbd8/812bcae0b22e2a38.jpg!q90!cc_590x470.webp'}
                },
                {
                    id: "7",
                    title: "oppo",
                    subTitle: "照亮你的美",
                    image: "",
                    uri:{uri:'https://img10.360buyimg.com/n7/jfs/t29065/335/1630058179/447068/5770c26f/5ce66f29Nca358e47.png'}
                },
                {
                    id: "8",
                    title: "vivo",
                    subTitle: "柔光拍攝",
                    image: "",
                    uri:{uri:'https://img11.360buyimg.com/n7/jfs/t1/38769/11/2224/318485/5cbfcfaaEfe8197e5/f67f128aef875b28.jpg'}
                },      
            ],
            loading:true
        }
    }
    renderItemHandler = ({item,index})=>{//從data(products)中抽取數據進行渲染
        return <View style={styles.item}>
            <Image 
                source={item.uri}
                style={styles.image}>
            </Image>
            <View style={styles.content}>
                <Text style={styles.title}>{item.title}</Text>
                <Text style={styles.subTitle}>{item.subTitle}</Text>
            </View>
        </View>       
    }
    keyExtractorHandler = (item)=>{ //需要指定key;否則會警告
        return item.id 
    }
    // 模擬下拉刷新功能
    timerId = ()=>{
        setTimeout(()=>{
            this.setState({
                loading:false
            })
        },2000)
    }
    componentDidMount(){
        this.timerId()
    }
    loadingHandler = ()=>{
        this.setState({
            loading:true
        },()=>{
            this.timerId()
        })
    }
    render(){
        return (
            <FlatList
                onEndReachedThreshold={0.1}
                onRefresh={this.loadingHandler}
                refreshing={this.state.loading}
                data={this.state.products}
                renderItem={this.renderItemHandler}
                keyExtractor={this.keyExtractorHandler}
            >
            </FlatList>
        )
    }
}
const styles = StyleSheet.create({
    products: {
        flex: 1,
        backgroundColor: "blue"
    },
    item: {
        flexDirection: 'row',
        justifyContent: "center",
        alignContent: 'center',
        marginHorizontal: 10,
        marginTop: 30,
        height: 60,
    },
    image: {
        marginRight: 10,
        width: 50,
        height: 50,
        backgroundColor: "green"
    },
    content: {
        flex: 1
    },
    title: {
        lineHeight: 28,
        fontSize: 16,
        color: "#000"
    },
    subTitle: {
        lineHeight: 18,
        fontSize: 12,
        color: "#ccc"
    }
})

基本的頁面已經構成了…接下來豐富我們的頁面

Adverticement組件添加分頁器(完善輪播圖)

也就是添加指示器,就是輪播圖上面可以點擊的小圓圈;我們做web的時候一般都是ul和li,然後給其樣式,最後定位;我們同樣可以使用View然後通過定位和伸縮佈局來做其效果;當然爲了方便維護我們可以將初始化其大小等一些基本樣式…

  1. 我們現在狀態裏面初始化它的大小;
        this.state = {
            circleSize: 8,//指示器的大小
            circleMR: 5//指示器左右的距離
        }
  1. 寫組件,初始化樣式
  2. 我們通過自調用函數,將基本的樣式寫在circleStyle;動態寫數據方便我們後期的維護
    render(){
        return <View style={styles.adverticement}>
            <ScrollView
			......
            </ScrollView>
            <View style={styles.circle}>
                {      
                    // 一個自調用函數 
                    (()=>{
                        // 先把基本的樣式在狀態裏面定義好
                        const circleStyle = {
                            width: this.state.circleSize,
                            height: this.state.circleSize,
                            borderRadius: this.state.circleSize /2,
                            marginHorizontal: this.state.circleMR
                        }
                        return this.state.advertisements.map((item,index)=>{
                            return (
                                <View 
                                style={[circleStyle,this.state.currentPage===index?styles.circleActiveStyle:styles.indicator]}
                                key={index}>
                                </View>
                            )
                        })
                    })()
                }
            </View>
        </View>
    }
  1. 定位和高亮顯示
const styles = StyleSheet.create({
......
   // 給指示器進行定位
    circle:{
        position: 'absolute',
        left: '50%',
        bottom: 10,
        display: 'flex',
        flexDirection: 'row',
        marginLeft: -32
    },
    // 指示器背景顏色
    indicator:{
        backgroundColor: '#ccc',
    },
    // 指示器高亮樣式
    circleActiveStyle:{
        backgroundColor: 'red'
    },
})

基本的樣式結構
5. 當然可以根據自己的思維寫;以上的寫法只是爲了方便修改數據

完善Products加載功能

上面我們只是模擬上拉加載的效果;現在完善加載數據

  1. 我們使用RefreshControl組件(這一組件可以用在ScrollView或FlatList內部,爲其添加下拉刷新的功能);需要引入該組件RefreshControl
  2. 我們將方法還有一些樣式全部定義在該組件內
   render(){
        return (
            <FlatList
                onEndReachedThreshold={0.1}
                data={this.state.products}
                renderItem={this.renderItemHandler}
                keyExtractor={this.keyExtractorHandler}
                refreshControl={
                    <RefreshControl
                        refreshing={this.state.loading}
                        onRefresh={this.loadingHandler}
                        title="loading"
                        colors={['red','yellow']}
                        progressBackgroundColor={['transparent']}
                        progressViewOffset={50}
                    ></RefreshControl>
                }
            >
            </FlatList>
        )
    }
  1. 定義一個模擬數據,下拉更新數據
    loadingHandler = ()=>{
        const products = Array.from(Array(10)).map((v,i)=>{
            return {
                id: i.toString(),
                title: "vivo"+i,
                subTitle: "vivo-限時至高直降300+領券減300iQOO水滴全面屏超廣",
                image: "",
                uri:{uri:'https://img11.360buyimg.com/n7/jfs/t1/42675/4/1533/86437/5cc6a322E10252bba/64158e66e444403b.jpg'}
            }
        })
        this.setState({
            products,
            loading:true
        },()=>{
            this.timerId()
        })
    }
  1. 對於RefreshControl組件的屬性不懂也可以查閱文檔;看一下效果也能明白
    效果圖

走過路過不要錯過,以下是對新鮮便宜的路由導航的學習


點擊Products的每一項調整到詳情頁面

react-navigation

文檔

https://reactnavigation.org/docs/zh-Hans/getting-started.html

可能遇到的一些問題

  • 我們下載過程儘量使用npm來下載這套包,不要使用cnpm或則yarn
  • 如果還是報錯,刪除我們的包node_modules;然後重新安裝npm i
  • 在不行就下載穩定版本得npm install --save [email protected]
  • 使用的使用記得導入的配置
import {
    createStackNavigator,
    createAppContainer
} from 'react-navigation';

我們對整體的文件進行改造

列表

  1. 上面我們的搜索欄,輪播圖,產品列表都是我們首頁的內容;所以我們將其放到我們的Home.js
import React from 'react';
import { StyleSheet, Text, View,StatusBar } from 'react-native';
// 導入組件
import Adverticement from '../../components/Adverticement.js'
import Products from '../../components/Products.js'
import SearchBar from '../../components/SearchBar.js'
export default class Home extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <StatusBar
          hidden={true}
          animated={true}
          backgroundColor="#ccc"
          barStyle="light-content"
          translucent={false}
        ></StatusBar>
        <SearchBar></SearchBar>
        <Adverticement></Adverticement>
        <Products></Products>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff'
  },
});
  1. 在App.js使用我們的react-navigation
// import GlobalStack from './navigation/GlobalStack.js'
// export default GlobalStack
import {
  createStackNavigator,
  createAppContainer
} from 'react-navigation';
import Home from './pages/Home/Home.js';
import Detail from './pages/Products/Detail.js';

const RootStack = createStackNavigator(
{
  home: {
    screen: Home,
    navigationOptions: ({navigation, navigationOptions}) => ({
      header: null,
    })
  },
  detail: {
    screen: Detail,
    navigationOptions: ({navigationOptions}) => ({
      title: "商品詳情",
    })
  }
}, 
{
  initialRouteName: "detail"
}
)
const App = createAppContainer(RootStack)
export default App;
  1. 我們更改默認配置使用的導航測試一下是否引入成功
initialRouteName: "home"
  1. 我們點擊Products的列表需要跳轉到我們的詳情頁面’./pages/Products/Detail.js’;那麼我們的home頁面需要到導航傳給它
<Products {...this.props}></Products>
  1. 我們到Products頁面綁定一個點擊事件,導入TouchableNativeFeedback;將產品列表每一項item包裹在裏面
    renderItemHandler = ({item,index})=>{//從data(products)中抽取數據進行渲染
        return (
        <TouchableNativeFeedback
        onPress={this.toProductDetailHandler.bind(this,item)}
        >
        <View style={styles.item}>
            <Image 
                source={item.uri}
                style={styles.image}>
            </Image>
            <View style={styles.content}>
                <Text style={styles.title}>{item.title}</Text>
                <Text style={styles.subTitle}>{item.subTitle}</Text>
            </View>
        </View> 
        </TouchableNativeFeedback> 
        )    
    }
  1. 點擊跳轉
    toProductDetailHandler = (item)=>{
        const {navigation} = this.props
        // console.log(navigation)
        navigation.push("detail",item)
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章