React Native城市选择与切换

虽然React Native已经不在是当前热门的跨平台技术了,但是还是可以看到很多的公司和个人在用,Flutter有Flutter好,RN呢也有适合自己的地方,至于究竟哪个更好,我们不做过多的比较。
最近,我在升级之前的《React Native移动开发实战》一书,书中的项目有城市切换的功能,效果如下图所示。
React Native城市选择与切换_第1张图片
可以看到,这个城市选择页面是很常规的,包含了当前定位城市和城市列表,右侧可以通过SlideBar进行快捷定位,除此之外,此组件还支持搜索功能。

首先,我们看一下城市列表,对于这一功能,我们可以使用SectionList组件,因为我们可以使用SectionList的ListHeaderComponent属性来实现当前定位布局,而右边的字母索引效果需要借助SectionList的scrollToLocation()函数,如下所示。

const _scrollTo = (index, letter) => {
    listViewRef?.current?.scrollToLocation({itemIndex: 0, sectionIndex: index});
  };

而搜索功能就更加简单了,使用FlatList组件展示即可,此处也可以使用list.map循环来开发列表功能。
CitySelectScreen.js

import React, {useState, useEffect} from 'react';
import {
  View,
  Text,
  TextInput,
  StyleSheet,
  TouchableOpacity,
  Keyboard,
} from 'react-native';
import PropTypes from 'prop-types';

import {CityList} from './components';
import apiRequest from '../../api';
import Header from '../../common/Header/Header';

const CitySelectScreen = ({location = '上海市', navigation}) => {
  let inputRef = null;
  const [cities, setCities] = useState([]);
  const [currentCityList, setCurrentCityList] = useState({});
  const [isFocused, setIsFocused] = useState(false);
  const [result, setResult] = useState([]);
  const [keyword, setKeyword] = useState('');

  useEffect(() => {
    getCities();
  }, []);

  const onChangeText = e => {
    setKeyword(e);
  };

  const onSelectCity = city => {
    setTimeout(() => {
      navigation.navigate('SelectCinemaScreen', {
        title: city.CITY_NAME,
        CITY_CD: city.CITY_CD,
      });
    }, 200);
    setResult([]);
  };

  const searchSubmit = () => {
    if (isFocused) {
      inputRef.blur();
      setIsFocused(false);
      setResult([]);
      Keyboard.dismiss();
    } else {
      setIsFocused(true);
      inputRef.focus();
    }
  };

  const getCities = async () => {
    let url = 'https://prd-api.cgv.com.cn/product/areas/that/group';
    const res = await apiRequest.get(url);
    setCities(res);
  };

  const searchCities = async () => {
    let url = 'https://prd-api.cgv.com.cn/product/areas/that/group';
    const params = {condition: keyword};
    const res = await apiRequest.get(url, params);
    console.log(res[0].data);
    setResult(res[0].data);
  };

  const onCurrentPress = (name = '上海市') => {
    cities.map(item =>
      item.data.map(val => {
        if (val.CITY_NAME === name) {
          onSelectCity(val);
          return null;
        }
      }),
    );
  };

  const renderSearchView = () => {
    return (
      
         {
            inputRef = c;
          }}
          onChangeText={onChangeText}
          returnKeyType="search"
          onSubmitEditing={() => {
            if (keyword) {
              searchCities();
            }
          }}
          onFocus={() => setIsFocused(true)}
          placeholder="输入城市名或拼音"
        />
         searchSubmit(isFocused)}>
          {!isFocused ? '搜索' : '取消'}
        
      
    );
  };

  return (
    
      
{renderSearchView()} {(!isFocused && !keyword && keyword.length < 1) || !isFocused ? ( ) : ( )} ); }; const SearchResult = ({list, onSelectCity}) => { return ( {list.map((item, index) => ( { onSelectCity(item); }}> {item.CITY_NAME} ))} ); }; const styles = StyleSheet.create({ container: { flex: 1, flexDirection: 'column', }, searchView: { height: 48, flexDirection: 'row', backgroundColor: '#fff', alignItems: 'center', paddingLeft: 10, paddingRight: 10, }, searchTxt: { color: '#FC5869', marginRight: 5, fontSize: 16, }, rowView: { backgroundColor: '#fff', height: 44, paddingLeft: 13, justifyContent: 'center', }, leftIcon: { width: 28, height: 28, paddingLeft: 13, }, }); CitySelectScreen.propTypes = { cities: PropTypes.array, getCities: PropTypes.func, }; export default CitySelectScreen;

