react native實現(xiàn)地圖展示和周邊POI數(shù)據(jù)以及關(guān)鍵字搜索

最近做了一個基于高德地圖demo,主要功能是定位和地圖的展示,周邊POI的數(shù)據(jù)獲取以及搜索關(guān)鍵字給出相應(yīng)地址提示的功能。下面是效果圖:

安卓

DDDE29DA2E2D28E900285E3077DEC2EB.jpg

IOS

WechatIMG134.jpeg

用的是基于三方庫react-native-smart-amap,首先按照文檔,進(jìn)行一些配置。
執(zhí)行安裝命令

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

iOS配置

  • 將RCTAMap.xcodeproj作為Library拖進(jìn)你的Xcode里的project中.

  • 將RCTAMap目錄里Frameworks目錄拖進(jìn)主project目錄下, 選擇copy items if needed, create groups, 另外add to target不要忘記選擇主project.

  • 將RCTAMap目錄里Frameworks目錄里的AMap.bundle拖進(jìn)主project目錄下, 選擇copy items if needed, create groups, 另外add to target不要忘記選擇主project.

  • 點擊你的主project, 選擇Build Phases -> Link Binary With Libraries, 將RCTAMap.xcodeproj里Product目錄下的libRCTAMap.a拖進(jìn)去.

  • 同上位置, 選擇Add items, 添加一下系統(tǒng)庫
    libstdc++.6.0.9.tbd
    libc++.tb
    libz.tbd
    Security.framework
    CoreTelephony.framework
    SystemConfiguration.framework
    JavaScriptCore.framework
    CoreLocation.framework

  • 選擇Build Settings, 找到Header Search Paths, 確認(rèn)其中包含$(SRCROOT)/../../../react-native/React, 模式為recursive.

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

  • 點擊在Libraries下已拖進(jìn)來的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屬性, 并設(shè)置值為YES(ios 10)

  • 在AppDelegate.m中

#import <AMapFoundationKit/AMapFoundationKit.h> //引入高德地圖核心包
...
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{

  [AMapServices sharedServices].apiKey = @"請?zhí)顚懩膋ey"; //設(shè)置高德地圖SDK服務(wù)key
  ...
}

安卓

使用高德地圖SDK, 申請應(yīng)用key等詳細(xì)信息請點擊這里
需要注意的是,需要獲取安全碼SHA1,分別發(fā)布和調(diào)試版

獲取發(fā)布版SHA1 :cd到你的keystore 所在的文件夾目錄

然后執(zhí)行cmd  keytool -v -list -keystore (keystore) 回車 獲取發(fā)布版的SHA1值

獲取開發(fā)版的SHA1值:

終端輸入:cd .android 進(jìn)入到 .android下,輸入keytool -v -list -keystore debug.keystore命令,注意的是調(diào)試下的密碼默認(rèn)是: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中, 加入所需權(quán)限
<!--*************************高德地圖-定位所需要權(quán)限*************************-->
    <!-- Normal Permissions 不需要運行時注冊 -->
    <!--獲取運營商信息,用于支持提供運營商信息相關(guān)的接口-->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <!--用于訪問wifi網(wǎng)絡(luò)信息,wifi信息會用于進(jìn)行網(wǎng)絡(luò)定位-->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <!--這個權(quán)限用于獲取wifi的獲取權(quán)限,wifi信息會用來進(jìn)行網(wǎng)絡(luò)定位-->
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />

    <!-- 請求網(wǎng)絡(luò) -->
    <uses-permission android:name="android.permission.INTERNET" />

    <!-- 不是SDK需要的權(quán)限,是示例中的后臺喚醒定位需要的權(quán)限 -->
    <!--<uses-permission android:name="android.permission.WAKE_LOCK" />-->

    <!-- 需要運行時注冊的權(quán)限 -->
    <!--用于進(jìn)行網(wǎng)絡(luò)定位-->
    <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" />
    <!--寫入擴展存儲,向擴展卡寫入數(shù)據(jù),用于寫入緩存定位數(shù)據(jù)-->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <!--讀取緩存數(shù)據(jù)-->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

    <!--用于讀取手機當(dāng)前的狀態(tài)-->
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />

    <!-- 更改設(shè)置 -->
    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
<!--*************************高德地圖-定位所需要權(quán)限*************************-->
  • 在AndroidManifest.xml中, application標(biāo)簽內(nèi)加入
 <!--高德地圖SDK key設(shè)置-->
    <meta-data
        android:name="com.amap.api.v2.apikey"
        android:value="請?zhí)顚懩膋ey"/>
    <!--高德地圖APS服務(wù)設(shè)置-->
    <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: '商務(wù)住宅|學(xué)校',
           dataArray: [],
           searchArray: [],
           isHidden: true
       }
   }
componentDidMount() {
//監(jiān)聽原生周邊POI數(shù)據(jù)回調(diào)
       NativeAppEventEmitter.addListener('amap.onPOISearchDone', this._onPOISearchDone)
//監(jiān)聽輸入關(guān)鍵字回調(diào)周邊POI數(shù)據(jù)
       NativeAppEventEmitter.addListener('amap.location.onLocationResult', this._onLocationResult),
       //獲取當(dāng)前位置
       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數(shù)據(jù)
_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='請輸入搜索內(nèi)容'
                              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(){
//這里可進(jìn)行一些數(shù)據(jù)更多加載的操作, 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
   }
});

還有個需要另外做的是這個庫是沒有封裝原生的搜索功能,就是根據(jù)輸入來獲取周邊的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];
    
}

/* 提示搜索回調(diào) */
- (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);//限制在當(dāng)前城市

        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數(shù)據(jù)以及根據(jù)輸入提示獲取周邊的相關(guān)功能就完成了。當(dāng)然這個只是一些基礎(chǔ)的簡單功能,如果需要更多更復(fù)雜的功能,就需要自己去集成iOS和安卓的原生API然后接入到自己的應(yīng)用里,需要親們自個兒去封裝啦。當(dāng)然如果有碰到的任何問題,歡迎各位咨詢我或者評論留言或者加入RN技術(shù)交流群:397885169。

如果遇到錯誤:
錯誤: 不兼容的類型: RCTAMapModule無法轉(zhuǎn)換為InputtipsListener
inputTips.setInputtipsListener(this);
改成:inputTips.setInputtipsListener((Inputtips.InputtipsListener) this);

項目地址,如果需要關(guān)鍵字搜索功能,記得需要手動修改node modules里的代碼,具體修改看文章或者demo。喜歡的同學(xué)可以給個star啊

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 文|枺一 枺一文字 雖然我抓不住夕陽.點擊收聽DJ枺一 前腳剛邁出大學(xué)的校門,后腳也跟著出來了,沒有畢業(yè),但也離畢...
    枺一文字閱讀 385評論 2 4
  • 靜謐的月光下,石子路旁的鳶尾花在沉默的綻放;淅瀝的梅雨中,石頭上的青苔在純凈的生長。房門發(fā)出歲月的聲響,木桌上的年...
    青文魚閱讀 408評論 2 2
  • 第二天早晨,雨還是不停地下,今天的雨似乎比昨天的更大,與地面碰撞的聲音也明顯。 何晟被連續(xù)而來的電話聲吵醒,他迷迷...
    漫兮羅蘭閱讀 761評論 0 1
  • 曾幾何時,看到別人辭職去洱海邊蓋房子,開民宿 看到為了夢想離開北京回老家開咖啡店的文章推送 看到朋友圈又刷屏的說走...
    小魅李閱讀 244評論 0 0

友情鏈接更多精彩內(nèi)容