如何在谷歌地图中平滑移动当前位置?

Posted

技术标签:

【中文标题】如何在谷歌地图中平滑移动当前位置?【英文标题】:How to smoothly move the current position in Google Maps? 【发布时间】:2018-10-29 14:18:02 【问题描述】:

在我的android项目中,我需要在车辆移动时,标记位置也应该平滑移动,但是当位置改变时,标记从一个位置跳到另一个位置,移动不顺畅。

我搜索了很多,但没有得到结果。 我不知道最好的方法是什么。请给我完整的指导。

非常感谢

更新

@AnkitMehta 谢谢你的回答

MapsActivity 中的所有代码:

import android.Manifest;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.location.Location;
import android.os.Build;
import android.os.Handler;
import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentActivity;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.util.Log;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import android.widget.Toast;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MapStyleOptions;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;

public class MapsActivity extends FragmentActivity implements OnMapReadyCallback, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener 

  private GoogleMap mMap;
  SupportMapFragment mapFragment;
  LocationRequest mLocationRequest;
  GoogleApiClient mGoogleApiClient;
  Location mLastLocation;
  Marker mCurrLocationMarker;

  @Override
  protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_maps);
    // Obtain the SupportMapFragment and get notified when the map is ready to be used.
    mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
    mapFragment.getMapAsync(this);
  

  @Override
  protected void onPause() 
    super.onPause();

    //stop Location updates when Activity is no longer active
    if (mGoogleApiClient != null) 
      LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
    
  


  @Override
  public void onMapReady(GoogleMap googleMap) 
    mMap = googleMap;

    try 
      // Customise the styling of the base map using a JSON object defined
      // in a raw resource file.
      boolean success = googleMap.setMapStyle(
        MapStyleOptions.loadRawResourceStyle(
          this, R.raw.style_json));

      if (!success) 
        Log.e("LOG" , "Style parsing failed.");
      
     catch (Resources.NotFoundException e) 
      Log.e("LOG", "Can't find style. Error: ", e);
    

    //initialize Google play Services
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) 
      if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) 
        // Location Permission already granted
        buildGoogleApiClient();
        mMap.setMyLocationEnabled(true);

       else 
        // Request Location Permission
        checkLocationPermission();
      
     else 
      buildGoogleApiClient();
      mMap.setMyLocationEnabled(true);
    


  

  protected synchronized void buildGoogleApiClient() 
    mGoogleApiClient = new GoogleApiClient.Builder(this)
      .addConnectionCallbacks(this)
      .addOnConnectionFailedListener(this)
      .addApi(LocationServices.API)
      .build();
    mGoogleApiClient.connect();
  


  @Override
  public void onConnected(@Nullable Bundle bundle) 
    mLocationRequest = new LocationRequest();
    //mLocationRequest.setInterval(30000);
    //mLocationRequest.setFastestInterval(30000);
    mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) 
      LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
    
  

  @Override
  public void onConnectionSuspended(int i) 

  

  @Override
  public void onConnectionFailed(@NonNull ConnectionResult connectionResult) 

  

  @Override
  public void onLocationChanged(Location location) 
    mLastLocation = location;
    if (mCurrLocationMarker != null) 
      mCurrLocationMarker.remove();
    

    //Place current location marker
    LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
    animateMarker(latLng, latLng, true);


    //move map camera
    mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng,11));
    mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, 11));
  

  public static final int MY_PERMISSIONS_REQUEST_LOCATION = 99;
  private void checkLocationPermission() 
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
      != PackageManager.PERMISSION_GRANTED) 

      // Should we show an explanation?
      if (ActivityCompat.shouldShowRequestPermissionRationale(this,
        Manifest.permission.ACCESS_FINE_LOCATION)) 

        // Show an explanation to the user *asynchronously* -- don't block
        // this thread waiting for the user's response! After the user
        // sees the explanation, try again to request the permission.
        new AlertDialog.Builder(this)
          .setTitle("Location Permission Needed")
          .setMessage("This app needs the Location permission, please accept to use location functionality")
          .setPositiveButton("OK", new DialogInterface.OnClickListener() 
            @Override
            public void onClick(DialogInterface dialogInterface, int i) 
              //Prompt the user once explanation has been shown
              ActivityCompat.requestPermissions(MapsActivity.this,
                new String[]Manifest.permission.ACCESS_FINE_LOCATION,
                MY_PERMISSIONS_REQUEST_LOCATION );
            
          )
          .create()
          .show();


       else 
        // No explanation needed, we can request the permission.
        ActivityCompat.requestPermissions(this,
          new String[]Manifest.permission.ACCESS_FINE_LOCATION,
          MY_PERMISSIONS_REQUEST_LOCATION );
      
    
  

  @Override
  public void onRequestPermissionsResult(int requestCode,
                                         String permissions[], int[] grantResults) 
    switch (requestCode) 
      case MY_PERMISSIONS_REQUEST_LOCATION: 
        // If request is cancelled, the result arrays are empty.
        if (grantResults.length > 0
          && grantResults[0] == PackageManager.PERMISSION_GRANTED) 

          // permission was granted, yay! Do the
          // location-related task you need to do.
          if (ContextCompat.checkSelfPermission(this,
            Manifest.permission.ACCESS_FINE_LOCATION)
            == PackageManager.PERMISSION_GRANTED) 

            if (mGoogleApiClient == null) 
              buildGoogleApiClient();
            
            mMap.setMyLocationEnabled(true);
          

         else 

          // permission denied, boo! Disable the
          // functionality that depends on this permission.
          Toast.makeText(this, "permission denied", Toast.LENGTH_LONG).show();
        
        return;
      

      // other 'case' lines to check for other
      // permissions this app might request
    
  


  //This methos is used to move the marker of each car smoothly when there are any updates of their position
  public void animateMarker(final LatLng startPosition, final LatLng toPosition,
                            final boolean hideMarker) 


    final Marker marker = mMap.addMarker(new MarkerOptions()
      .position(startPosition)
      .icon(BitmapDescriptorFactory.fromResource(R.drawable.car)));


    final Handler handler = new Handler();
    final long start = SystemClock.uptimeMillis();

    final long duration = 1000;
    final Interpolator interpolator = new LinearInterpolator();

    handler.post(new Runnable() 
      @Override
      public void run() 
        long elapsed = SystemClock.uptimeMillis() - start;
        float t = interpolator.getInterpolation((float) elapsed
          / duration);
        double lng = t * toPosition.longitude + (1 - t)
          * startPosition.longitude;
        double lat = t * toPosition.latitude + (1 - t)
          * startPosition.latitude;

        marker.setPosition(new LatLng(lat, lng));

        if (t < 1.0) 
          // Post again 16ms later.
          handler.postDelayed(this, 16);
         else 
          if (hideMarker) 
            marker.setVisible(false);
           else 
            marker.setVisible(true);
          
        
      
    );
  

