1. 程式人生 > >Android開發,MapBox的使用及部分功能實現(一)----- 初始化、標記、定位、styleurl

Android開發,MapBox的使用及部分功能實現(一)----- 初始化、標記、定位、styleurl

近期,應公司要求,開始接觸MapBox For Android的開發。
經過初步的接觸,發現MapBox與我之前使用的Arcgis有很多不同,相比起來,MapBox更清潔,更輕便,也更容易使用,但是相對的,MapBox相對於Arcgis缺少了很多的功能實現,許多的東西都需要自己去進行處理才能實現。
下面是我一步步接觸並使用MapBox的記錄,可能會比較亂。我這裡提供了目錄的功能,請不要使用csdn的極客模式,不然看不到目錄的,另外,由於是一步步的寫下來了,可能前期的看法或者結論,到了後期就不一樣了,我會定期修改,但是可能會存在漏網的,歡迎指出。
首先是接入,相比於Arcgis又需要匯入包檔案,又需要匯入lib,又需要設定jcenter,打包下來,直接就幾十兆的Apk,一編譯就是一分鐘。
MapBox只需要匯入一下程式碼即可

compile('com.mapbox.mapboxsdk:mapbox-android-sdk:[email protected]') {
        transitive = true
    }

哦,對了,還有mavencentral,一般來說,應該都設定了的

allprojects {
    repositories {
        jcenter()
        mavenCentral()
    }
}

一般來說,下載一段時間後,就可以了,如果不可以,就試著翻牆,設定代理什麼的,這裡不細說了。

MapBox和Arcgis相同,在Xml中新增一個MapView就可以了

<com.mapbox.mapboxsdk.maps.MapView
        android:id="@+id/mapView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

如果是從網上,或者demo上學習的朋友,可能會遇到一個問題
就是在xml上無法設定初始座標,zoom等引數,只能強行寫出來,我估計是該控制元件item的問題。為了更好的學習,我將這些功能都放到了程式碼中來實現。

申請accessToken

就和arcgis的key類似,就是一個使用憑證,就不多說了,可以去

官方網站申請

進行初始化

申請完畢後,在Activity的onCreate()中新增如下程式碼,進行初始化

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //設定accessToken
        Mapbox.getInstance(this, getString(R.string.accessToken));
 setContentView(R.layout.activity_simple_map); }

敲黑板,這裡的初始化程式碼一定要放到setContentView之前,否則無法使用。

初始化Mapview

mapView = (MapView) findViewById(R.id.mapView);
        mapView.setStyleUrl("mapbox://styles/mapbox/streets-v9");
        mapView.onCreate(savedInstanceState);

注意,其中的setStyleUrl,就是設定地圖樣式,可以採用預設樣式,Light,Dark等,也可以使用網路地址,事實上預設的Style.LIGHT也是一個地址

還有下面的

mapView.onCreate(savedInstanceState);

需要在初始化的時候設定,同時,除了onCreate,還包括以下

// Add the mapView lifecycle to the activity's lifecycle methods
    @Override
    public void onResume() {
        super.onResume();
        mapView.onResume();
    }


    @Override
    protected void onStart() {
        super.onStart();
        mapView.onStart();
    }


    @Override
    protected void onStop() {
        super.onStop();
        mapView.onStop();
    }


    @Override
    public void onPause() {
        super.onPause();
        mapView.onPause();
    }


    @Override
    public void onLowMemory() {
        super.onLowMemory();
        mapView.onLowMemory();
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        mapView.onDestroy();
    }


    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        mapView.onSaveInstanceState(outState);
    }

都要進行設定,建議封裝到baseactvity中


非同步載入地圖

mapView.getMapAsync(new OnMapReadyCallback() {
            @Override
            public void onMapReady(MapboxMap mapboxMap) {
                
            }
        });

該方法即是對地圖進行載入,載入成功後,會返回MapboxMap物件,只有有了這個物件,我們才可能對地圖進行各種操作,和Arcgis不同,arcgis的大部分操作,都是在mapview上進行操作的,但是MapBox的mapview並沒有太多的功能元素,像地圖定點、polygon、marker等操作,都得對mapboxmap進行操作,我估計很多朋友都會和我一樣,想進行初始化時中心點設定的時候,嘗試了centerAt、location、setCenter、setPosition、moveTo、moveCamere。。等等一系列的姿勢去設定,都失敗了。

更多操作

從上一步開始,地圖已經可以跑起來了,下面就是一些進一步的使用,一下排序不分先後,純粹是我做到哪,寫到哪,不過我提供了目錄,需要的朋友可以直接在目錄中進行搜尋,如果是和我一樣才開始學習的,可以跟著我的程式碼一直敲就可以了。

將地圖移動到某一點

這個方法有很多

