高德地圖-地理圍欄功能實現

Rx_Re發表於2019-01-12

最近需要實現一個地理圍欄相關的功能。

專案是和騎行相關的,主要是當遊客或者騎友定位地址進入到對應的景點的地理圍欄裡面,則播報景點相關的報導語音。

接到需求之後,我開始檢視高德的相關API,由於圍欄是多邊形的,則需要後臺提供對應的圍欄的經緯度資料,

1.建立地理圍欄客戶端

fenceClient = new GeoFenceClient(mContext);
IntentFilter filter = new IntentFilter(
    ConnectivityManager.CONNECTIVITY_ACTION);
filter.addAction(GEOFENCE_BROADCAST_ACTION);
//註冊地理圍欄廣播
registerReceiver(mGeoFenceReceiver, filter);
fenceClient.createPendingIntent(GEOFENCE_BROADCAST_ACTION);
fenceClient.setActivateAction(GeoFenceClient.GEOFENCE_IN);
複製程式碼

地理圍欄廣播可以接收到客戶定位是否在圍欄內部,主要是用來處理圍欄相關的操作

2.根據圍欄客戶端建立地理圍欄


list = new ArrayList<>();
String lnglatGaode = encloseEntity.getLnglatGaode();
ArrayList<LnglatGaodeEntity> gaodeList = new GsonImpl()
  .toList(lnglatGaode, LnglatGaodeEntity.class);
for (LnglatGaodeEntity entity : gaodeList) {
latLng = new DPoint(StringUtils.parseDouble(entity.getLat()),
    StringUtils.parseDouble(entity.getLng()));
list.add(latLng);
}
DPoint firstLng = new DPoint(StringUtils.parseDouble(gaodeList.get(0).getLat()),
  StringUtils.parseDouble(gaodeList.get(0).getLng()));
list.add(firstLng);
//後臺提供的圍欄list資料,圍欄對應的id
fenceClient.addGeoFence(list, encloseEntity.getSrcurl());

複製程式碼

圍欄資料由於是不規則的,則需要根據一系列的經緯度集合直接畫出來,這樣一切相關的程式就準備好

3.圍欄廣播相關處理

  private BroadcastReceiver mGeoFenceReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
      LogUtils.w("mGeoFenceReceiver---------------------");
      // 接收廣播
      if (intent.getAction().equals(GEOFENCE_BROADCAST_ACTION)) {
        Bundle bundle = intent.getExtras();
        //獲取對應的圍欄的語音url地址
        String customId = bundle
            .getString(GeoFence.BUNDLE_KEY_CUSTOMID);
        String fenceId = bundle.getString(GeoFence.BUNDLE_KEY_FENCEID);
        GeoFence fence = bundle.getParcelable(GeoFence.BUNDLE_KEY_FENCE);
        //status標識的是當前的圍欄狀態,不是圍欄行為
        int status = bundle.getInt(GeoFence.BUNDLE_KEY_FENCESTATUS);
        StringBuffer sb = new StringBuffer();
        switch (status) {
          case GeoFence.STATUS_LOCFAIL:
            sb.append("定位失敗");
            break;
          case GeoFence.STATUS_IN:
            sb.append("進入圍欄 ");
            openMediaPlay(customId);
            break;
          case GeoFence.STATUS_OUT:
            sb.append("離開圍欄 ");
            mediaPlayer.pause();
            break;
          case GeoFence.STATUS_STAYED:
            sb.append("停留在圍欄內 ");
            break;
          default:
            break;
        }
        if (status != GeoFence.STATUS_LOCFAIL) {
          if (!TextUtils.isEmpty(customId)) {
            sb.append(" customId: " + customId);
          }
          sb.append(" fenceId: " + fenceId);
        }
        String str = sb.toString();
        Message msg = Message.obtain();
        msg.obj = str;
        msg.what = 2;
        mHandler.sendMessage(msg);
      }
    }
  };
複製程式碼

這裡使用message主要是為了讓對應的狀態status按照佇列傳送,不至於亂,獲取對應的語音url就可以播放了

4.檢視對應的handlerMessage

@Override
public void handleMessage(Message msg) {
    super.handleMessage(msg);
    switch (msg.what) {
      case 0:
        //繪製多邊形
        drawFence2Map();
        break;
      case 1:
        //圍欄新增失敗
        int errorCode = msg.arg1;
        break;
      case 2:
        //其他status資料型別
        String statusStr = (String) msg.obj;
        break;
      default:
        break;
    }
}

//依次繪製多邊形
private void drawFence2Map() {
    new Thread() {
      @Override
      public void run() {
        try {
          synchronized (lock) {
            if (null == fenceList || fenceList.isEmpty()) {
              return;
            }
            for (GeoFence fence : fenceList) {
              LogUtils.w("fence--id" + fence.getFenceId() + "---Custom--" + fence.getCustomId());
              if (fenceMap.containsKey(fence.getFenceId())) {
                continue;
              }
              drawPolygon(fence);
              fenceMap.put(fence.getFenceId(), fence);
            }
          }
        } catch (Throwable e) {

        }
      }
    }.start();
  }


private void drawPolygon(GeoFence fence) {
    final List<List<DPoint>> pointList = fence.getPointList();
    if (null == pointList || pointList.isEmpty()) {
      return;
    }
    for (List<DPoint> subList : pointList) {
      List<LatLng> lst = new ArrayList<LatLng>();
      PolygonOptions polygonOption = new PolygonOptions();
      for (DPoint point : subList) {
        lst.add(new LatLng(point.getLatitude(), point.getLongitude()));
        boundsBuilder.include(
            new LatLng(point.getLatitude(), point.getLongitude()));
      }
      polygonOption.addAll(lst);
      polygonOption.zIndex(2);
      polygonOption.strokeColor(Color.argb(255, 255, 20, 147))
          .fillColor(Color.argb(50, 255, 20, 147)).strokeWidth(2);
      aMap.addPolygon(polygonOption);
    }
}
複製程式碼

這裡加上繪製多邊形的圍欄方便測試圍欄效果

5.播放和暫停對應的音樂

private void openMediaPlay(String mp3) {
    try {
      if (!srcMp3.equals(mp3)) {
        mediaPlayer.setDataSource(Net.HOST + Net.PREFIX + mp3);
      }
      srcMp3 = mp3;
      mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
      // 通過非同步的方式裝載媒體資源
      mediaPlayer.prepareAsync();
      mediaPlayer.setOnPreparedListener(new OnPreparedListener() {
        @Override
        public void onPrepared(MediaPlayer mp) {
          // 裝載完畢回撥
          mediaPlayer.start();
        }
      });
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
複製程式碼

使用的是系統自帶的播放器

其實難點就在於圍欄的繪製那塊

相關文章