商城頁面-MerchandiseGrid

  • 使用numColumns 為 2 欄

 <FlatList
   keyExtractor={this.keyExtractor}
   numColumns={2}
   data={this.state.data}
   renderItem={this.renderItem}
/>

再來用flex 0.5 的方式把list item 做平分

const styles = StyleSheet.create({
  listItem: {
    margin: 10,
    flex: 0.5
  },
})

..
renderItem = ()=> {
  return (
   <TouchableOpacity onPress={this.goMerchandiseDetail} style={styles.listItem}>
        {...}
    </TouchableOpacity>
  )
}

壓上折扣UI

  • Image 上 疊加Text

  • 對Text 設定背景色與字體顏色

自製TabBar

  • 利用ScrollView 做水平捲移動,並關掉ScrollIndictator...

  • 在裡面包裹 TouhableHighlight做選中處理 ,可以是更改背景色或是加上框線

  • 根據儲存的state 來得知有無被按下,並設定框線

  • 使用`contentContanierStyle 教item 排列設為flexDirection:'row'

const styles = StyleSheet.create({
  container: {
    flexDirection: 'row'
  },
})

export default class TabBar {

  static propTypes = {
    tabs: PropTypes.array.isRequired,
    onTabPress: PropTypes.func.isRequired
  }
  render(){
    return (
      <ScrollView
        contentContainerStyle={styles.container}
        horizontal
        showsHorizontalScrollIndicator={false}
      >
         <TouchableHighlight
            underlayColor="transparent"
            activeOpacity={1}
            onPress={() => {
              this.onTabPress(tab);
            }}
            style={ [ styles.tab, selected[tab.type] && { borderColor: 'white' } ] }
            key={tab.type}
          >

          </TouchableHighlight>
      </ScrollView>
    )
  }
}

Styles v.s ContentContainerStyle

  • ScrollView 與 FlatList 都利用 contentContainer. height > style.height 來判斷是否需要scrollBar

  • 所以不能把contentContainerStyle 設為{flex: 1},否則失去scroll 效果

  • e.g 畫面高長的width 為 640,但contentContainer 因item 超過範圍而變成 1024,這時把contentContainerStyle 設為 {flex: 1},則contentContainer 的width 變為 640 ,導致無法進行scroll

偵測程式是在背景(background)還是前景(foreground)

  • 在商品總覽中,利用此項機制,當使用者切回App時,重新刷新一次list,讓使用者看到最新的商品。

  • 在componentDidMount 進行註冊

  • 由於該listener 是從Native 曾回呼的,所以當該listener 發生錯誤後,在開發時會導致App Crash 而非 JS 層 的unhandled error

import {
  StyleSheet,
  View,
  Text,
  FlatList,
  Image,
  TouchableOpacity,
  TextInput,
  AppState
} from 'react-native';

export default class Example extends Component  {
   state = {
    appState: AppState.currentState
  };

  refresh = ()=>{
    //刷新list
  }
  handleAppStateChange = (nextAppState) => {
    if (nextAppState === 'active') {
      this.refresh();
    }
  };
  componentDidMount() {
    AppState.addEventListener('change', this.handleAppStateChange);
  }

  componentWillUnmount() {
    AppState.removeEventListener('change', this.handleAppStateChange);
  }

}

加入PullToRefresh 的機制

  • 新增refreshingonRefresh屬性

  • 在即將開始刷新之前,變更this.state.refreshing

  • 再刷新之後,再變更this.state.refreshing為false

  • 因為是測試資料,利用shuffle的方式改變測試資料的順序,模擬資料不同的情況

  export default class Example {

    shuffle = arr => arr.sort(() => Math.random() - 0.5);
    onRefresh = () => {
      this.setState({
        refreshing: true
      });
      setTimeout(() => {
        this.setState({
          refreshing: false,
          data: this.shuffle(this.state.data)
        });
      }, 1000);
    };

   render(){
     return (
       <FlatList
         refreshing={this.state.refreshing}
         onRefresh={this.onRefresh}
         {...}
       />
     )
   }

}

使用onEndReached 實現Pagination

NetInfo 偵測網路狀態 與 ListEmptyView

  • ListEmptyView的三種狀態

    • Wifi/3G 未連上

    • 連上了,但獲取不到資料

    • 本來就是空的

  • 設置NetInfo 偵測網路狀態並在ListEmptyComponent 加入自製EmptyView

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

const CONNECTION_ERROR = {
  offline: '網路斷線了',
  unknown: '未知的錯誤'
};
export default class Example {

  componentDidMount() {
    NetInfo.isConnected.addEventListener('connectionChange', this.handleConnectivity);
  }

  componentWillUnmount() {
    NetInfo.isConnected.addEventListener('connectionChange', this.handleConnectivity);
  }

  emptyView = () => {
      const { error } = this.state;
      return (
        <View style={[styles.container, styles.center]}>
          <Text>{CONNECTION_ERROR[error]}</Text>
        </View>
      );
  };
  handleConnectivity = (isConnected) => {
      if (!isConnected) {
        this.setState({
          data: [],
          error: 'offline'
        });
      } else {
        // refetch your data
        this.refresh();
      }
      this.setState({
        isConnected
      });
  };

  render(){
    return (
      <FlatList
        ListEmptyComponent={this.emptyView}
        refreshing={this.state.refreshing}
        onRefresh={this.refresh}
        contentContainerStyle={styles.merchandiseGrid}
        keyExtractor={this.keyExtractor}
        numColumns={2}
        data={this.state.data}
        renderItem={this.renderItem}
      />
    )
  }
}

Last updated