LatLng latLng = new LatLng(29.735281, 106.99155);
mapboxMap.moveCamera(CameraUpdateFactory.newLatLng(latLng));

最簡單的地圖定點,是的,沒錯,這就centerAt的功能

首先我來看前面的方法moveCamera;這個方法就是簡單將地圖中心定到某個位置,更多的還有

注意,這個LatLng不僅僅可以傳入座標,還可以傳入location的

mapboxMap.animateCamera(CameraUpdateFactory.newLatLng(latLng),2000);//動畫移動,花費兩秒時間,動態的移動到該位置,時間可以不寫
mapboxMap.easeCamera(CameraUpdateFactory.newLatLng(latLng),2000);//講真,我也沒發現和其他的有什麼區別

然後是後面的引數

後面可以有四種形式




很顯然,一個必填,三個可填。
主要呢是必填項CamerUpdate型別的引數,CameraUpdate呢,其實可以當做一個數據處理命令集來使用,即先將要move到的位置的各種引數設定完成,封裝好後,再來進行處理。
CameraUpdate的構造需要用到CamaeraUpdateFactory(目前就我瞭解的話)
CamaeraUpdateFactory專門是用來構建CameraUpdate的,是他的工廠類,我們來看看他的new方法(zoomby,scrollby我沒用過。。)
①.newLatLng 根據簡單的座標點來進行構造

這裡只需要傳入一個座標引數即可,LatLng,用過Arcgis等地圖的都知道,這就是一個座標,可以用new LatLng(double latitude,double longitude)來進行構造,同下,我們統一用animateCamera來進行舉例。

mapboxMap.animateCamera(CameraUpdateFactory.newLatLng(latLng),2000);

②.newCameraPosition 根據CameraPosition來進行構造

這裡需要傳入的是一個CameraPosition的物件,初始化

CameraPosition cameraPosition = new CameraPosition.Builder()         
			.target(new LatLng(latLng.getLatitude(), latLng.getLongitude()))
                        .zoom(17)//放大尺度 從0開始,0即最大比例尺,最大未知,17左右即為街道層級
                        .bearing(180)//地圖旋轉,但並不是每次點選都旋轉180度,而是相對於正方向180度,即如果已經為相對正方向180度了,就不會進行旋轉
                        .tilt(30)//地圖傾斜角度,同上,相對於初始狀態(平面)成30度
                        .build();//建立CameraPosition物件

敲黑板了,注意其中的引數,這幾個引數在很多地方都有用到。

mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), 2000);

使用就很簡單了,大同小異。

③.newLatLngBounds 根據一個座標座標邊界進行移動,這個有兩種方法,除了必傳的LatLngBounds,都一樣,不一樣的就是padding,一種是統一padding,一種是四個方向,這個我就不細細說了,我只說統一padding

LatLngBounds latLngBounds = new LatLngBounds.Builder()//根據座標集,初始化一個座標界限
                        .include(latlng1)
                        .include(latlng2)
                        .include(latlng3)
                        .include(latlng4)
                        .build();

構造是很簡單的,傳入座標點即可,除了include,每次只新增一個點,也可以採用includes,一次傳入一個點的集合

mapboxMap.animateCamera(CameraUpdateFactory.newLatLng(latLngBounds,50), 2000);

其中的50指的是padding,使用這種構造,地圖會將比例尺自動調節到剛好能顯示全部點以及點外面的padding的程度。
④newLatLngZoom 這個和第一個很相似

無非是這個還可以傳入一個zoom即縮放尺度的值

mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng,13),2000);

至此,移動的方法就全部用完了。
mapview在fragment中的使用

用過arcgis的可能都知道,mapview在fragment中的使用和在activity中使用其實是一樣的,但是在mapbox中,還為fragment提供了另外一種構造方式,

SupportMapFragment。
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fragment_map);
        //設定accessToken
        Mapbox.getInstance(this, getString(R.string.accessToken));
 }

為了加深記憶,初始化這個再寫一次,可能有人發現了,這裡我們的getInstance的方法並沒有放到setContentView裡面,這是我故意的,因為如果mapview要在activity中使用,就必須放在setContentView之前,但是如果在fragment中使用就不需要了。

// Create fragment
        final FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();


        if (savedInstanceState == null) {
            // Build mapboxMap
            MapboxMapOptions options = new MapboxMapOptions();
            options.styleUrl(Style.LIGHT);
            options.camera(new CameraPosition.Builder()
                    .target(new LatLng(29.735281, 106.99155))
                    .zoom(13)
                    .build());

            // Create map fragment
            mapFragment = SupportMapFragment.newInstance(options);


            // Add map fragment to parent container
            transaction.add(R.id.container, mapFragment, "com.mapbox.map");
            transaction.commit();
        } else {
            mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentByTag("com.mapbox.map");
        }

        mapFragment.getMapAsync(new OnMapReadyCallback() {
            @Override
            public void onMapReady(MapboxMap mapboxMap) {

            }
        });

