Android 整合高德地圖

語言: CN / TW / HK

文章目錄

demo的使用

如果想要在自己的專案中整合進高德,第一步就是要看看人家寫的demo,所以首先

下載高德的demo

下載地址

下載完以後包含4個資料夾,其中2DMap包含了我們平時用到的很多功能\ 這裡寫圖片描述

我們執行在手機上的時候,使用某些功能會提示\ 這裡寫圖片描述

這是SHA1值的問題,我們可以進行如下操作來暫時使用高德的demo

如何正常使用高德demo

1、獲取 SHA1 值\ 進入cmd

cd .android keytool -list -v -keystore debug.keystore

提示輸入金鑰庫密碼,編譯器提供的debug keystore預設密碼是 android\ 這裡寫圖片描述\ 此時可在控制檯顯示的資訊中獲取 SHA1 值\ 這裡寫圖片描述

2、登入高德開放平臺\ 高德開放平臺

建立應用,並新增新key\ 這裡寫圖片描述

3、複製應用的key\ 這裡寫圖片描述

填寫到AndroidManifest.xml中的如下位置

這裡寫圖片描述

這樣高德的demo就可以正常使用了\ 這裡寫圖片描述

在自己 app 整合高德,仿釘釘打卡

在這裡插入圖片描述

1、按照上面的步驟在高德開放平臺新增 release 的 簽名檔案獲取新的 key\ 2、要實現仿釘釘打卡,需要利用高德的定位 SDK 的地理圍欄功能,參見 示例中心:移動端地理圍欄,根據 開發指南:Android Studio 配置工程 引入定位依賴

kotlin implementation 'com.amap.api:3dmap:latest.integration' implementation 'com.amap.api:location:5.2.0'

3、build.gradle 確認使用的 key\ 為了除錯,可以把 debug 和 release 用一個簽名檔案

kotlin signingConfigs { debug { storeFile file('../app/key.jks') storePassword '123456' keyAlias "key0" keyPassword '123456' } release { storeFile file('../app/key.jks') storePassword '123456' keyAlias "key0" keyPassword '123456' } }

4、確認 build.gradle 中的 applicationId 是否是剛才申請 key 時填的一樣\ 5、AndroidManifest 中配置 高德 key,並新增許可權

```kotlin //地圖包、搜尋包需要的基礎許可權

    <!-- 設定key -->
    <meta-data
        android:name="com.amap.api.v2.apikey"
        android:value="設定剛才申請的key"/>
    <!-- 定位需要的服務 適配Android Q需要加上android:foregroundServiceType="location"-->
    <service
        android:name="com.amap.api.location.APSService"
        android:foregroundServiceType="location" />

```

6、因為我們要用到 地理圍欄,所以可以先看下官方的demo,在下載的例項中找到 Amap_Android_API_Location_Demo 並執行\ 在這裡插入圖片描述\ 7、把 Demo 專案中的頁面 plugin_geofence_map.xml 拿到自己專案中

```xml

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <com.amap.api.maps.MapView
        android:id="@+id/map"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    </com.amap.api.maps.MapView>

    <ScrollView
            android:layout_width="match_parent"
            android:layout_height="60dp"
            android:background="#D999"
            android:layout_alignParentBottom="true">
    <TextView
        android:id="@+id/tv_result"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
    </ScrollView>
</RelativeLayout>

```

程式碼直接把 GeoFence_Round_Activity 拿過來就行了

其中 CheckPermissionsActivity 是檢查以下許可權

java protected String[] needPermissions = { Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.READ_PHONE_STATE };