CityList.js代码如下:

import React, {useEffect, useRef} from 'react';
import {
  View,
  SectionList,
  TouchableOpacity,
  StyleSheet,
  Text,
  Image,
  Dimensions,
} from 'react-native';
import PropTypes from 'prop-types';
import ItemSeparatorComponent from '../../../../common/ItemSeparator';
import location from '../../../../assets/images/home/location.png';
import refresh from '../../../../assets/images/home/refresh.png';

const {width} = Dimensions.get('window');

const propTypes = {
  keyword: PropTypes.string,
  onChangeTextKeyword: PropTypes.func,
};

const defaultProps = {};

const CityList = ({
  onSelectCity,
  allCityList = [],
  currentCity,
  onCurrentCityPress,
  position: _position,
}) => {
  const listViewRef = useRef(null);

  useEffect(() => {
    console.log(allCityList);
  }, []);

  const city =
    currentCity && currentCity.city
      ? currentCity.city
      : '定位失败,请手动选择城市';

  const _cityNameClick = cityJson => {
    onSelectCity(cityJson);
  };

  const getLocation = async () => {
    // await PermissionsAndroid.request(
    //   PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
    // );
    // const res = await Geocode.reverse({
    //   latitude: '29.604451007313266',
    //   longitude: '106.52727499999997',
    // });
    // _position(res);
  };

  const CityHeader = props => {
    const {currentCity = '上海', onCurrentCityPress} = props;
    return (
      
        
          当前城市
        
         onCurrentCityPress(currentCity)}
          style={styles.headerContainer}>
          
            
            
              {currentCity}
            
          
           getLocation()}>
            
          
        
      
    );
  };

  const _renderListRow = (cityJson, rowId) => {
    return (
       _cityNameClick(cityJson.item)}>
        
          {cityJson.item.CITY_NAME}
        
      
    );
  };

  const _scrollTo = (index, letter) => {
    listViewRef?.current?.scrollToLocation({itemIndex: 0, sectionIndex: index});
  };

  const _renderRightLetters = (letter, index) => {
    return (
       {
          _scrollTo(index, letter);
        }}>
        
          {letter}
        
      
    );
  };

  return (
    
       ({
          length: 44,
          offset: 44 * index,
          index,
        })}
        ListHeaderComponent={
          
        }
        ref={listViewRef}
        sections={allCityList}
        keyExtractor={(item, index) => index.toString()}
        renderItem={_renderListRow}
        ItemSeparatorComponent={() => }
        renderSectionHeader={({section: {name}}) => (
          
            {name}
          
        )}
        stickySectionHeadersEnabled={true}
      />
      
        {allCityList.map((item, index) =>
          _renderRightLetters(item.name, index),
        )}
      
    
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#F4F4F4',
  },
  sectionTitle: {
    paddingVertical: 5,
    paddingLeft: 12,
    backgroundColor: '#F3F4F5',
  },
  iconContainer: {
    height: 40,
    justifyContent: 'center',
    alignItems: 'center',
  },
  leftIconContainer: {
    marginEnd: 12,
  },
  rightIconContainer: {
    marginStart: 8,
  },
  headerView: {
    width: width,
    display: 'flex',
    flexDirection: 'row',
    position: 'relative',
    alignItems: 'center',
    backgroundColor: '#fff',
  },
  leftIcon: {
    width: 15,
    height: 13,
    marginLeft: 15,
    marginRight: 5,
  },
  rowView: {
    paddingLeft: 12,
    backgroundColor: '#fff',
  },
  rowData: {
    width: width,
    height: 44,
    justifyContent: 'center',
  },
  headerContainer: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    backgroundColor: '#fff',
    paddingHorizontal: 12,
    height: 44,
  },
  headerLeft: {
    flexDirection: 'row',
    alignItems: 'center',
  },
  letter: {
    marginBottom: 3,
  },
  letterSpace: {
    position: 'absolute',
    right: 4,
    bottom: 0,
    top: 0,
    justifyContent: 'center',
  },
});

CityList.propTypes = propTypes;
CityList.defaultProps = defaultProps;

export default CityList;

另外,我们的网络请求使用的是Axios,相关内容可以查看我之前文章的介绍:React Native使用axios进行网络请求

源码:https://github.com/xiangzhihong/rn_city

你可能感兴趣的:(React Native城市选择与切换)