以上就是構建的方法,比較長,一句一句的看,首先我再看看xml檔案

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/whitesmoke">


    <TextView
        android:id="@+id/fragment_below_textview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:gravity="center_horizontal"
        android:text="整合在fragment上的"/>


    <android.support.v7.widget.CardView
        android:id="@+id/cardview"
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:layout_below="@id/fragment_below_textview"
        android:layout_marginBottom="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        app:cardCornerRadius="2dp"
        app:cardElevation="@dimen/cardview_default_elevation">


        <FrameLayout
            android:id="@+id/container"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    </android.support.v7.widget.CardView>




</RelativeLayout>


只需要看其中的container,這個是用來存放fragment的容器,首先建立一個FragmentTranstion物件

final FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();


再動態例項化地圖物件

// Build mapboxMap
            MapboxMapOptions options = new MapboxMapOptions();
            options.styleUrl(Style.LIGHT);
            options.camera(new CameraPosition.Builder()
                    .target(new LatLng(29.735281, 106.99155))
                    .zoom(13)
                    .build());
            // Create map fragment
            SupportMapFragment mapFragment = SupportMapFragment.newInstance(options);


再次敲個黑板,這裡的MapBoxOptions,是用於地圖初始化時給地圖初始化設定各種引數的。相當於配置檔案,這裡使用options初始化了SupportMapFragment

,options也可以在activity中進行使用,比如這樣

MapboxMapOptions options = new MapboxMapOptions()
      .styleUrl(Style.OUTDOORS)
      .camera(new CameraPosition.Builder()
        .target(new LatLng(43.7383, 7.4094))
        .zoom(12)
        .build());

    // create map
    mapView = new MapView(this, options);
    mapView.onCreate(savedInstanceState);
    mapView.getMapAsync(new OnMapReadyCallback() {
      @Override
      public void onMapReady(MapboxMap mapboxMap) {
        // Customize map with markers, polylines, etc.

      }
    });

    setContentView(mapView);

其實設定options和在onMapReady中進行設定,或者定位是差不多的,只是一個是動態新增使用,一個是在xml中新增使用,這裡就先不細說了。我們繼續剛剛的

// Add map fragment to parent container
            transaction.add(R.id.container, mapFragment, "com.mapbox.map");
            transaction.commit();


這個就是將fragment新增到容器中去,這是android的程式碼,不是mapbox的程式碼,沒什麼好說的。
至此就實現了mapbox在fragment中的使用,如下圖

其實設定options和在onMapReady中進行設定,或者定位是差不多的,只是一個是動態新增使用,一個是在xml中新增使用,這裡就先不細說了。我們繼續剛剛的

// Add map fragment to parent container
            transaction.add(R.id.container, mapFragment, "com.mapbox.map");
            transaction.commit();

這個就是將fragment新增到容器中去,這是android的程式碼,不是mapbox的程式碼,沒什麼好說的。

至此就實現了mapbox在fragment中的使用,如下圖
地圖風格,StyleUrl
這個就是mapview.setStyleUrl了,這個引數呢,可以通過mapview來進行設定,也可以通過mapboxmap來進行設定,上面大概的說了一點,這裡詳細的說一下。
這個就是地圖的風格,其實不管是系統自帶的風格,還是自己的寫的其他的,都是一個url地址而已。
先看系統自帶:
Style.MAPBOX_STREETS  預設的
Style.DARK 黑色主題的
Style.LIGHT 亮色主題
Style.OUTDOORS  戶外的,具體有什麼不同數不清楚我
Style.SATELLITE 影像圖
Style.SATELLITE_STREETS 更為詳細的影像圖
他們都是一個地址,比如DARK,他的地址就是mapbox://styles/mapbox/dark-v9 你也可以嘗試一下 "https://www.mapbox.com/android-sdk/files/mapbox-raster-v8.json" 等,更多的我也沒有嘗試過了,畢竟我一般來說都不會用官方提供的地址,而是使用天地圖
地圖示記,Marker
很好理解
首先是最基礎的使用
mapboxMap.addMarker(new MarkerOptions()
                        .position(new LatLng(29.619861, 106.515911))
                        .title("MarkerTitle")
                        .snippet("Message detail"));
