美文网首页Android收藏集
Android GoogleMap不完全指南 (二)

Android GoogleMap不完全指南 (二)

作者: 阿敏其人 | 来源:发表于2017-02-09 21:09 被阅读4294次

    Android Google Map/谷歌地图 接入不完全指南 (一)已良好运行起官方demo和说明注意事项。

    一、GoogleMap

    谷歌地图使用起来大概分两种方式
    1、以fragment的方式使用
    2、以控件的方式,也就是原生的GoogleMap

    发现,现在google家的地图现在已经有比较全的官方中文文档啦,官方文档是个好东西,我这里小文,只是根据其衍生的出来的东西,毕竟“曾见郭象注庄子,却是庄子注郭象”嘛。
    原著就是叼。

    一.1 以fragment的方式使用googleMap

    亦可参见源例

    image.png

    .

    示例代码
    SimpleFragmentModeActivity

    public class SimpleFragmentModeActivity extends AppCompatActivity implements OnMapReadyCallback {
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_fragment_mode);
    
            SupportMapFragment mapFragment =
                    (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
            mapFragment.getMapAsync(this);
        }
    
        @Override
        public void onMapReady(GoogleMap googleMap) {
    
            double lat = 0.0;
            double lng = 0.0;
            LatLng appointLoc = new LatLng(lat, lng);
    
            // 移动地图到指定经度的位置
            googleMap.moveCamera(CameraUpdateFactory.newLatLng(appointLoc));
    
            //添加标记到指定经纬度
            googleMap.addMarker(new MarkerOptions().position(new LatLng(lat, lng)).title("Marker")
                    .icon(BitmapDescriptorFactory.fromResource(R.drawable.chat_loc_point)));
    
            /*// 点击标记点,默认点击弹出跳转google地图或导航选择,返回true则不弹出
            googleMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
                @Override
                public boolean onMarkerClick(Marker marker) {
                    return true;
                }
            });
    
            // 单击
            googleMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
                @Override
                public void onMapClick(LatLng latLng) {
    
                }
            });
    
            // 不允许手势缩放
            googleMap.getUiSettings().setZoomGesturesEnabled(false);
            //googleMap.getUiSettings().setMapToolbarEnabled(false); 禁用精简模式下右下角的工具栏
    
            // 不允许拖动地图
            googleMap.getUiSettings().setScrollGesturesEnabled(false);
    
            // 设置缩放级别
            CameraUpdate zoom = CameraUpdateFactory.zoomTo(13);
            googleMap.animateCamera(zoom);*/
        }
    }
    

    .
    .
    对应布局

    <?xml version="1.0" encoding="utf-8"?>
    <fragment xmlns:android="http://schemas.android.com/apk/res/android"
              android:id="@+id/map"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              class="com.google.android.gms.maps.SupportMapFragment" />
    

    .
    .
    使用步骤

    • 第一步
      布局为文件放上fragment,注意class="com.google.android.gms.maps.SupportMapFragment"不可或缺

    • 第二步
      得到SupportMapFragment对象,调用getMapAsync异步读取地图

    • 第三步,在getMapAsync的OnMapReadyCallback接口中的onMapReady方法中指定经纬度和marker。
      .
      默认效果

    默认效果
    1、可双指缩放
    2、可单指移动
    3、带有指南针的,
    4、maker点是点击的,点击后弹出toolbar(此toolbar是地图的toobar,即跳转googlemap app或者导航)
    5、双击放大

    我们可以获取UiSettings或者利用googleMap对象直接设置。
    代码的备注里面我也简单列举了一些,可以参见官网文档进行调整。

    如果不需要这些东西,就要特别简单的地图,那么我们可以使用精简模式。

    精简模式

    如果我们不想要默认的效果,那么我们完全可以使用精简模式。

    • 使用方式

    1、fragment布局代码中 map:liteMode="true"
    2、java代码里面配置 GoogleMapOptions options = new GoogleMapOptions().liteMode(true);

    image.png
    • 精简模式与默认的地图toolbar
      我们使用精简模式之后,右下角的地图的Toolbar自己就跑出来了,我们可以通过UiSettings().setMapToolbarEnabled(false);把Toolbar禁掉,就不会出现了。

    如果你说为什么使用精简模式之后地图不是居中了,那时因为缩放关系导致的,你只要把缩放级别调整一下就好

            // 设置缩放级别
            CameraUpdate zoom = CameraUpdateFactory.zoomTo(13);
            googleMap.animateCamera(zoom);
    

    zoomTo(float),因为默认的太小,看到是世界地图,只要放大到可以铺满屏幕,大概3-5之间吧,就不会出现上下空白仅有中间的情况。

    嗯,fragment大概到这里。

    一.2 MapView

    亦可参见源例
    .
    .
    MapView

    MapView 是 Android View 类的一个子类, 用于在 Android View 中放置地图。 View 表示屏幕的某个矩形区域, 是 Android 应用和小工具的基本构建基块。 MapView 与 MapFragment 很相似,它也充当地图容器,通过 GoogleMap 对象公开核心地图功能。

    在完全交互模式下使用该 API 时,MapView 类的用户必须将下列 Activity 生命周期方法转发给 MapView 类中的相应方法:onCreate()、onStart()、onResume()、onPause()、onStop()、onDestroy()、onSaveInstanceState() 和 onLowMemory()。

    例子

    image.png

    .
    SimpleMapActivity

    public class SimpleMapActivity extends AppCompatActivity implements OnMapReadyCallback {
    
        private MapView mMapView;
    
        private static final String MAPVIEW_BUNDLE_KEY = "MapViewBundleKey";
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_raw_map);
    
            // *** IMPORTANT ***
            // MapView requires that the Bundle you pass contain _ONLY_ MapView SDK
            // objects or sub-Bundles.
            Bundle mapViewBundle = null;
            if (savedInstanceState != null) {
                mapViewBundle = savedInstanceState.getBundle(MAPVIEW_BUNDLE_KEY);
            }
            mMapView = (MapView) findViewById(R.id.mMapView);
            mMapView.onCreate(mapViewBundle);
    
            mMapView.getMapAsync(this);
        }
    
        @Override
        public void onMapReady(GoogleMap map) {
            double lat = 39.937795;
            double lng = 116.387224;
            LatLng appointLoc = new LatLng(lat, lng);
    
            map.addMarker(new MarkerOptions().position(appointLoc).title("Marker"));
            map.moveCamera(CameraUpdateFactory.newLatLng(appointLoc));
        }
    
        @Override
        public void onSaveInstanceState(Bundle outState) {
            super.onSaveInstanceState(outState);
    
            Bundle mapViewBundle = outState.getBundle(MAPVIEW_BUNDLE_KEY);
            if (mapViewBundle == null) {
                mapViewBundle = new Bundle();
                outState.putBundle(MAPVIEW_BUNDLE_KEY, mapViewBundle);
            }
    
            mMapView.onSaveInstanceState(mapViewBundle);
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            mMapView.onResume();
        }
    
        @Override
        protected void onStart() {
            super.onStart();
            mMapView.onStart();
        }
    
        @Override
        protected void onStop() {
            super.onStop();
            mMapView.onStop();
        }
    
    
    
        @Override
        protected void onPause() {
            mMapView.onPause();
            super.onPause();
        }
    
        @Override
        protected void onDestroy() {
            mMapView.onDestroy();
            super.onDestroy();
        }
    
        @Override
        public void onLowMemory() {
            super.onLowMemory();
            mMapView.onLowMemory();
        }
    }
    

    .
    .

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:orientation="vertical"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent">
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Raw Map"
            android:textSize="30sp"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="20dp"
            />
    
        <com.google.android.gms.maps.MapView
            android:id="@+id/mMapView"
            android:layout_width="200dp"
            android:layout_height="150dp"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="20dp"
            android:background="#66ff0000"
            >
        </com.google.android.gms.maps.MapView>
    </LinearLayout>
    

    .
    .
    MapView的大概就到这里,声明周期对于MapView很重要。


    二、自动获取经纬度和逆地理编码

    定位,一种是官方的My Location,主要使用方式是 mMap.setMyLocationEnabled(true);
    My Location官方文档 位置数据
    My Location demo

    但是在这种方式只是地图层面地获取“我的位置”,My Location 层不会返回任何数据,这往往不是我们要的,所以这里略过,想了解可以看上面的两个链接。


    FusedLocationProviderApi

    编码获取位置现在比较好的方式是使用 [FusedLocationProviderApi ],因为它提供了比旧式的LocationManager API更加方便更加省电的方式。另外,如果你已经在谷歌地图上使用谷歌Play服务,就没有理由不使用它了。

    使用FusedLocationProviderApi,就需要在gradle引入play-services,比如

       compile 'com.google.android.gms:play-services:10.2.6'
    

    而引入play-services,可能报multiDex
    那么我们需要配置一下主mudule的gradle

    image.png
    // 添加之
    multiDexEnabled true
    
    // 添加之
    dexOptions {
        incremental true
        javaMaxHeapSize "4g"
    }
    

    一般即可解决。

    接下来我们来写一个demo,功能是
    进入页面自动获取当前经纬度,拿到经纬度之后进行逆地理编码,得到地址

    获取当前位置经纬度

    我们要利用FusedLocationApi获取当前位置,其实核心就是调用下面这几行代码,

    mGoogleApiClient = new GoogleApiClient.Builder(this)
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this)
                    .addApi(LocationServices.API)
                    .build();
    

    addConnectionCallbacks

    而这个build的过程最主要的就是addConnectionCallbacks方法,传入的GoogleApiClient.ConnectionCallbacks接口要求我们实现如下两个方法

    • 1、onConnected 异步进行连接,获取位置 (关键方法)
    • 2、onConnectionSuspended 当客户端断开
      .
      .

    addOnConnectionFailedListener

    其他的,addOnConnectionFailedListener传入的GoogleApiClient.OnConnectionFailedListener实现的onConnectionFailed方法当然是连接失败时调用,这点没其他特别的。

    gif

    myloc.gif
    public class SimpleMyLocActivity extends AppCompatActivity implements
    
            OnMapReadyCallback, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
    
        LocationRequest mLocationRequest;
        GoogleApiClient mGoogleApiClient;
    
        LatLng latLng;
        GoogleMap mGoogleMap;
        SupportMapFragment mMapFragment;
        Marker mCurrLocation;
    
        private static final int REQUESTCODE = 6001;
    
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_fragment_loc);
    
            mMapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
            mMapFragment.getMapAsync(this);
    
        }
    
        @Override
        public void onMapReady(GoogleMap googleMap) {
            mGoogleMap = googleMap;
            // 允许获取我的位置
            try {
                mGoogleMap.setMyLocationEnabled(true);
            } catch (SecurityException e) {
                e.printStackTrace();
            }
            buildGoogleApiClient();
            mGoogleApiClient.connect();
        }
    
        @Override
        public void onPause() {
            super.onPause();
            //Unregister for location callbacks:
            if (mGoogleApiClient != null) {
                LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, new LocationCallback() {
                    @Override
                    public void onLocationResult(LocationResult locationResult) {
                        super.onLocationResult(locationResult);
                    }
                });
            }
        }
    
        protected synchronized void buildGoogleApiClient() {
            Toast.makeText(this, "buildGoogleApiClient", Toast.LENGTH_SHORT).show();
            mGoogleApiClient = new GoogleApiClient.Builder(this)
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this)
                    .addApi(LocationServices.API)
                    .build();
        }
    
        @Override
        public void onConnected(Bundle bundle) {
            Toast.makeText(this, "onConnected", Toast.LENGTH_SHORT).show();
            Location mLastLocation = null;
            try {
                Log.i("位置", LocationServices.FusedLocationApi.getLocationAvailability(mGoogleApiClient) + "");
                mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
            } catch (SecurityException e) {
                e.printStackTrace();
            }
    
    
            if (mLastLocation != null) {
                //place marker at current position
                mGoogleMap.clear();
                latLng = new LatLng(mLastLocation.getLatitude(), mLastLocation.getLongitude());
                MarkerOptions markerOptions = new MarkerOptions();
                markerOptions.position(latLng);
                markerOptions.title("Current Position");
                mGoogleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(mLastLocation.getLatitude(), mLastLocation.getLongitude()), 15));
                // 添加marker,但是这里我们特意把marker弄成透明的
                markerOptions.icon(BitmapDescriptorFactory.fromResource(R.drawable.chat_loc_point));
    
                mCurrLocation = mGoogleMap.addMarker(markerOptions);
    
                Log.i("位置", mLastLocation + "1111111");
                Log.i("位置", "最新的位置 getProvider " + mLastLocation.getProvider());
                Log.i("位置", "最新的位置 getAccuracy " + mLastLocation.getAccuracy());
                Log.i("位置", "最新的位置 getAltitude " + mLastLocation.getAltitude());
                Log.i("位置", "最新的位置 Bearing() " + mLastLocation.getBearing());
                Log.i("位置", "最新的位置 Extras() " + mLastLocation.getExtras());
                Log.i("位置", "最新的位置 Latitude() " + mLastLocation.getLatitude());
                Log.i("位置", "最新的位置 Longitude()() " + mLastLocation.getLongitude());
                Log.i("位置", " =============  ");
                TextView mTvAddress = (TextView) findViewById(R.id.mTvAddress);
                String address = getAddress(SimpleMyLocActivity.this, mLastLocation.getLatitude(), mLastLocation.getLongitude());
                mTvAddress.setText(address);
            }
            mLocationRequest = LocationRequest.create();
            mLocationRequest.setInterval(5000); //5 seconds
            mLocationRequest.setFastestInterval(3000); //3 seconds
            mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
            //mLocationRequest.setSmallestDisplacement(0.1F); //1/10 meter
    
            //LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
            LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, new LocationCallback() {
                @Override
                public void onLocationResult(LocationResult locationResult) {
                    super.onLocationResult(locationResult);
                }
            });
        }
    
        @Override
        public void onConnectionSuspended(int i) {
            Toast.makeText(this, "onConnectionSuspended", Toast.LENGTH_SHORT).show();
        }
    
        @Override
        public void onConnectionFailed(ConnectionResult connectionResult) {
            Toast.makeText(this, "onConnectionFailed", Toast.LENGTH_SHORT).show();
        }
    
    
        /**
         * 逆地理编码 得到地址
         * @param context
         * @param latitude
         * @param longitude
         * @return
         */
        public static String getAddress(Context context, double latitude, double longitude) {
            Geocoder geocoder = new Geocoder(context, Locale.getDefault());
            try {
                List<android.location.Address> address = geocoder.getFromLocation(latitude, longitude, 1);
                Log.i("位置", "得到位置当前" + address + "'\n"
                        + "经度:" + String.valueOf(address.get(0).getLongitude()) + "\n"
                        + "纬度:" + String.valueOf(address.get(0).getLatitude()) + "\n"
                        + "纬度:" + "国家:" + address.get(0).getCountryName() + "\n"
                        + "城市:" + address.get(0).getLocality() + "\n"
                        + "名称:" + address.get(0).getAddressLine(1) + "\n"
                        + "街道:" + address.get(0).getAddressLine(0)
                );
                return address.get(0).getAddressLine(0) + "  " + address.get(0).getLocality() + " " + address.get(0).getCountryName();
            } catch (Exception e) {
                e.printStackTrace();
                return "未知";
            }
        }
    
        @Override
        public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
            switch (requestCode) {
                case REQUESTCODE: {
                    if (grantResults.length > 0
                            && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
    
                        buildGoogleApiClient();
                        mGoogleApiClient.connect();
                    } else {
                    }
                    return;
                }
            }
        }
    }
    

    .
    .
    .
    布局

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  xmlns:map="http://schemas.android.com/apk/res-auto"
                  xmlns:tools="http://schemas.android.com/tools"
                  android:orientation="vertical"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent">
    
        <RelativeLayout
            android:layout_height="match_parent"
            android:layout_width="match_parent"
            android:layout_weight="1">
            <fragment xmlns:android="http://schemas.android.com/apk/res/android"
                      xmlns:tools="http://schemas.android.com/tools"
                      android:id="@+id/map"
                      android:name="com.google.android.gms.maps.SupportMapFragment"
                      android:layout_width="match_parent"
                      android:layout_height="match_parent"
                      tools:context="com.gmapdemo2.googlemapdemosimple.MapsActivity"
                      android:layout_alignParentLeft="true"
                      android:layout_alignParentStart="true" >
            </fragment>
            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerHorizontal="true"
                android:layout_centerInParent="true"
                android:layout_centerVertical="true"
                android:src="@drawable/poi_marker_pressed"/>
        </RelativeLayout>
        
        <TextView
            android:id="@+id/mTvAddress"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="20dp"
            tools:text="123123"
            android:background="#ffffff"
            android:gravity="center"
            android:textColor="#0000ff"
            android:paddingTop="20dp"
            />
    </LinearLayout>
    

    最后把流程画一下

    GoogleMap地理编码与逆地理编码.png

    核心代码在此,根据上面的代码,即可实现自动定位,获取经纬度并且得到地址位置信息。
    当前地点为东南亚,经测试,安卓5.0,6.0和7.1均可良好运行,其他国家地区应该也可以。大天朝除外。

    二、小细节

    1、在google控制台创建项目默认是不开启包括地图在内的任何api的,请开启对应功能,不然无法显示地图,开启方法请看Android Google Map/谷歌地图 接入不完全指南 (一)

    2、android API23 以上请做好权限申请工作,避免因为权限问题导致的闪退。

    .

    本篇大概多少到这里,下一遍是关于列表的
    Android GoogleMap不完全指南 (三)

    本文完。

    三、链接

    小demo git链接

    相关文章

      网友评论

        本文标题:Android GoogleMap不完全指南 (二)

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