```java /* * 圓形地理圍欄 * * @author hongming.wang * @since 3.2.0 / public class GeoFence_Round_Activity extends CheckPermissionsActivity implements OnClickListener, GeoFenceListener, OnMapClickListener, LocationSource, AMapLocationListener, OnCheckedChangeListener {

private View lyOption;

private TextView tvGuide;
private TextView tvResult;

private EditText etCustomId;
private EditText etRadius;

private CheckBox cbAlertIn;
private CheckBox cbAlertOut;
private CheckBox cbAldertStated;

private Button btAddFence;
private Button btOption;

/**
 * 用於顯示當前的位置
 * <p>
 * 示例中是為了顯示當前的位置,在實際使用中,單獨的地理圍欄可以不使用定位介面
 * </p>
 */
private AMapLocationClient mlocationClient;
private OnLocationChangedListener mListener;
private AMapLocationClientOption mLocationOption;

private MapView mMapView;
private AMap mAMap;

// 中心點座標
private LatLng centerLatLng = null;
// 中心點marker
private Marker centerMarker;
private BitmapDescriptor ICON_YELLOW = BitmapDescriptorFactory
        .defaultMarker(BitmapDescriptorFactory.HUE_YELLOW);
private BitmapDescriptor ICON_RED = BitmapDescriptorFactory
        .defaultMarker(BitmapDescriptorFactory.HUE_RED);
private MarkerOptions markerOption = null;
private List<Marker> markerList = new ArrayList<Marker>();
// 當前的座標點集合,主要用於進行地圖的可視區域的縮放
private LatLngBounds.Builder boundsBuilder = new LatLngBounds.Builder();

// 地理圍欄客戶端
private GeoFenceClient fenceClient = null;
// 要建立的圍欄半徑
private float fenceRadius = 0.0F;
// 觸發地理圍欄的行為,預設為進入提醒
private int activatesAction = GeoFenceClient.GEOFENCE_IN;
// 地理圍欄的廣播action
private static final String GEOFENCE_BROADCAST_ACTION = "com.example.geofence.round";

// 記錄已經新增成功的圍欄
private HashMap<String, GeoFence> fenceMap = new HashMap<String, GeoFence>();

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_geofence_new);
    setTitle(R.string.roundGeoFence);
    // 初始化地理圍欄
    fenceClient = new GeoFenceClient(getApplicationContext());

    lyOption = findViewById(R.id.ly_option);
    btAddFence = (Button) findViewById(R.id.bt_addFence);
    btOption = (Button) findViewById(R.id.bt_option);
    tvGuide = (TextView) findViewById(R.id.tv_guide);
    tvResult = (TextView) findViewById(R.id.tv_result);
    tvResult.setVisibility(View.GONE);
    etCustomId = (EditText) findViewById(R.id.et_customId);
    etRadius = (EditText) findViewById(R.id.et_radius);

    cbAlertIn = (CheckBox) findViewById(R.id.cb_alertIn);
    cbAlertOut = (CheckBox) findViewById(R.id.cb_alertOut);
    cbAldertStated = (CheckBox) findViewById(R.id.cb_alertStated);

    mMapView = (MapView) findViewById(R.id.map);
    mMapView.onCreate(savedInstanceState);
    markerOption = new MarkerOptions().draggable(true);
    init();
}

void init() {
    if (mAMap == null) {
        mAMap = mMapView.getMap();
        mAMap.getUiSettings().setRotateGesturesEnabled(false);
        mAMap.moveCamera(CameraUpdateFactory.zoomBy(6));
        setUpMap();
    }

    btOption.setVisibility(View.VISIBLE);
    btOption.setText(getString(R.string.hideOption));
    resetView_round();

    btAddFence.setOnClickListener(this);
    btOption.setOnClickListener(this);
    cbAlertIn.setOnCheckedChangeListener(this);
    cbAlertOut.setOnCheckedChangeListener(this);
    cbAldertStated.setOnCheckedChangeListener(this);

    IntentFilter filter = new IntentFilter();
    filter.addAction(GEOFENCE_BROADCAST_ACTION);
    registerReceiver(mGeoFenceReceiver, filter);

    /**
     * 建立pendingIntent
     */
    fenceClient.createPendingIntent(GEOFENCE_BROADCAST_ACTION);
    fenceClient.setGeoFenceListener(this);
    /**
     * 設定地理圍欄的觸發行為,預設為進入
     */
    fenceClient.setActivateAction(GeoFenceClient.GEOFENCE_IN);
}

/**
 * 設定一些amap的屬性
 */
private void setUpMap() {
    mAMap.setOnMapClickListener(this);
    mAMap.setLocationSource(this);// 設定定位監聽
    mAMap.getUiSettings().setMyLocationButtonEnabled(true);// 設定預設定位按鈕是否顯示
    // 自定義系統定位藍點
    MyLocationStyle myLocationStyle = new MyLocationStyle();
    // 自定義定位藍點圖示
    myLocationStyle.myLocationIcon(
            BitmapDescriptorFactory.fromResource(R.drawable.gps_point));
    // 自定義精度範圍的圓形邊框顏色
    myLocationStyle.strokeColor(Color.argb(0, 0, 0, 0));
    // 自定義精度範圍的圓形邊框寬度
    myLocationStyle.strokeWidth(0);
    // 設定圓形的填充顏色
    myLocationStyle.radiusFillColor(Color.argb(0, 0, 0, 0));
    // 將自定義的 myLocationStyle 物件新增到地圖上
    mAMap.setMyLocationStyle(myLocationStyle);
    mAMap.setMyLocationEnabled(true);// 設定為true表示顯示定位層並可觸發定位,false表示隱藏定位層並不可觸發定位,預設是false
    // 設定定位的型別為定位模式 ,可以由定位、跟隨或地圖根據面向方向旋轉幾種
    mAMap.setMyLocationType(AMap.LOCATION_TYPE_LOCATE);
}

/**
 * 方法必須重寫
 */
@Override
protected void onResume() {
    super.onResume();
    mMapView.onResume();
}

/**
 * 方法必須重寫
 */
@Override
protected void onPause() {
    super.onPause();
    mMapView.onPause();
    deactivate();
}

/**
 * 方法必須重寫
 */
@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    mMapView.onSaveInstanceState(outState);
}

/**
 * 方法必須重寫
 */
@Override
protected void onDestroy() {
    super.onDestroy();
    mMapView.onDestroy();
    try {
        unregisterReceiver(mGeoFenceReceiver);
    } catch (Throwable e) {
    }

    if (null != fenceClient) {
        fenceClient.removeGeoFence();
    }
    if (null != mlocationClient) {
        mlocationClient.onDestroy();
    }
}

@Override
public void onClick(View v) {
    switch (v.getId()) {
        case R.id.bt_addFence :
            addFence();
            break;
        case R.id.bt_option :
            if (btOption.getText().toString()
                    .equals(getString(R.string.showOption))) {
                lyOption.setVisibility(View.VISIBLE);
                btOption.setText(getString(R.string.hideOption));
            } else {
                lyOption.setVisibility(View.GONE);
                btOption.setText(getString(R.string.showOption));
            }
            break;
        default :
            break;
    }
}

private void drawFence(GeoFence fence) {
    switch (fence.getType()) {
        case GeoFence.TYPE_ROUND :
        case GeoFence.TYPE_AMAPPOI :
            drawCircle(fence);
            break;
        case GeoFence.TYPE_POLYGON :
        case GeoFence.TYPE_DISTRICT :
            drawPolygon(fence);
            break;
        default :
            break;
    }

    // // 設定所有maker顯示在當前可視區域地圖中
    // LatLngBounds bounds = boundsBuilder.build();
    // mAMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 150));

    removeMarkers();
}

private void drawCircle(GeoFence fence) {
    LatLng center = new LatLng(fence.getCenter().getLatitude(),
            fence.getCenter().getLongitude());
    // 繪製一個圓形
    mAMap.addCircle(new CircleOptions().center(center)
            .radius(fence.getRadius()).strokeColor(Const.STROKE_COLOR)
            .fillColor(Const.FILL_COLOR).strokeWidth(Const.STROKE_WIDTH));
    boundsBuilder.include(center);
}

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.strokeColor(Const.STROKE_COLOR)
                .fillColor(Const.FILL_COLOR).strokeWidth(Const.STROKE_WIDTH);
        mAMap.addPolygon(polygonOption);
    }
}

Object lock = new Object();
void drawFence2Map() {
    new Thread() {
        @Override
        public void run() {
            try {
                synchronized (lock) {
                    if (null == fenceList || fenceList.isEmpty()) {
                        return;
                    }
                    for (GeoFence fence : fenceList) {
                        if (fenceMap.containsKey(fence.getFenceId())) {
                            continue;
                        }
                        drawFence(fence);
                        fenceMap.put(fence.getFenceId(), fence);
                    }
                }
            } catch (Throwable e) {

            }
        }
    }.start();
}

Handler handler = new Handler() {
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case 0 :
                StringBuffer sb = new StringBuffer();
                sb.append("新增圍欄成功");
                String customId = (String)msg.obj;
                if(!TextUtils.isEmpty(customId)){
                    sb.append("customId: ").append(customId);
                }
                Toast.makeText(getApplicationContext(), sb.toString(),
                        Toast.LENGTH_SHORT).show();
                drawFence2Map();
                break;
            case 1 :
                int errorCode = msg.arg1;
                Toast.makeText(getApplicationContext(),
                        "新增圍欄失敗 " + errorCode, Toast.LENGTH_SHORT).show();
                break;
            case 2 :
                String statusStr = (String) msg.obj;
                tvResult.setVisibility(View.VISIBLE);
                tvResult.append(statusStr + "\n");
                break;
            default :
                break;
        }
    }
};

List<GeoFence> fenceList = new ArrayList<GeoFence>();
@Override
public void onGeoFenceCreateFinished(final List<GeoFence> geoFenceList,
        int errorCode, String customId) {
    Message msg = Message.obtain();
    if (errorCode == GeoFence.ADDGEOFENCE_SUCCESS) {
        fenceList.addAll(geoFenceList);
        msg.obj = customId;
        msg.what = 0;
    } else {
        msg.arg1 = errorCode;
        msg.what = 1;
    }
    handler.sendMessage(msg);
}

/**
 * 接收觸發圍欄後的廣播,當新增圍欄成功之後,會立即對所有圍欄狀態進行一次偵測,如果當前狀態與使用者設定的觸發行為相符將會立即觸發一次圍欄廣播;
 * 只有當觸發圍欄之後才會收到廣播,對於同一觸發行為只會傳送一次廣播不會重複傳送,除非位置和圍欄的關係再次發生了改變。
 */
private BroadcastReceiver mGeoFenceReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        // 接收廣播
        if (intent.getAction().equals(GEOFENCE_BROADCAST_ACTION)) {
            Bundle bundle = intent.getExtras();
            String customId = bundle
                    .getString(GeoFence.BUNDLE_KEY_CUSTOMID);
            String fenceId = bundle.getString(GeoFence.BUNDLE_KEY_FENCEID);
            //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("進入圍欄 ");
                    break;
                case GeoFence.STATUS_OUT :
                    sb.append("離開圍欄 ");
                    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;
            handler.sendMessage(msg);
        }
    }
};

@Override
public void onMapClick(LatLng latLng) {
    markerOption.icon(ICON_YELLOW);
    centerLatLng = latLng;
    addCenterMarker(centerLatLng);
    tvGuide.setBackgroundColor(getResources().getColor(R.color.gary));
    tvGuide.setText("選中的座標:" + centerLatLng.longitude + ","
            + centerLatLng.latitude);
}

/**
 * 定位成功後回撥函式
 */
@Override
public void onLocationChanged(AMapLocation amapLocation) {
    if (mListener != null && amapLocation != null) {
        if (amapLocation != null && amapLocation.getErrorCode() == 0) {
            tvResult.setVisibility(View.GONE);
            mListener.onLocationChanged(amapLocation);// 顯示系統小藍點
        } else {
            String errText = "定位失敗," + amapLocation.getErrorCode() + ": "
                    + amapLocation.getErrorInfo();
            Log.e("AmapErr", errText);
            tvResult.setVisibility(View.VISIBLE);
            tvResult.setText(errText);
        }
    }
}

/**
 * 啟用定位
 */
@Override
public void activate(OnLocationChangedListener listener) {
    mListener = listener;
    if (mlocationClient == null) {
        mlocationClient = new AMapLocationClient(this);
        mLocationOption = new AMapLocationClientOption();
        // 設定定位監聽
        mlocationClient.setLocationListener(this);
        // 設定為高精度定位模式
        mLocationOption.setLocationMode(AMapLocationMode.Hight_Accuracy);
        // 只是為了獲取當前位置,所以設定為單次定位
        mLocationOption.setOnceLocation(true);
        // 設定定位引數
        mlocationClient.setLocationOption(mLocationOption);
        mlocationClient.startLocation();
    }
}

/**
 * 停止定位
 */
@Override
public void deactivate() {
    mListener = null;
    if (mlocationClient != null) {
        mlocationClient.stopLocation();
        mlocationClient.onDestroy();
    }
    mlocationClient = null;
}

private void addCenterMarker(LatLng latlng) {
    if (null == centerMarker) {
        centerMarker = mAMap.addMarker(markerOption);
    }
    centerMarker.setPosition(latlng);
    markerList.add(centerMarker);
}

private void removeMarkers() {
    if(null != centerMarker){
        centerMarker.remove();
        centerMarker = null;
    }
    if (null != markerList && markerList.size() > 0) {
        for (Marker marker : markerList) {
            marker.remove();
        }
        markerList.clear();
    }
}

@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    switch (buttonView.getId()) {
        case R.id.cb_alertIn :
            if (isChecked) {
                activatesAction |= GeoFenceClient.GEOFENCE_IN;
            } else {
                activatesAction = activatesAction
                        & (GeoFenceClient.GEOFENCE_OUT
                                | GeoFenceClient.GEOFENCE_STAYED);
            }
            break;
        case R.id.cb_alertOut :
            if (isChecked) {
                activatesAction |= GeoFenceClient.GEOFENCE_OUT;
            } else {
                activatesAction = activatesAction
                        & (GeoFenceClient.GEOFENCE_IN
                                | GeoFenceClient.GEOFENCE_STAYED);
            }
            break;
        case R.id.cb_alertStated :
            if (isChecked) {
                activatesAction |= GeoFenceClient.GEOFENCE_STAYED;
            } else {
                activatesAction = activatesAction
                        & (GeoFenceClient.GEOFENCE_IN
                                | GeoFenceClient.GEOFENCE_OUT);
            }
            break;
        default :
            break;
    }
    if (null != fenceClient) {
        fenceClient.setActivateAction(activatesAction);
    }
}

private void resetView_round() {
    etRadius.setHint("圍欄半徑");
    etRadius.setVisibility(View.VISIBLE);
    tvGuide.setBackgroundColor(getResources().getColor(R.color.red));
    tvGuide.setText("請點選地圖選擇圍欄的中心點");
    tvGuide.setVisibility(View.VISIBLE);
}

/**
 * 新增圍欄
 * 
 * @since 3.2.0
 * @author hongming.wang
 *
 */
private void addFence() {
    addRoundFence();
}

/**
 * 新增圓形圍欄
 * 
 * @since 3.2.0
 * @author hongming.wang
 *
 */
private void addRoundFence() {
    String customId = etCustomId.getText().toString();
    String radiusStr = etRadius.getText().toString();
    if (null == centerLatLng
            || TextUtils.isEmpty(radiusStr)) {
        Toast.makeText(getApplicationContext(), "引數不全", Toast.LENGTH_SHORT)
                .show();
        return;
    }

    DPoint centerPoint = new DPoint(centerLatLng.latitude,
            centerLatLng.longitude);
    fenceRadius = Float.parseFloat(radiusStr);
    fenceClient.addGeoFence(centerPoint, fenceRadius, customId);
}

} ```

除錯

我們可以直接在網頁測試或驗證:

高德搜尋關鍵字獲取座標

其他除錯