addMarker就是新增一個地圖示記,需要傳入一個MarkerOptions或者BaseMarkerOptions、BaseMarkerViewOptions這三種的其中一種如果傳入BaseMarkerViewOptions,還可以傳入一個OnMarkerViewAddedListener,這個之後再說先看最簡單的MarkerOptions,這就是一個定點的座標標記,positon是座標點,title是點選圖標出現的資訊的標題,snippet是內容,如圖假如你嘗試新增很多個,嘗試移除全部重新新增等操作的時候,第一個坑就出現了那就是竟然沒有mapboxMap.removeAllMarkers(),這樣的方法,一般來說,用腳想這玩意兒也應該是存在的,然而他就是沒有。想要移除,一般的方法如下
for (Marker marker : mapboxMap.getMarkers()) {
            mapboxMap.removeMarker(marker);
        }
這就特別坑了,另外你還可以用
mapboxMap.clear();
這個方法,但是這個主要是用來清空mapview上所有新增上去的東西的,或者就是
mapboxMap.removeAnnotations();
這個是用來移除所有註釋,不限於標記,但是至少不會移除layer。所以我建議最好還是封裝出來removeAllMarker()這樣的方法吧。

然後來試一試使用自己的圖示
IconFactory iconFactory = IconFactory.getInstance(this);
                Icon icon = iconFactory.fromResource(R.mipmap.purple_marker);
                MarkerOptions markerOptions = new MarkerOptions()
                        .position(new LatLng(29.619861, 106.515911))
                        .title("MarkerTitle")
                        .snippet("MarkerInfo")
                        .icon(icon);
                mapboxMap.addMarker(markerOptions);
如上,想要使用自己的icon,需要先初始化一個icon工廠類,該類可以從bitmap、resource、path、file、asset這幾個來源來生成icon 但是,這裡又有一個坑,就是大小不是定死的,也就是說,假如你給的圖片特別大,他在地圖上顯示就會特別大,你可以嘗試將圖片放到不同解析度的mipmap資料夾中,如果你使用的drawable中來使用,就切換drawable,這是在使用資原始檔的情況下,如果你使用的其他型別,一定要提前設定好長和寬。

新增多個Marker
新增多個其實可以採用重複addMarker來實現,這裡講另外一種
MarkerOptions marker1 = new MarkerOptions()
                        .position(new LatLng(29.619861, 106.515911))
                        .title("MarkerTitle")
                        .snippet("Message detail");
                MarkerOptions marker2 = new MarkerOptions()
                        .position(new LatLng(29.612261, 106.154911))
                        .title("MarkerTitle")
                        .snippet("Message detail");
                MarkerOptions marker3 = new MarkerOptions()
                        .position(new LatLng(29.519861, 106.547211))
                        .title("MarkerTitle")
                        .snippet("Message detail");
                MarkerOptions marker4 = new MarkerOptions()
                        .position(new LatLng(29.657261, 106.152711))
                        .title("MarkerTitle")
                        .snippet("Message detail");
                List<MarkerOptions> markerList = new ArrayList<>();
                markerList.add(marker1);
                markerList.add(marker2);
                markerList.add(marker3);
                markerList.add(marker4);
                mapboxMap.addMarkers(markerList);
如上,採用addMarkers來進行新增,很簡單,不多說。
帶動畫的Marker
這個其實並不是mapbox提供的方法,是我在官方demo上發現的,貌似得在android 25.0.0版本以上才能使用,我覺得很不錯,特意分享一下
final Marker marker = mapboxMap.addMarker(new MarkerViewOptions()
                        .position(new LatLng(29.619861, 106.515911))
                        .title("移動")
                        .snippet("也不動"));
                mapboxMap.setOnMapClickListener(new MapboxMap.OnMapClickListener() {
                    @Override
                    public void onMapClick(@NonNull LatLng latLng) {
                        ValueAnimator maValueAnimator = ObjectAnimator.ofObject(marker, "position",
                                new LatLngEvaluator(), marker.getPosition(), latLng);
                        maValueAnimator.setDuration(2000);
                        maValueAnimator.start();
                    }
                });

需要一個內部類

private static class LatLngEvaluator implements TypeEvaluator<LatLng> {
        // Method is used to interpolate the marker animation.

        private LatLng latLng = new LatLng();

        @Override
        public LatLng evaluate(float fraction, LatLng startValue, LatLng endValue) {
            latLng.setLatitude(startValue.getLatitude()
                    + ((endValue.getLatitude() - startValue.getLatitude()) * fraction));
            latLng.setLongitude(startValue.getLongitude()
                    + ((endValue.getLongitude() - startValue.getLongitude()) * fraction));
            return latLng;
        }
    }
實現原理,我沒有深度研究,各位可以看看。
marker的詳細設定
IconFactory iconFactory1 = IconFactory.getInstance(this);
                Icon icon1 = iconFactory1.fromResource(R.mipmap.purple_marker);
                // marker view using all the different options available
                mapboxMap.addMarker(new MarkerViewOptions()
                        .position(new LatLng(29.619861, 106.515911))
                        .icon(icon1)
                        .rotation(70)//傾斜角度
                        .anchor(1f, 1f)
                        .alpha(0.5f)//透明度
                        .title("Hisense Arena")
                        .snippet("Olympic Blvd, Melbourne VIC 3001")
                        .infoWindowAnchor(0.5f, 0.5f)//infowindow的
                        .flat(false));//是否是平的
