You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Studio中实现地图自动跟随Marker居中的动画方案

实现Google Map拖动后自动居中Marker的方案

没问题,我来帮你搞定这个需求!核心思路是监听地图的拖动行为:用户开始拖动时取消之前的延迟任务,停止拖动后启动5秒倒计时,时间到了就通过平滑动画把地图移回Marker中心,避免重复触发冲突。

具体修改步骤

  1. 添加全局变量
    需要一个Handler处理延迟任务,以及一个Runnable定义居中Marker的逻辑:
private Handler mHandler = new Handler(Looper.getMainLooper());
private Runnable mCenterMarkerRunnable;
  1. 在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();
}
  1. 避免内存泄漏
    在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 07:23:23