zoukankan      html  css  js  c++  java
  • Android中使用GoogleMap的地理位置服务

    写在前面:android中使用地理位置功能,可以借助Google给我们提供的框架,要是有地理位置功能,你需要引用Google Play Services,请在sdk manager中下载。
    如果你还要使用地图功能,请到google官网申请api-key

    如果要看官方例子可以到https://github.com/googlesamples/android-play-location.git下载

    使用Google地理服务都需要如下,

    引用Google Play Services

    <meta-data android:name="com.google.android.gms.version"
        android:value="@integer/google_play_services_version" />

    声明权限

    定位功能涉及到权限,下面2选1;

    ACCESS_COARSE_LOCATION 粗略位置
    或者ACCESS_FINE_LOCATION精确位置

    1.基本使用,获取上一次的位置。

       /**
         * 构建位置服务客户端对象
         */
        protected synchronized void buildGoogleApiClient() {
            mGoogleApiClient = new GoogleApiClient.Builder(this)
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this)
                    .addApi(LocationServices.API)
                    .build();
        }
    
        /**
         *在onStart中使用客户端连接GooglePlay Service
         */
        @Override
        protected void onStart() {
            super.onStart();
            mGoogleApiClient.connect();
        }
    
        /**
         * 离开界面时,断开连接
         */
        @Override
        protected void onStop() {
            super.onStop();
            if (mGoogleApiClient.isConnected()) {
                mGoogleApiClient.disconnect();
            }
        }
    
        /**
         * 连接GooglePlay Service成功后,可以在回调方法中获取上一次的位置
         */
        @Override
        public void onConnected(Bundle connectionHint) {
            // Provides a simple way of getting a device's location and is well suited for
            // applications that do not require a fine-grained location and that do not need location
            // updates. Gets the best and most recent location currently available, which may be null
            // in rare cases when a location is not available.
            mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
            if (mLastLocation != null) {
                mLatitudeText.setText(String.format("%s: %f", mLatitudeLabel,
                        mLastLocation.getLatitude()));
                mLongitudeText.setText(String.format("%s: %f", mLongitudeLabel,
                        mLastLocation.getLongitude()));
            } else {
                Toast.makeText(this, R.string.no_location_detected, Toast.LENGTH_LONG).show();
            }
        }
    
        /**
         * 连接失败
         * @param result
         */
        @Override
        public void onConnectionFailed(ConnectionResult result) {
            // Refer to the javadoc for ConnectionResult to see what error codes might be returned in
            // onConnectionFailed.
            Log.i(TAG, "Connection failed: ConnectionResult.getErrorCode() = " + result.getErrorCode());
        }
    
    
        /**
         * 连接被暂停了,重新连接
         * @param cause
         */
        @Override
        public void onConnectionSuspended(int cause) {
            // The connection to Google Play services was lost for some reason. We call connect() to
            // attempt to re-establish the connection.
            Log.i(TAG, "Connection suspended");
            mGoogleApiClient.connect();
        }

    2.实时刷新最新位置,获取最新位置信息; 

    连接成功后,在回调方法中执行位置监听;首先配置位置请求参数,创建位置请求对象,后调用监听。

       /**
         * The desired interval for location updates. Inexact. Updates may be more or less frequent.
         */
        public static final long UPDATE_INTERVAL_IN_MILLISECONDS = 10000;
    
        /**
         * The fastest rate for active location updates. Exact. Updates will never be more frequent
         * than this value.
         */
        public static final long FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS =
                UPDATE_INTERVAL_IN_MILLISECONDS / 2;
    
        /**
         * 创建位置请求对象mLocationRequest,封装监听参数
         */
        protected void createLocationRequest() {
            mLocationRequest = new LocationRequest();
    
            // Sets the desired interval for active location updates. This interval is
            // inexact. You may not receive updates at all if no location sources are available, or
            // you may receive them slower than requested. You may also receive updates faster than
            // requested if other applications are requesting location at a faster interval.
            mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
    
            // Sets the fastest rate for active location updates. This interval is exact, and your
            // application will never receive updates faster than this value.
            mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS);
    
            mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        }
        /**
         * 开始监听位置变化
         */
        protected void startLocationUpdates() {
            // The final argument to {@code requestLocationUpdates()} is a LocationListener
            // (http://developer.android.com/reference/com/google/android/gms/location/LocationListener.html).
            LocationServices.FusedLocationApi.requestLocationUpdates(
                    mGoogleApiClient, mLocationRequest, this);
        }
        /**
         * 回调方法中获取到改变的最新位置
         */
        @Override
        public void onLocationChanged(Location location) {
            mCurrentLocation = location;
            mLastUpdateTime = DateFormat.getTimeInstance().format(new Date());
            updateUI();
            Toast.makeText(this, getResources().getString(R.string.location_updated_message),
                    Toast.LENGTH_SHORT).show();
        }

    3.获取到位置的经纬度后,可以将其转为地址信息。

    这里使用开启服务来解析经纬度。

           if (mLastLocation != null) {
                // Determine whether a Geocoder is available.
                if (!Geocoder.isPresent()) {
                    Toast.makeText(this, R.string.no_geocoder_available, Toast.LENGTH_LONG).show();
                    return;
                }
                // It is possible that the user presses the button to get the address before the
                // GoogleApiClient object successfully connects. In such a case, mAddressRequested
                // is set to true, but no attempt is made to fetch the address (see
                // fetchAddressButtonHandler()) . Instead, we start the intent service here if the
                // user has requested an address, since we now have a connection to GoogleApiClient.
                if (mAddressRequested) {
                    startIntentService(); //开启服务解析经纬度
                }
            }

    使用IntentService

    public class FetchAddressIntentService extends IntentService {
        private static final String TAG = "FetchAddressIS";
    
        /**
         * The receiver where results are forwarded from this service.
         */
        protected ResultReceiver mReceiver;
    
        /**
         * This constructor is required, and calls the super IntentService(String)
         * constructor with the name for a worker thread.
         */
        public FetchAddressIntentService() {
            // Use the TAG to name the worker thread.
            super(TAG);
        }
    
        /**
         * Tries to get the location address using a Geocoder. If successful, sends an address to a
         * result receiver. If unsuccessful, sends an error message instead.
         * Note: We define a {@link android.os.ResultReceiver} in * MainActivity to process content
         * sent from this service.
         *
         * This service calls this method from the default worker thread with the intent that started
         * the service. When this method returns, the service automatically stops.
         */
        @Override
        protected void onHandleIntent(Intent intent) {
            String errorMessage = "";
    
            mReceiver = intent.getParcelableExtra(Constants.RECEIVER);
    
            // Check if receiver was properly registered.
            if (mReceiver == null) {
                Log.wtf(TAG, "No receiver received. There is nowhere to send the results.");
                return;
            }
    
            // Get the location passed to this service through an extra.
            Location location = intent.getParcelableExtra(Constants.LOCATION_DATA_EXTRA);
    
            // Make sure that the location data was really sent over through an extra. If it wasn't,
            // send an error error message and return.
            if (location == null) {
                errorMessage = getString(R.string.no_location_data_provided);
                Log.wtf(TAG, errorMessage);
                deliverResultToReceiver(Constants.FAILURE_RESULT, errorMessage);
                return;
            }
    
            // Errors could still arise from using the Geocoder (for example, if there is no
            // connectivity, or if the Geocoder is given illegal location data). Or, the Geocoder may
            // simply not have an address for a location. In all these cases, we communicate with the
            // receiver using a resultCode indicating failure. If an address is found, we use a
            // resultCode indicating success.
    
            // The Geocoder used in this sample. The Geocoder's responses are localized for the given
            // Locale, which represents a specific geographical or linguistic region. Locales are used
            // to alter the presentation of information such as numbers or dates to suit the conventions
            // in the region they describe.
            Geocoder geocoder = new Geocoder(this, Locale.getDefault());
    
            // Address found using the Geocoder.
            List<Address> addresses = null;
    
            try {
                // Using getFromLocation() returns an array of Addresses for the area immediately
                // surrounding the given latitude and longitude. The results are a best guess and are
                // not guaranteed to be accurate.
                addresses = geocoder.getFromLocation(
                        location.getLatitude(),
                        location.getLongitude(),
                        // In this sample, we get just a single address.
                        1);
            } catch (IOException ioException) {
                // Catch network or other I/O problems.
                errorMessage = getString(R.string.service_not_available);
                Log.e(TAG, errorMessage, ioException);
            } catch (IllegalArgumentException illegalArgumentException) {
                // Catch invalid latitude or longitude values.
                errorMessage = getString(R.string.invalid_lat_long_used);
                Log.e(TAG, errorMessage + ". " +
                        "Latitude = " + location.getLatitude() +
                        ", Longitude = " + location.getLongitude(), illegalArgumentException);
            }
    
            // Handle case where no address was found.
            if (addresses == null || addresses.size()  == 0) {
                if (errorMessage.isEmpty()) {
                    errorMessage = getString(R.string.no_address_found);
                    Log.e(TAG, errorMessage);
                }
                deliverResultToReceiver(Constants.FAILURE_RESULT, errorMessage);
            } else {
                Address address = addresses.get(0);
                ArrayList<String> addressFragments = new ArrayList<String>();
    
                // Fetch the address lines using {@code getAddressLine},
                // join them, and send them to the thread. The {@link android.location.address}
                // class provides other options for fetching address details that you may prefer
                // to use. Here are some examples:
                // getLocality() ("Mountain View", for example)
                // getAdminArea() ("CA", for example)
                // getPostalCode() ("94043", for example)
                // getCountryCode() ("US", for example)
                // getCountryName() ("United States", for example)
                for(int i = 0; i < address.getMaxAddressLineIndex(); i++) {
                    addressFragments.add(address.getAddressLine(i));
                }
                Log.i(TAG, getString(R.string.address_found));
                deliverResultToReceiver(Constants.SUCCESS_RESULT,
                        TextUtils.join(System.getProperty("line.separator"), addressFragments));
            }
        }
    
        /**
         * Sends a resultCode and message to the receiver.
         */
        private void deliverResultToReceiver(int resultCode, String message) {
            Bundle bundle = new Bundle();
            bundle.putString(Constants.RESULT_DATA_KEY, message);
            mReceiver.send(resultCode, bundle);
        }
    }

    将服务声明下

           <service
                android:name=".FetchAddressIntentService"
                android:exported="false"/>

    4.使用地理围栏,在进入和退出围栏时有提醒。

        /**
         * Adds geofences, which sets alerts to be notified when the device enters or exits one of the
         * specified geofences. Handles the success or failure results returned by addGeofences().
         */
        public void addGeofencesButtonHandler(View view) {
            if (!mGoogleApiClient.isConnected()) {
                Toast.makeText(this, getString(R.string.not_connected), Toast.LENGTH_SHORT).show();
                return;
            }
    
            try {
                LocationServices.GeofencingApi.addGeofences(
                        mGoogleApiClient,
                        // The GeofenceRequest object.
                        getGeofencingRequest(),
                        // A pending intent that that is reused when calling removeGeofences(). This
                        // pending intent is used to generate an intent when a matched geofence
                        // transition is observed.
                        getGeofencePendingIntent()
                ).setResultCallback(this); // Result processed in onResult().
            } catch (SecurityException securityException) {
                // Catch exception generated if the app does not use ACCESS_FINE_LOCATION permission.
                logSecurityException(securityException);
            }
        }
       /**
         * Builds and returns a GeofencingRequest. Specifies the list of geofences to be monitored.
         * Also specifies how the geofence notifications are initially triggered.
         */
        private GeofencingRequest getGeofencingRequest() {
            GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
    
            // The INITIAL_TRIGGER_ENTER flag indicates that geofencing service should trigger a
            // GEOFENCE_TRANSITION_ENTER notification when the geofence is added and if the device
            // is already inside that geofence.
            builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER);
    
            // Add the geofences to be monitored by geofencing service.
            builder.addGeofences(mGeofenceList);
    
            // Return a GeofencingRequest.
            return builder.build();
        }
      /**
         * Gets a PendingIntent to send with the request to add or remove Geofences. Location Services
         * issues the Intent inside this PendingIntent whenever a geofence transition occurs for the
         * current list of geofences.
         *
         * @return A PendingIntent for the IntentService that handles geofence transitions.
         */
        private PendingIntent getGeofencePendingIntent() {
            // Reuse the PendingIntent if we already have it.
            if (mGeofencePendingIntent != null) {
                return mGeofencePendingIntent;
            }
            Intent intent = new Intent(this, GeofenceTransitionsIntentService.class);
            // We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when calling
            // addGeofences() and removeGeofences().
            return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        }

    创建围栏

        /**
         * This sample hard codes geofence data. A real app might dynamically create geofences based on
         * the user's location.
         */
        public void populateGeofenceList() {
            for (Map.Entry<String, LatLng> entry : Constants.BAY_AREA_LANDMARKS.entrySet()) {
    
                mGeofenceList.add(new Geofence.Builder()
                        // Set the request ID of the geofence. This is a string to identify this
                        // geofence.
                        .setRequestId(entry.getKey())
    
                        // Set the circular region of this geofence.
                        .setCircularRegion(
                                entry.getValue().latitude,
                                entry.getValue().longitude,
                                Constants.GEOFENCE_RADIUS_IN_METERS
                        )
    
                        // Set the expiration duration of the geofence. This geofence gets automatically
                        // removed after this period of time.
                        .setExpirationDuration(Constants.GEOFENCE_EXPIRATION_IN_MILLISECONDS)
    
                        // Set the transition types of interest. Alerts are only generated for these
                        // transition. We track entry and exit transitions in this sample.
                        .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER |
                                Geofence.GEOFENCE_TRANSITION_EXIT)
    
                        // Create the geofence.
                        .build());
            }
        }

    删除围栏。

       /**
         * Removes geofences, which stops further notifications when the device enters or exits
         * previously registered geofences.
         */
        public void removeGeofencesButtonHandler(View view) {
            if (!mGoogleApiClient.isConnected()) {
                Toast.makeText(this, getString(R.string.not_connected), Toast.LENGTH_SHORT).show();
                return;
            }
            try {
                // Remove geofences.
                LocationServices.GeofencingApi.removeGeofences(
                        mGoogleApiClient,
                        // This is the same pending intent that was used in addGeofences().
                        getGeofencePendingIntent()
                ).setResultCallback(this); // Result processed in onResult().
            } catch (SecurityException securityException) {
                // Catch exception generated if the app does not use ACCESS_FINE_LOCATION permission.
                logSecurityException(securityException);
            }
        }

    创建和删除围栏的回调,

        /**
         * Runs when the result of calling addGeofences() and removeGeofences() becomes available.
         * Either method can complete successfully or with an error.
         *
         * Since this activity implements the {@link ResultCallback} interface, we are required to
         * define this method.
         *
         * @param status The Status returned through a PendingIntent when addGeofences() or
         *               removeGeofences() get called.
         */
        public void onResult(Status status) {
            if (status.isSuccess()) {
                // Update state and save in shared preferences.
                mGeofencesAdded = !mGeofencesAdded;
                SharedPreferences.Editor editor = mSharedPreferences.edit();
                editor.putBoolean(Constants.GEOFENCES_ADDED_KEY, mGeofencesAdded);
                editor.apply();
    
                // Update the UI. Adding geofences enables the Remove Geofences button, and removing
                // geofences enables the Add Geofences button.
                setButtonsEnabledState();
    
                Toast.makeText(
                        this,
                        getString(mGeofencesAdded ? R.string.geofences_added :
                                R.string.geofences_removed),
                        Toast.LENGTH_SHORT
                ).show();
            } else {
                // Get the status code for the error and log it using a user-friendly message.
                String errorMessage = GeofenceErrorMessages.getErrorString(this,
                        status.getStatusCode());
                Log.e(TAG, errorMessage);
            }
        }

    使用服务来监听这种进出围栏的状态,

     /**
     * Listener for geofence transition changes.
     *
     * Receives geofence transition events from Location Services in the form of an Intent containing
     * the transition type and geofence id(s) that triggered the transition. Creates a notification
     * as the output.
     */
    public class GeofenceTransitionsIntentService extends IntentService {
    
        protected static final String TAG = "GeofenceTransitionsIS";
    
        /**
         * This constructor is required, and calls the super IntentService(String)
         * constructor with the name for a worker thread.
         */
        public GeofenceTransitionsIntentService() {
            // Use the TAG to name the worker thread.
            super(TAG);
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
        }
    
        /**
         * Handles incoming intents.
         * @param intent sent by Location Services. This Intent is provided to Location
         *               Services (inside a PendingIntent) when addGeofences() is called.
         */
        @Override
        protected void onHandleIntent(Intent intent) {
            GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
            if (geofencingEvent.hasError()) {
                String errorMessage = GeofenceErrorMessages.getErrorString(this,
                        geofencingEvent.getErrorCode());
                Log.e(TAG, errorMessage);
                return;
            }
    
            // Get the transition type.
            int geofenceTransition = geofencingEvent.getGeofenceTransition();
    
            // Test that the reported transition was of interest.
            if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER ||
                    geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) {
    
                // Get the geofences that were triggered. A single event can trigger multiple geofences.
                List<Geofence> triggeringGeofences = geofencingEvent.getTriggeringGeofences();
    
                // Get the transition details as a String.
                String geofenceTransitionDetails = getGeofenceTransitionDetails(
                        this,
                        geofenceTransition,
                        triggeringGeofences
                );
    
                // Send notification and log the transition details.
                sendNotification(geofenceTransitionDetails);
                Log.i(TAG, geofenceTransitionDetails);
            } else {
                // Log the error.
                Log.e(TAG, getString(R.string.geofence_transition_invalid_type, geofenceTransition));
            }
        }
    
        /**
         * Gets transition details and returns them as a formatted string.
         *
         * @param context               The app context.
         * @param geofenceTransition    The ID of the geofence transition.
         * @param triggeringGeofences   The geofence(s) triggered.
         * @return                      The transition details formatted as String.
         */
        private String getGeofenceTransitionDetails(
                Context context,
                int geofenceTransition,
                List<Geofence> triggeringGeofences) {
    
            String geofenceTransitionString = getTransitionString(geofenceTransition);
    
            // Get the Ids of each geofence that was triggered.
            ArrayList triggeringGeofencesIdsList = new ArrayList();
            for (Geofence geofence : triggeringGeofences) {
                triggeringGeofencesIdsList.add(geofence.getRequestId());
            }
            String triggeringGeofencesIdsString = TextUtils.join(", ",  triggeringGeofencesIdsList);
    
            return geofenceTransitionString + ": " + triggeringGeofencesIdsString;
        }
    
        /**
         * Posts a notification in the notification bar when a transition is detected.
         * If the user clicks the notification, control goes to the MainActivity.
         */
        private void sendNotification(String notificationDetails) {
            // Create an explicit content Intent that starts the main Activity.
            Intent notificationIntent = new Intent(getApplicationContext(), MainActivity.class);
    
            // Construct a task stack.
            TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
    
            // Add the main Activity to the task stack as the parent.
            stackBuilder.addParentStack(MainActivity.class);
    
            // Push the content Intent onto the stack.
            stackBuilder.addNextIntent(notificationIntent);
    
            // Get a PendingIntent containing the entire back stack.
            PendingIntent notificationPendingIntent =
                    stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
    
            // Get a notification builder that's compatible with platform versions >= 4
            NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
    
            // Define the notification settings.
            builder.setSmallIcon(R.drawable.ic_launcher)
                    // In a real app, you may want to use a library like Volley
                    // to decode the Bitmap.
                    .setLargeIcon(BitmapFactory.decodeResource(getResources(),
                            R.drawable.ic_launcher))
                    .setColor(Color.RED)
                    .setContentTitle(notificationDetails)
                    .setContentText(getString(R.string.geofence_transition_notification_text))
                    .setContentIntent(notificationPendingIntent);
    
            // Dismiss notification once the user touches it.
            builder.setAutoCancel(true);
    
            // Get an instance of the Notification manager
            NotificationManager mNotificationManager =
                    (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    
            // Issue the notification
            mNotificationManager.notify(0, builder.build());
        }
    
        /**
         * Maps geofence transition types to their human-readable equivalents.
         *
         * @param transitionType    A transition type constant defined in Geofence
         * @return                  A String indicating the type of transition
         */
        private String getTransitionString(int transitionType) {
            switch (transitionType) {
                case Geofence.GEOFENCE_TRANSITION_ENTER:
                    return getString(R.string.geofence_transition_entered);
                case Geofence.GEOFENCE_TRANSITION_EXIT:
                    return getString(R.string.geofence_transition_exited);
                default:
                    return getString(R.string.unknown_geofence_transition);
            }
        }
    }

    5.继承GoogleMap到android项目中;

    详细参考可以看,参考

    下面说说最关键的地方,其他地方的操作例如如何操地图,添加覆盖物等等则需要看说明文档了。

    集成GoogleMap关键就是要申请用于Google地图的apikey,分正式的和测试的key;

    正式的key是你打包上架时要用的;测试key是你平时用于测试调试的时候用的。

    这2个key的生成是使用正式的keystoreh或者测试的keystore文件,然后利用keytool 工具生成对应的SHA1值,然后到google官网填写包名和SHA1去申请对应的apikey;

    测试SHA1的值,keytool -list -v -keystore "%USERPROFILE%.androiddebug.keystore" -alias androiddebugkey -storepass android -keypass android

    正式SHA1的值,keytool -list -v -keystore your_keystore_name -alias your_alias_name

    your_alias_name为你生成正式keystore文件时填写的别名。

  • 相关阅读:
    使用JS获取上一页的url地址
    禁止弹窗中蒙层底部页面跟随滚动的几种方法汇总
    一个Option请求引发的深度解析
    html li标签前面添加图标三种方法
    CSS3
    计算x
    计算圆周率
    最长公共子序列
    小字辈(左子右兄加强版)
    Excel列名和列序号转换
  • 原文地址:https://www.cnblogs.com/zhujiabin/p/7599223.html
Copyright © 2011-2022 走看看