這裡使用的就不是簡單的MarkerOption了,這裡使用的是MarkerViewOptions 他可以設定rotation即傾斜排程
anchor,這個東西我一直沒搞清楚是什麼效果,各種設定都沒變化 alpha,透明度,不解釋 flat,這個,代表了地圖傾斜角變化後,標記是否也跟著變扁平,預設是false
infoWindowAnchor,這個也比較好理解,就是infowindow彈出與圖示的相對位置

可以看到圖示是傾斜的,另外,兩個標記,上面那個的flat設定為false,下面的設定為了true

自定義的infowindow

marker點擊出現的infowindow的自定義介面,如下

mapboxMap.addMarker(new MarkerOptions()
                        .position(new LatLng(29.619861, 106.515911))
                        .title("infowindow1"));
                mapboxMap.addMarker(new MarkerOptions()
                        .position(new LatLng(29.519861, 106.615911))
                        .title("infowindow2"));
                mapboxMap.addMarker(new MarkerOptions()
                        .position(new LatLng(29.719861, 106.415911))
                        .title("infowindow3"));
                mapboxMap.setInfoWindowAdapter(new MapboxMap.InfoWindowAdapter() {
                    @Nullable
                    @Override
                    public View getInfoWindow(@NonNull final Marker marker) {
                        LinearLayout parent = new LinearLayout(MarkerActivity.this);
                        parent.setLayoutParams(new LinearLayout.LayoutParams(ZXSystemUtil.dp2px(MarkerActivity.this, 200), ZXSystemUtil.dp2px(MarkerActivity.this, 80)));
                        parent.setOrientation(LinearLayout.HORIZONTAL);
                        parent.setBackgroundColor(ContextCompat.getColor(MarkerActivity.this, R.color.whitesmoke));
                        ImageView imageView = new ImageView(MarkerActivity.this);
                        TextView textView = new TextView(MarkerActivity.this);
                        switch (marker.getTitle()) {
                            case "infowindow1":
                                imageView.setImageDrawable(ContextCompat.getDrawable(MarkerActivity.this, R.mipmap.__picker_checkbox_marked));
                                break;
                            case "infowindow2":
                                imageView.setImageDrawable(ContextCompat.getDrawable(MarkerActivity.this, R.mipmap.__picker_ic_camera_p));
                                break;
                            case "infowindow3":
                                imageView.setImageDrawable(ContextCompat.getDrawable(MarkerActivity.this, R.mipmap.__picker_ic_broken_image_black_48dp));
                                break;
                            default:
                                break;
                        }
                        textView.setText(marker.getTitle());
                        textView.setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View view) {
                                ZXToastUtil.showToast(MarkerActivity.this, marker.getTitle()+"123");
                            }
                        });
                        parent.addView(imageView);
                        parent.addView(textView);
                        return parent;
                    }
                });
可以看到,我添加了三個marker,並使用了setInfoWindowAdapter這個方法,這個方法就是用於給infowindow設定介面卡的,我這裡是動態生成的介面,也可以使用View.inflater來新增xml介面生成view,就可以實現在infowindow上進行各種操作。同時可以實現點選事件等,marker是MarkerOption,MarkerViewOption等的父類,可以根據這個marker傳過來的title來進行判斷是哪一個marker,來區分不同marker實現不同的介面。

但是需要注意的是,這裡的parent是無法設定點選事件的,也就是說infowindow內部的view的各種事件可以在這裡實現,但是infowindow本身不能在這裡設定點選事件,如果需要設定,需要使用另外一個方法,

mapboxMap.setOnInfoWindowClickListener(new MapboxMap.OnInfoWindowClickListener() {
                    @Override
                    public boolean onInfoWindowClick(@NonNull Marker marker) {
                        ZXToastUtil.showToast(MarkerActivity.this, marker.getTitle());
                        return true;
                    }
                });
注:裡面的ZXToastUtil是我自己封裝的一個Toast方法,你們使用Toast.makeToast就可以了。

同樣的,還可以實現setOnInfoWindowLongClickListener和setOnInfoWindowCloseListener兩個監聽

地圖定位
地圖定位,不多說了,都懂,直接看程式碼
1.基礎使用
//獲取定位引擎並激活
        LocationSource locationEngine = LocationSource.getLocationEngine(this);
        locationEngine.activate();
