Android Studio中实现地图自动跟随Marker居中的动画方案
实现Google Map拖动后自动居中Marker的方案
没问题,我来帮你搞定这个需求!核心思路是监听地图的拖动行为:用户开始拖动时取消之前的延迟任务,停止拖动后启动5秒倒计时,时间到了就通过平滑动画把地图移回Marker中心,避免重复触发冲突。
具体修改步骤
- 添加全局变量
需要一个Handler处理延迟任务,以及一个Runnable定义居中Marker的逻辑:
private Handler mHandler = new Handler(Looper.getMainLooper()); private Runnable mCenterMarkerRunnable;
- 在
onMapReady中配置监听与任务
- 定义
Runnable:用animateCamera实现平滑移动动画(比moveCamera更友好) - 监听地图开始移动:用户拖动时取消之前的延迟任务
- 监听地图停止移动:用户停止拖动后启动5秒倒计时
修改后的onMapReady方法:
@Override public void onMapReady(GoogleMap googleMap) { mMap = googleMap; Log.i("", "onMapReady()"); // 初始化居中Marker的任务:带平滑动画 mCenterMarkerRunnable = new Runnable() { @Override public void run() { if (mLatlng != null && mMap != null) { mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(mLatlng, 17), 1000, null); } } }; // 用户开始拖动地图时,取消之前的延迟任务 mMap.setOnCameraMoveStartedListener(new GoogleMap.OnCameraMoveStartedListener() { @Override public void onCameraMoveStarted(int reason) { // 只响应用户手势拖动,排除地图自动移动的情况 if (reason == GoogleMap.OnCameraMoveStartedListener.REASON_GESTURE) { mHandler.removeCallbacks(mCenterMarkerRunnable); } } }); // 用户停止拖动后,启动5秒延迟居中任务 mMap.setOnCameraIdleListener(new GoogleMap.OnCameraIdleListener() { @Override public void onCameraIdle() { mHandler.removeCallbacks(mCenterMarkerRunnable); // 先清理旧任务 mHandler.postDelayed(mCenterMarkerRunnable, 5000); // 5秒后执行居中 } }); displayMarkers(); }
- 避免内存泄漏
在Activity销毁时,清理Handler的所有回调任务:
@Override protected void onDestroy() { super.onDestroy(); mHandler.removeCallbacks(mCenterMarkerRunnable); }
完整修改后的代码
public class TrackDifferentLocation extends AppCompatActivity implements OnMapReadyCallback { private GoogleMap mMap; LatLng mLatlng; String json_string; public static TextView data; LatLng latLng = null; private Handler mHandler = new Handler(Looper.getMainLooper()); private Runnable mCenterMarkerRunnable; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_track_different_location); SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager() .findFragmentById(R.id.map_fragment); mapFragment.getMapAsync(this); getSupportActionBar().setDisplayShowHomeEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true); new FetchJSON().execute(); } @Override public void onMapReady(GoogleMap googleMap) { mMap = googleMap; Log.i("", "onMapReady()"); // 初始化居中Marker的任务:带平滑动画 mCenterMarkerRunnable = new Runnable() { @Override public void run() { if (mLatlng != null && mMap != null) { mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(mLatlng, 17), 1000, null); } } }; // 用户开始拖动地图时,取消之前的延迟任务 mMap.setOnCameraMoveStartedListener(new GoogleMap.OnCameraMoveStartedListener() { @Override public void onCameraMoveStarted(int reason) { // 只响应用户手势拖动,排除地图自动移动的情况 if (reason == GoogleMap.OnCameraMoveStartedListener.REASON_GESTURE) { mHandler.removeCallbacks(mCenterMarkerRunnable); } } }); // 用户停止拖动后,启动5秒延迟居中任务 mMap.setOnCameraIdleListener(new GoogleMap.OnCameraIdleListener() { @Override public void onCameraIdle() { mHandler.removeCallbacks(mCenterMarkerRunnable); // 先清理旧任务 mHandler.postDelayed(mCenterMarkerRunnable, 5000); // 5秒后执行居中 } }); displayMarkers(); } private void displayMarkers(){ if (mMap == null)return; if(mLatlng == null) return; mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(mLatlng, 17)); mMap.addMarker(new MarkerOptions().position(mLatlng).title(String.valueOf(mLatlng))); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu, menu); return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); if (id == android.R.id.home){ this.finish(); } switch (item.getItemId()) { case R.id.mapTypeNone: mMap.setMapType(GoogleMap.MAP_TYPE_NONE); break; case R.id.mapTypeNormal: mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL); break; case R.id.mapTypeTerrain: mMap.setMapType(GoogleMap.MAP_TYPE_TERRAIN); break; case R.id.mapTypeSatellite: mMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE); break; case R.id.mapTypeHybrid: mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID); break; default: break; } return super.onOptionsItemSelected(item); } @Override protected void onDestroy() { super.onDestroy(); mHandler.removeCallbacks(mCenterMarkerRunnable); } class FetchJSON extends AsyncTask<String, Integer, LatLng> { String JSONStr = ""; String name, address, type = ""; String lat = ""; String lng = ""; String id = ""; double latDouble = -1; double lngDouble = -1; private static final String TAG = "TrackDifferentLocation"; protected LatLng doInBackground(String... args) { try { URL url = new URL("https://api.myjson.com/bins/sv5vm"); HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); InputStream inputStream = httpURLConnection.getInputStream(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String line; while ((line = bufferedReader.readLine()) != null) { JSONStr += line; } JSONObject obj = new JSONObject(JSONStr); JSONArray array = obj.getJSONArray("server response"); for (int i = 0; i < array.length(); i++) { JSONObject o = array.getJSONObject(i); id = o.optString("id"); name = o.optString("name"); address = o.optString("address"); lat = o.optString("lat"); lng = o.optString("lng"); Log.d("lat: ",lat); latDouble = Double.parseDouble(lat); lngDouble = Double.parseDouble(lng); latLng = new LatLng(latDouble, lngDouble); Log.i("JSON Values", lat + " " + lng); type = o.optString("type"); } } catch (Exception ex) { Log.e(TAG, "FetchJSON --- " + ex.getMessage()); } return latLng; } protected void onPostExecute(LatLng latLng) { if (latLng != null) { mLatlng = latLng; displayMarkers(); } } } }
功能说明
- 平滑动画:用
animateCamera代替moveCamera,让地图移动更自然 - 防重复触发:用户多次拖动时,会自动取消之前的延迟任务,只保留最后一次的倒计时
- 精准响应:只对用户手动拖动的行为生效,不会干扰地图自动移动的场景
- 内存安全:Activity销毁时清理Handler任务,避免内存泄漏
内容的提问来源于stack exchange,提问作者JamesDoh96
相关产品推荐
相关产品推荐

