美文网首页
react native实现地图展示和周边POI数据以及关键字搜

react native实现地图展示和周边POI数据以及关键字搜

作者: 凹凸怪cq | 来源:发表于2018-04-23 22:38 被阅读1350次

    最近做了一个基于高德地图demo,主要功能是定位和地图的展示,周边POI的数据获取以及搜索关键字给出相应地址提示的功能。下面是效果图:

    安卓

    DDDE29DA2E2D28E900285E3077DEC2EB.jpg

    IOS

    WechatIMG134.jpeg

    用的是基于三方库react-native-smart-amap,首先按照文档,进行一些配置。
    执行安装命令

    npm install react-native-smart-amap --save

    iOS配置

    • 将RCTAMap.xcodeproj作为Library拖进你的Xcode里的project中.

    • 将RCTAMap目录里Frameworks目录拖进主project目录下, 选择copy items if needed, create groups, 另外add to target不要忘记选择主project.

    • 将RCTAMap目录里Frameworks目录里的AMap.bundle拖进主project目录下, 选择copy items if needed, create groups, 另外add to target不要忘记选择主project.

    • 点击你的主project, 选择Build Phases -> Link Binary With Libraries, 将RCTAMap.xcodeproj里Product目录下的libRCTAMap.a拖进去.

    • 同上位置, 选择Add items, 添加一下系统库
      libstdc++.6.0.9.tbd
      libc++.tb
      libz.tbd
      Security.framework
      CoreTelephony.framework
      SystemConfiguration.framework
      JavaScriptCore.framework
      CoreLocation.framework

    • 选择Build Settings, 找到Header Search Paths, 确认其中包含$(SRCROOT)/../../../react-native/React, 模式为recursive.

    • 同上位置, 找到Framework Search Paths, 加入$(PROJECT_DIR)/Frameworks.

    • 点击在Libraries下已拖进来的RCTAMap.xcodeproj, 选择Build Settings, 找到Framework Search Paths, 将(SRCROOT)/../../../ios/Frameworks替换成(SRCROOT)/../../../../ios/Frameworks.

    • 在info.plist中加入Privacy - Location When In Use Usage Description属性(ios 10)

    • 在info.plist中加入Allow Arbitrary Loads属性, 并设置值为YES(ios 10)

    • 在AppDelegate.m中

    #import <AMapFoundationKit/AMapFoundationKit.h> //引入高德地图核心包
    ...
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
    
      [AMapServices sharedServices].apiKey = @"请填写您的key"; //设置高德地图SDK服务key
      ...
    }
    

    安卓

    使用高德地图SDK, 申请应用key等详细信息请点击这里
    需要注意的是,需要获取安全码SHA1,分别发布和调试版

    获取发布版SHA1 :cd到你的keystore 所在的文件夹目录
    
    然后执行cmd  keytool -v -list -keystore (keystore) 回车 获取发布版的SHA1值
    
    获取开发版的SHA1值:
    
    终端输入:cd .android 进入到 .android下,输入keytool -v -list -keystore debug.keystore命令,注意的是调试下的密码默认是:android
    

    PackageName就是你的app的包名。

    • 在android/settings.gradle中添加
    include ':react-native-smart-amap'
    project(':react-native-smart-amap').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-smart-amap/android')
    
    • 在android/app/build.gradle中添加
    dependencies {
        ...
        // From node_modules
        compile project(':react-native-smart-amap')
    }
    
    • 在MainApplication.java中添加
    import com.reactnativecomponent.amaplocation.RCTAMapPackage;    //import package
    ...
    /**
     * A list of packages used by the app. If the app uses additional views
     * or modules besides the default ones, add more packages here.
     */
    @Override
    protected List<ReactPackage> getPackages() {
        return Arrays.<ReactPackage>asList(
            new MainReactPackage(),
            new RCTAMapPackage()  //register Module
        );
    }
    
    • 在AndroidManifest.xml中, 加入所需权限
    <!--*************************高德地图-定位所需要权限*************************-->
        <!-- Normal Permissions 不需要运行时注册 -->
        <!--获取运营商信息,用于支持提供运营商信息相关的接口-->
        <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
        <!--用于访问wifi网络信息,wifi信息会用于进行网络定位-->
        <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
        <!--这个权限用于获取wifi的获取权限,wifi信息会用来进行网络定位-->
        <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
        <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
    
        <!-- 请求网络 -->
        <uses-permission android:name="android.permission.INTERNET" />
    
        <!-- 不是SDK需要的权限,是示例中的后台唤醒定位需要的权限 -->
        <!--<uses-permission android:name="android.permission.WAKE_LOCK" />-->
    
        <!-- 需要运行时注册的权限 -->
        <!--用于进行网络定位-->
        <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
        <!--用于访问GPS定位-->
        <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
        <!--用于提高GPS定位速度-->
        <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
        <!--写入扩展存储,向扩展卡写入数据,用于写入缓存定位数据-->
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
        <!--读取缓存数据-->
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    
        <!--用于读取手机当前的状态-->
        <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    
        <!-- 更改设置 -->
        <uses-permission android:name="android.permission.WRITE_SETTINGS" />
    <!--*************************高德地图-定位所需要权限*************************-->
    
    • 在AndroidManifest.xml中, application标签内加入
     <!--高德地图SDK key设置-->
        <meta-data
            android:name="com.amap.api.v2.apikey"
            android:value="请填写您的key"/>
        <!--高德地图APS服务设置-->
        <service android:name="com.amap.api.location.APSService" >
        </service>
    

    然后在js文件里 app.js

    const AnimatedFlatList = Animated.createAnimatedComponent(FlatList);
    var Geolocation = require('Geolocation');
    const {width: deviceWidth, height: deviceHeight} = Dimensions.get('window')
    import AMap from 'react-native-smart-amap'
    
    type Props = {};
    export default class App extends Component<Props> {
    constructor(props) {
           super(props)
           this.state = {
               data: [],
               longitude: '',
               latitude: '',
               loaded: false,
               keywords: '商务住宅|学校',
               dataArray: [],
               searchArray: [],
               isHidden: true
           }
       }
    componentDidMount() {
    //监听原生周边POI数据回调
           NativeAppEventEmitter.addListener('amap.onPOISearchDone', this._onPOISearchDone)
    //监听输入关键字回调周边POI数据
           NativeAppEventEmitter.addListener('amap.location.onLocationResult', this._onLocationResult),
           //获取当前位置
           this.getCurrentPosition()
       }
    
    getCurrentPosition(){
           Geolocation.getCurrentPosition(
               (location) => {
                   console.log('---location----:',location)
                   this.setState({
                       longitude: location.coords.longitude,
                       latitude: location.coords.latitude,
                       loaded: true
                   },()=>{
                       setTimeout(()=>{
                           this.setState({
                               isHidden: false
                           })
                       },200)
                   })
               },
               (error) => {
                   alert("获取位置失败")
               },
           );
       }
    
    //获取周边POI数据
    _onPOISearchDone=(data)=>{
           console.log('----_onPOISearchDone----:',data)
           this.setState({
               dataArray: data.searchResultList,
           })
       }
    
    _onLocationResult = (result) => {
    
           console.log(`_onLocationResult...result`,result)
           this.setState({
               searchArray: result.searchResultList,
           })
       }
    
       _onDidMoveByUser = (e) => {
           // console.log('----_onDidMoveByUser--:',e.nativeEvent)
           this.setState({
               longitude: e.nativeEvent.data.centerCoordinate.longitude,
               latitude: e.nativeEvent.data.centerCoordinate.latitude,
           })
           this._searchNearBy(e.nativeEvent.data.centerCoordinate.latitude,e.nativeEvent.data.centerCoordinate.longitude)
       }
    
       _searchNearBy(latitude,longitude) {
           console.log('----_searchNearBy--:',latitude,longitude)
           let obj={
               page: 1,
               coordinate: {
                   latitude: latitude,
                   longitude: longitude,
               },
               keywords: this.state.keywords,
           }
    
           this._amap.searchPoiByCenterCoordinate(obj)
       }
    render() {
    
       if (!this.state.loaded){
           return(
               <View></View>
           )
       }
    
       return (
         <View style={styles.container}>
             <View style={{width:deviceWidth,height: deviceHeight}}>
                 {
                     this.state.isHidden?(
                         null
                     ):(
                 <AMap
                     ref={ component => this._amap = component }
                     style={{width:deviceWidth,height: deviceHeight/2,marginTop: 40}}
                     options={{
                               frame: {
                                   width: deviceWidth,
                                   height: deviceHeight/2
                               },
                               showsUserLocation: true,
                               userTrackingMode: Platform.OS == 'ios' ? AMap.constants.userTrackingMode.none : null,
                               centerCoordinate: {
                                   latitude: this.state.latitude,
                                   longitude: this.state.longitude,
                               },
                               zoomLevel: 18.1,
                               centerMarker: Platform.OS == 'ios' ? 'icon_location' : 'poi_marker',
                           }}
                     onLayout={this._onLayout}
                     onDidMoveByUser={this._onDidMoveByUser}
                 />
                         )
                 }
    
                   <Image source={require('./redPin_lift.png')} style={styles.imageStyle}/>
                   <View style={styles.topView}>
                       <TextInput style={styles.searchTextInput}
                                  value={this.state.searchValue}
                                  onChangeText={(value) =>{
                                this._amap.searchLocation(value)
                                                      this.setState({searchValue: value});
                                                  }}
                                  placeholder='请输入搜索内容'
                                  placeholderTextColor="rgb(155,155,155)"
                                  underlineColorAndroid="transparent"
                                  autoCorrect={false}
                                  autoCapitalize='none'
                       />
                       <AnimatedFlatList
                           data={this.state.searchArray}
                           legacyImplementation={false}
                           ref={(flatList)=>this._flatList = flatList}
                           renderItem={this._renderSearchComponent}
                           keyExtractor={(item, index) => 'search_list_'+index}
                       />
                   </View>
                   <AnimatedFlatList
                       data={this.state.dataArray}
                       legacyImplementation={false}
                       ref={(flatList)=>this._flatList = flatList}
                       renderItem={this._renderItemComponent}
                       onEndReached={()=>this._onEndReached()}
                       keyExtractor={(item, index) => 'dialog_list_'+index}
                   />
    
             </View>
         </View>
       );
     }
    
       _onEndReached(){
    //这里可进行一些数据更多加载的操作, page+1即可
       }
    
       _renderItemComponent=({item,index})=>{
           return(
               <TouchableOpacity style={styles.itemStyle}>
                   <Text>{item.name}</Text>
                   <Text style={{color: '#676767'}}>{item.address}</Text>
               </TouchableOpacity>
           )
       }
    
       _renderSearchComponent=({item,index})=>{
           let address=item.district+item.address+item.name
           return(
               <TouchableOpacity style={styles.itemSearchStyle} onPress={()=>{
                   alert(address)
               }}>
                   <Text>{address}</Text>
               </TouchableOpacity>
           )
       }
    
    
    }
    
    const styles = StyleSheet.create({
       container: {
           flex: 1,
           backgroundColor: '#efefef',
       },
       itemStyle: {
           width: '100%',
           height: 50,
           alignItems: 'center',
           backgroundColor:'#ffffff',
           justifyContent: 'center',
           marginTop: 1
       },
       itemSearchStyle: {
           width: '100%',
           height: 50,
           alignItems: 'center',
           backgroundColor:'#ffffff',
           justifyContent: 'center',
           borderBottomColor: '#efefef',
           borderBottomWidth: 1
       },
       imageStyle: {
           position: 'absolute',
           top: deviceHeight/2/2-36+50,
           left: deviceWidth/2-22
       },
       searchTextInput: {
           width: '100%',
           marginTop: 20,
           height: 40,
           paddingLeft: 10,
           backgroundColor: '#ffffff'
       },
       topView: {
           position: 'absolute',
           top: 0,
           left: 0,
           width: deviceWidth
       }
    });
    

    还有个需要另外做的是这个库是没有封装原生的搜索功能,就是根据输入来获取周边的POI提示,这个需要分别在iOS和安卓库里封装原生

    iOS

    在react-native-smart-amap/AMap-ios.js里添加方法

        searchLocation(value){
            AMapManager.searchLocation(value)
        }
    

    接着在react-native-smart-amap/ios/RCTAMap/RCTAMap/的RCTAMapManager.m里添加方法:

    RCT_EXPORT_METHOD(searchLocation:(NSString *)keywords)
    {
        AMapInputTipsSearchRequest *request=[[AMapInputTipsSearchRequest alloc] init];
        request.keywords=keywords;
        [self.search AMapInputTipsSearch:request];
        
    }
    
    /* 提示搜索回调 */
    - (void)onInputTipsSearchDone:(AMapInputTipsSearchRequest *)request response:(AMapInputTipsSearchResponse *)response;
    {
        
        NSDictionary *result;
        NSMutableArray *resultList;
        resultList = [NSMutableArray arrayWithCapacity:response.tips.count];
        if (response.tips.count > 0)
        {
            [response.tips enumerateObjectsUsingBlock:^(AMapTip *obj, NSUInteger idx, BOOL *stop) {
                
                [resultList addObject:@{
                                        @"uid": obj.uid,
                                        @"name": obj.name,
                                        @"adcode": obj.adcode,
                                        @"district": obj.district,
                                        @"latitude": @(obj.location.latitude),
                                        @"longitude": @(obj.location.longitude),
                                        @"address": obj.address,
                                        }];
            }];
        }
        result = @{
                   @"searchResultList": resultList
                   };
        
        [self.bridge.eventDispatcher sendAppEventWithName:@"amap.location.onLocationResult"
                                                     body:result
         ];
    
    }
    
    

    Android

    在react-native-smart-amap/AMap-android.js里添加方法

        searchLocation(value){
            AMapManager.searchLocation(value)
        }
    

    在react-native-smart-amap/android/src/main/java/com/reactnativecomponent/amap/RCTAMapModule.java里添加方法:

        @ReactMethod
        public void searchLocation(String value){
            InputtipsQuery inputquery = new InputtipsQuery(value, "");
            inputquery.setCityLimit(true);//限制在当前城市
    
            Inputtips inputTips = new Inputtips(mContext, inputquery);
            inputTips.setInputtipsListener(this);
            inputTips.requestInputtipsAsyn();
        }
    @Override
        public void onGetInputtips(final List<Tip> tipList, int rCode) {
            WritableMap dataMap = Arguments.createMap();
            WritableArray array = Arguments.createArray();
    
            if (rCode == 1000) {
                for (Tip tip : tipList) {
                    WritableMap data = Arguments.createMap();
                    data.putString("name", tip.getName());
                    data.putString("address", tip.getAddress());
                    data.putString("uid", tip.getPoiID());
                    data.putString("adCode", tip.getAdcode());
                    data.putString("district", tip.getDistrict());
                    data.putDouble("longitude", tip.getPoint().getLongitude());
                    data.putDouble("latitude", tip.getPoint().getLatitude());
                    array.pushMap(data);
                }
                dataMap.putArray("searchResultList", array);
            }
            else {
                WritableMap error = Arguments.createMap();
                error.putString("code", String.valueOf(rCode));
                dataMap.putMap("error", error);
            }
    
     mContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
                    .emit("amap.location.onLocationResult", dataMap);
        }
    

    至此iOS和安卓的地图展示和周边POI数据以及根据输入提示获取周边的相关功能就完成了。当然这个只是一些基础的简单功能,如果需要更多更复杂的功能,就需要自己去集成iOS和安卓的原生API然后接入到自己的应用里,需要亲们自个儿去封装啦。当然如果有碰到的任何问题,欢迎各位咨询我或者评论留言或者加入RN技术交流群:397885169。

    如果遇到错误:
    错误: 不兼容的类型: RCTAMapModule无法转换为InputtipsListener
    inputTips.setInputtipsListener(this);
    改成:inputTips.setInputtipsListener((Inputtips.InputtipsListener) this);

    项目地址,如果需要关键字搜索功能,记得需要手动修改node modules里的代码,具体修改看文章或者demo。喜欢的同学可以给个star啊

    相关文章

      网友评论

          本文标题:react native实现地图展示和周边POI数据以及关键字搜

          本文链接:https://www.haomeiwen.com/subject/ukjrlftx.html