ps:地圖基礎設定那些,我就不寫了,和前面一樣。 我這裡添加了一個FloatingActionButton,為了測試定位的開啟和關閉,你們可以使用按鈕什麼的  都可以
fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (mapboxMap != null) {
                    toggleGps();
                }
            }
        });
只需要看裡面的toggleGps();這個方法。
//開關定位
    private void toggleGps() {
        if (mapboxMap.isMyLocationEnabled()) {//已開啟
            enableLocation(false);//若已開啟,就將它關閉
        } else {//未開啟
            if (ZXPermissionUtil.checkLocationPermissions(this)) {
                enableLocation(true);//若未開啟,就在檢查完許可權後,將它開啟
            } else {
                ZXPermissionUtil.requestLocationPermissions(this);
            }
        }
    }
這個方法呢其實就是開啟或者關閉定位

裡面有個判斷語句

mapboxMap.isMyLocationEnabled()

很好理解,就字面意思,判斷定位是否開啟。

如果開啟,就關閉定位,如果未開啟,就先檢查許可權,如果有許可權就開啟定位,如果沒有許可權就開啟許可權,裡面的ZXPermissionUtil是我封裝的許可權工具,這裡就不說的,你們使用checkPermission就可以了,後面有空再將我整合的工具類、view類、manager等拿出來分享。

好,然後是裡面的
enableLocation(true)
如下
//設定定位
    private void enableLocation(boolean enable) {
        if (enable) {
            //獲取上次定位引數,如果存在先直接使用
            final Location lastlocation = locationEngine.getLastLocation();
            if (lastlocation != null) {
                mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(lastlocation), 15), 1000);
            }
            locationEngineListener = new LocationEngineListener() {
                @Override
                public void onConnected() {
                    //連線到定位服務,不需要操作
                }

                @Override
                public void onLocationChanged(Location location) {
                    if (location != null) {
                        mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(location), 15), 1000);
                        locationEngine.removeLocationEngineListener(this);
                    }
                }
            };
            //設定監聽器
            locationEngine.addLocationEngineListener(locationEngineListener);
            fab.setImageResource(R.drawable.ic_location_disabled_24dp);
        } else {
            fab.setImageResource(R.drawable.ic_my_location_24dp);
        }
        //新增或移除定點陣圖層
        mapboxMap.setMyLocationEnabled(enable);
    }

這個就比較長了,慢慢看
//獲取上次定位引數,如果存在先直接使用
            final Location lastlocation = locationEngine.getLastLocation();
            if (lastlocation != null) {
                mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(lastlocation), 15), 1000);
            }

這個呢就是獲取上次定位時儲存好的座標位置,先給地圖一個定位座標,讓地圖先定過去再說。這個的用意,用過其他地圖的應該都知道。
locationEngineListener = new LocationEngineListener() {
                @Override
                public void onConnected() {
                    //連線到定位服務,不需要操作
                }

                @Override
                public void onLocationChanged(Location location) {
                    if (location != null) {
                        mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(location), 15), 1000);
                        locationEngine.removeLocationEngineListener(this);
                    }
                }
            };
            //設定監聽器
            locationEngine.addLocationEngineListener(locationEngineListener);
然後就初始化一個監聽器,並設定給定位引擎,我們可以看到,在得到定位的時候,就將地圖定了過去。
//新增或移除定點陣圖層
        mapboxMap.setMyLocationEnabled(enable);
這個就簡單啦,就是是否要顯示定位的那個標記的