【问题讨论】:

你能分享一下你目前写的代码(用于在地图上移动相机)吗? 【参考方案1】:
CameraPosition cameraPosition = new CameraPosition.Builder()
                                .target(latLng)
                                .build();
CameraUpdate cu = CameraUpdateFactory.newCameraPosition(cameraPosition);
map.animateCamera(cu);

这将移动到动画选择的纬度(相机位置 并使用相机更新)

【讨论】:

//移动地图相机而不是:mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng,11)); mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, 11));只做:mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, 11));你只需要使用 animateCamera 而不是 moveCamera 非常感谢,您的代码改进了我的代码但是当我在我的车里时,我在地图上的位置显示不流畅,但几秒钟后它会跳转到另一个位置.. .是什么原因? 可能是你没有使用低延迟进行更新...它可以节省电池但需要更长的时间来提供更新的位置 好的,但是在谷歌地图应用中,这是一个顺利的举动 locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, LOCATION_REFRESH_TIME, LOCATION_REFRESH_DISTANCE, mlocationListener); mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient); if(mLastLocation!=null) Toast.makeText(getApplicationContext(), "YES!mLastLocation!=null", Toast.LENGTH_SHORT).show();双纬度 = mLastLocation.getLatitude();双经度 = mLastLocation.getLongitude(); Toast.makeText(getApplicationContext(), "Latitude = " + latitude + "\nLongitude = " + longitude, Toast.LENGTH_SHORT).show();声明距离(m)n时间(ms)【参考方案2】:

重写此方法:

@Override
public void onLocationChange(Location location) 
     LatLng latLng = new LatLng(location.latitude, location.longitude);
     refreshMapPosition(latLng, 45)



private void refreshMapPosition(LatLng pos, float angle) 
    CameraPosition.Builder positionBuilder = new CameraPosition.Builder();
    positionBuilder.target(pos);
    positionBuilder.zoom(15f);
    positionBuilder.bearing(angle);
    positionBuilder.tilt(60);
    map.animateCamera(CameraUpdateFactory.newCameraPosition(positionBuilder.build()));

这个被覆盖的方法是在 GoogleMap 类上实现的,地图变量是我们定义的用于加载谷歌地图实例的变量。

请参考:https://developers.google.com/maps/documentation/android-sdk/views

【讨论】:

以上是关于如何在谷歌地图中平滑移动当前位置?的主要内容,如果未能解决你的问题,请参考以下文章

在谷歌地图中保留用户位置中心 - Swift

如何在谷歌地图应用程序中从另一个位置显示当前位置?

如何在谷歌地图上获取图像视图坐标(位置/纬度)

在谷歌地图javascript api中 - 如何移动地图而不是标记

如何在谷歌地图 api v2 上获取设备的当前位置?

在谷歌地图中将当前位置映射到目的地位置