Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Android

Cách phát hiện chạm (touch) và vị trí trên Google Maps trong Android

Giới thiệu

Bài viết này hướng dẫn cách phát hiện tương tác người dùng (chạm, kéo, zoom) trên Google Maps Android SDK thông qua các listener: OnCameraMoveStartedListener, OnCameraMoveListener, OnCameraIdleListener.

Yêu cầu tiên quyết

  • Google Play Services cài đặt trên thiết bị
  • API Key Google Maps (tạo tại Google Cloud Console)
  • Dependency: implementation 'com.google.android.gms:play-services-maps:18.2.0'

Các bước thực hiện

Bước 1: Tạo project Google Maps

Android Studio → File → New → New Project → Google Maps Activity (tự động tạo template).

Bước 2: Layout (res/layout/activity_maps.xml)

<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:tools="https://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=".MapsActivity" />

Bước 3: MapsActivity implement các listener (src/MapsActivity.java)

package app.com.myapplication;

import android.os.Bundle;
import android.widget.Toast;
import androidx.fragment.app.FragmentActivity;
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.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;

public class MapsActivity extends FragmentActivity implements
        OnMapReadyCallback,
        GoogleMap.OnCameraMoveStartedListener,
        GoogleMap.OnCameraMoveListener,
        GoogleMap.OnCameraMoveCanceledListener,
        GoogleMap.OnCameraIdleListener {

    private GoogleMap mMap;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_maps);

        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);
    }

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

        // Thêm marker tại Sydney
        LatLng sydney = new LatLng(-34, 151);
        mMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney"));
        mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney));

        // Đăng ký các listener
        mMap.setOnCameraIdleListener(this);
        mMap.setOnCameraMoveStartedListener(this);
        mMap.setOnCameraMoveListener(this);
        mMap.setOnCameraMoveCanceledListener(this);
    }

    // 1. Camera bắt đầu di chuyển
    @Override
    public void onCameraMoveStarted(int reason) {
        String reasonText;
        switch (reason) {
            case GoogleMap.OnCameraMoveStartedListener.REASON_GESTURE:
                reasonText = "Người dùng vuốt/chạm trên bản đồ";
                break;
            case GoogleMap.OnCameraMoveStartedListener.REASON_API_ANIMATION:
                reasonText = "Người dùng tap vào marker/UI";
                break;
            case GoogleMap.OnCameraMoveStartedListener.REASON_DEVELOPER_ANIMATION:
                reasonText = "App di chuyển camera (code)";
                break;
            default:
                reasonText = "Lý do không xác định";
        }
        Toast.makeText(this, "Bắt đầu di chuyển: " + reasonText, Toast.LENGTH_SHORT).show();
    }

    // 2. Camera đang di chuyển (gọi liên tục)
    @Override
    public void onCameraMove() {
        // Toast.makeText(this, "Camera đang di chuyển...", Toast.LENGTH_SHORT).show();
        // Không nên show Toast ở đây (gọi quá nhiều), dùng Log hoặc update UI nhẹ
        LatLng center = mMap.getCameraPosition().target;
        // Cập nhật UI real-time: center.latitude, center.longitude
    }

    // 3. Camera bị hủy di chuyển
    @Override
    public void onCameraMoveCanceled() {
        Toast.makeText(this, "Di chuyển camera bị hủy", Toast.LENGTH_SHORT).show();
    }

    // 4. Camera dừng lại (idle)
    @Override
    public void onCameraIdle() {
        LatLng finalPosition = mMap.getCameraPosition().target;
        float zoom = mMap.getCameraPosition().zoom;

        Toast.makeText(this,
            String.format("Camera dừng tại: %.4f, %.4f | Zoom: %.1f",
                finalPosition.latitude, finalPosition.longitude, zoom),
            Toast.LENGTH_SHORT).show();
    }
}

Giải thích 4 Listener:

ListenerKhi nào gọiUse case
OnCameraMoveStartedListenerCamera bắt đầu di chuyểnPhân biệt user gesture vs code, ẩn UI tạm thời
OnCameraMoveListenerMỗi frame khi camera di chuyểnReal-time tracking, clustering markers
OnCameraMoveCanceledListenerDi chuyển bị ngắt quãngRollback UI state
OnCameraIdleListenerCamera dừng hoàn toànLoad data mới, geocoding, search nearby

Bước 4: API Key (res/values/google_maps_api.xml)

<resources>
    <string name="google_maps_key" templateMergeStrategy="preserve" translatable="false">YOUR_API_KEY_HERE</string>
</resources>

Bước 5: Manifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
    package="app.com.myapplication">

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <activity
            android:name=".MapsActivity"
            android:label="@string/title_activity_maps">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="@string/google_maps_key" />

    </application>
</manifest>

Chạy ứng dụng

Chạy app trên thiết bị thật (cần Google Play Services). Vuốt, zoom, tap trên bản đồ để thấy các Toast hiển thị trạng thái camera.

Cách phát hiện chạm (touch) và vị trí trên Google Maps trong Android

Mở rộng: Xử lý tap vào bản đồ (OnMapClickListener)

mMap.setOnMapClickListener(latLng -> {
    // User tap vào bản đồ (không phải marker)
    Toast.makeText(this, "Tapped at: " + latLng.latitude + ", " + latLng.longitude, Toast.LENGTH_SHORT).show();
    
    // Thêm marker tại vị trí tap
    mMap.addMarker(new MarkerOptions().position(latLng).title("New Marker"));
});

mMap.setOnMarkerClickListener(marker -> {
    // User tap vào marker
    Toast.makeText(this, "Marker clicked: " + marker.getTitle(), Toast.LENGTH_SHORT).show();
    return false; // true = consume event, false = default behavior (show info window)
});

Lưu ý hiệu năng

  • onCameraMove() gọi rất nhiều lần (60fps) - không làm việc nặng ở đây
  • Dùng onCameraIdle() để fetch data, geocoding, network call
  • Hủy listener trong onDestroy(): mMap.setOnCameraMoveListener(null)