fab.setImageResource(R.drawable.ic_location_disabled_24dp);
fab.setImageResource(R.drawable.ic_my_location_24dp);
裡面這兩句是設定按鈕的背景的,不細說。 另外,還要注意幾個生命週期的重寫
@Override
    protected void onStart() {
        super.onStart();
        mapView.onStart();
        if (locationEngine != null && locationEngineListener != null) {
            locationEngine.activate();
            locationEngine.requestLocationUpdates();
            locationEngine.addLocationEngineListener(locationEngineListener);
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        mapView.onStop();
        if (locationEngine != null && locationEngineListener != null) {
            locationEngine.removeLocationEngineListener(locationEngineListener);
            locationEngine.removeLocationUpdates();
            locationEngine.deactivate();
        }
    }
@Override
    protected void onDestroy() {
        super.onDestroy();
        mapView.onDestroy();
        // Ensure no memory leak occurs if we register the location listener but the call hasn't
        // been made yet.
        if (locationEngineListener != null) {
            locationEngine.removeLocationEngineListener(locationEngineListener);
        }
    }
大意就是在應用重新進入的時候啟用定位,設定定位監聽 在停止的時候移除監聽並取消啟用 其實吧,這些吧,寫不寫都行,不過為了系統性能,可以寫上。
設定定點陣圖標的屬性

設定定點陣圖標的屬性需要獲取到一個MyLocationViewSetting的值

MyLocationViewSettings locationSettings = mapboxMap.getMyLocationViewSettings();
                locationSettings.setBackgroundDrawable(ContextCompat.getDrawable(LocationActivity.this, R.drawable.ic_my_location_24dp), new int[]{20, 20, 20, 20});
                locationSettings.setForegroundTintColor(ContextCompat.getColor(this, R.color.seagreen));
                locationSettings.setAccuracyTintColor(ContextCompat.getColor(this,R.color.brown));
                locationSettings.setAccuracyAlpha(50);
                locationSettings.setTilt(30);

如上,獲取到後,可以設定定點陣圖標的各種屬性,第一個是圖片不再多說

setForegroundTintColor,這個是用於設定定點陣圖標中心圓的顏色

setAccuracyTintColor,這個是用於設定定點陣圖標範圍框的顏色

setAccuracyAlpha,設定範圍框的透明度

setTilt,設定傾斜角,上面用過這個引數

注意,設定drawable同時還設定了padding


地圖跟隨

地圖跟隨,就是tracking,如下

TrackingSettings trackingSettings = mapboxMap.getTrackingSettings();
                // 讓地圖始終以定位點為中心,無法滑動
                trackingSettings.setDismissAllTrackingOnGesture(false);
                // 啟用位置和方位跟蹤
                trackingSettings.setMyLocationTrackingMode(MyLocationTracking.TRACKING_FOLLOW);
                trackingSettings.setMyBearingTrackingMode(MyBearingTracking.COMPASS);

獲取到一個trackingSetting物件,這個物件可用於設定地圖跟隨的相關屬性

首先是setDissmissAllTrackingOnGesture(false),就像我註釋說的,是將地圖的中心點就固定了,無法移動,只能放大縮小

然後是setMyLocationTrackingMode,這個是設定地圖位置模式,TRACKING_FOLLOW就是跟隨的意思,就是說如果自身位置移動了,地圖中心點也會跟著變化的意思

最後是setMyBearingTrackingMode,這個是設定方位的跟蹤模式,有COMPASS,GPS等,值得注意的是,不知道是不是我在室內的原因,這方向箭頭只能指向兩端兩個方位,轉動手機到某個位置就直接旋轉180度,而不是隨著手機轉動而慢慢轉動角度,我感覺是室內,因為我這gps一向不咋地,畢竟總覺得要是這就是mapbox自帶的,也太坑爹了。


相關推薦

Android開發MapBox的使用部分功能實現----- 初始標記定位styleurl

近期,應公司要求,開始接觸MapBox For Android的開發。 經過初步的接觸,發現MapBox與我之前使用的Arcgis有很多不同,相比起來,MapBox更清潔,更輕便,也更容易使用,但是相對的,MapBox相對於Arcgis缺少了很多的功能實現,許多的東西都需要

Android開發之一個簡單的通訊錄實現原始碼

通訊錄就是一個ListView,我們需要通過資料庫和ContentProvider來活動通訊錄的資料,當然,我們應該提供選中後編輯的功能。 很簡單的一個通訊略Demo,所以,直接上程式碼,需要的看一下就知道。不解釋。 檔案1: MyContacs 主活動頁面。 packag

Android 基於Zxing的掃碼功能實現

本篇文章已授權微信公眾號 guolin_blog (郭霖)獨家釋出 引言 本篇博文是基於 Android 二維碼的掃碼功能實現(一) 文章寫的,建議閱讀這篇文章之前,先看看上篇文章。還有建議閱讀本文的同學,結合zxing的原始碼理解。 上篇部落格說明z

【視頻】零基礎學Android開發:藍牙聊天室APP

android入門 mod http 開發 org 薪資 get target 下載 零基礎學Android開發:藍牙聊天室APP第一講 1. Android介紹與環境搭建:史上最高效Android入門學習 1.1 Google的大小戰略 1.2 物聯網與雲計算 1.3

Android開發技巧之:QQ第三方登入

使用的是Android_SDK_V2.9.1,建議使用最新版; 官方下載:SDK下載  Android studio 中新增到 然後在點選build.gradle檔案新增 配置AndroidManifest 在應用的Andr

Android開發人員不得不學習的JavaScript基礎

操作符 在JavaScript中,有很多種操作符,算術操作符、賦值操作符、比較操作符以及邏輯操作符 1.1、算術操作符: +,-,*,/,除了加號(+)之外,其他都是按照四則運算大方式來進行,而加號(+)在字串中可以作為連線符來使用,這個和Java是一

android中的跨程序通訊的實現——遠端呼叫過程和aidl

android在設計理念上強調元件化,元件之間的依賴性很小。我們往往發一個intent請求就可以啟動另一個應用的activity,或者一個你不知道在哪個程序的service,或者可以註冊一個廣播,只要有這個事件發生你都可以收到,又或者你可以查詢一個contentProvider獲得你想要的資料,這其

床頭筆記之Android開發番外篇報錯解決

已有專案時新建專案執行報錯 your project contains error(s),please fix them before running your application 工程上有紅叉,不知道少了什麼,但是工程中卻沒有任何錯誤,執行程式報錯為: Y

基於CANoe的OSEK_TP封裝的診斷刷寫FOTA自動化模擬測試實現

  原創內容,如若喜歡,轉載時請在開篇處註明出處   車輛網領域有個關鍵ECU——TBOX,本文圍繞TBOX的FOTA升級業務展開。主要講如何通過CANoe的模擬程式實現自動化測試, 驗證TBOX在FOTA業務過程中作為一個診斷儀刷寫整車其它ECU的流程以及業務邏輯處理的正確性。通常情況下,主機廠

Android Studio中配置使用OpenCV示例

Android Studio配置及使用OpenCV前言:最近在做專案移植,專案較大,在Eclipse中配置的Jni及OpenCV環境沒任何問題,但是遷移到Studio中就問題一大堆,網上也找了一些資料參考及學習,很感謝前人留下的總結及經驗。關於在AS中使用jni及配置Open

balance transfer程式碼解析api深度追蹤初始鏈碼

一程式碼解析 var path = require(‘path’); var fs = require(‘fs’); var util = require(‘util’); var hfc = require(‘fabric-client’); var Peer

Android intent.Action 引數值對應功能介紹

4 Intent.Action.ALL_APPS String: andriod.intent.action.ALL_APPS 列出所有的應用。 Input:Nothing. Output:Nothing. 5 Intent.ACTION_ANSWER Stirng:android.intent.action

Shiro實現: SSM整合筆記實現登入授權功能

開篇 本專案已經上傳github,建議對照程式碼理解 本篇主要講Shiro框架與SSM框架結合,實現登入和授權功能 利用spring 的aop切面思想,很簡單得融合Shiro許可權框架 程式碼 需要明白兩個點: 通過Subject.login() 登入成

實現自定義查詢的數據庫設計實現

bre 名稱 審批流程 work 數據庫名 需要 自定義查詢 perm 枚舉 需求 先說一下需求:實現用戶自定義的查詢,用戶可以自定義要查詢的列、自定義條件條件、自定義排序。除了查詢使用外,還可以使用於各個需要根據條件進行約束的業務,如權限; 本設計和實現,很大部分是通過數

Android開發 adb命令提示:Permission denied

模擬 ont lang rmi title fontsize fcm hbm ssi 如題:模擬器版本->android 7.1.1 遇到這樣的情況把模擬器root一下就好了:su root =============2017年4月3日20:57:33========

基於樹莓派Raspberry Pi平臺的智能家居實現----繼電器模塊DHT11模塊

Raspberry 繼電器模塊 DHT11溫濕度模塊 智能家居 前言:    ??其實做這個智能家居系統我還是因為學校的畢業設計,距離上篇文章發布已經過去了20多天了,之前想著只是做一個煙霧報警,然後通過Zabbix進行報警,但是通過這20多天的設計,我發現實現報警的功能其

Android項目實戰十六:QQ空間實現—— 展示說說中的評論內容並有相應點擊事件

con toast short demo append 集合 obj parent 自帶 原文:Android項目實戰(十六):QQ空間實現(一)—— 展示說說中的評論內容並有相應點擊事件大家都玩QQ空間客戶端,對於每一個說說,我們都可以評論,那麽,對於某一條評論:

KVM虛擬化的四種簡單網絡模型介紹實現

_for only 應該 code eth tun x86_64 信息 dock KVM中的四種簡單網絡模型,分別如下:1、隔離模型:虛擬機之間組建網絡,該模式無法與宿主機通信,無法與其他網絡通信,相當於虛擬機只是連接到一臺交換機上。2、路由模型:相當於虛擬機連接到一臺路由

【轉】Verilog學習筆記簡單功能實現...............異步FIFO

另一個 gif 多個 可靠 基本原理 drs bar next 不同 基本原理: 1.讀寫指針的工作原理   寫指針:總是指向下一個將要被寫入的單元,復位時,指向第1個單元(編號為0)。   讀指針:總是指向當前要被讀出的數據,復位時,指向第1個單元(編號為0)

軟件工程—WC功能實現 JAVA

要求 目錄 arsc 主類 read 準備 dsc row 源文件 軟件工程—WC功能實現(JAVA) Github項目地址:https://github.com/Ousyoung/wc 項目要求 ? wc.exe 是一個常見的工具,它能統計文本文件的字符數、單詞數和行數。