Google place API (Phần 2) Current Place



  • Trong phần này ta sẽ sử dụng Google place API kết hợp Google Map API để tạo một ứng dụng nhỏ hiển thị các địa điểm gần vị trí của thiết bị, khi người dùng click vào sẽ hiển thị vị trí và thông tin địa điểm đó trên bản đồ.

    Lấy API key

    Trong phần trước mình đã hướng dẫn rất chi tiết cách lấy API key. Mọi người có thể xem lại [tại đây].
    Ta chỉ cần lấy 1 api key để dùng cho cả Google Map API và Google Place .

    Khởi tạo Places API clients

    Ta sẽ sử dụng 2 Interface:

    • GeoDataClient cung cấp truy cập vào cơ sở dữ liệu của Google về địa điểm địa phương và thông tin business .
    • PlaceDetectionClient cung cấp truy cập nhanh đến vị trí hiện tại của thiết bị và cung cấp cơ hội để báo cáo vị trí của thiết bị ở một nơi cụ thể.
      Trong hàm onCreate() của activity hoặc fragment ta khởi tạo GeoDataClient, PlaceDetectionClient, và FusedLocationProviderClient:
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        // Construct a GeoDataClient.
        mGeoDataClient = Places.getGeoDataClient(this, null);
    
        // Construct a PlaceDetectionClient.
        mPlaceDetectionClient = Places.getPlaceDetectionClient(this, null);
    
        // Construct a FusedLocationProviderClient.
        mFusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this);
    }
    

    Yêu cầu quyền vị trí

    Để có thể xác định vị trí của thiết bị và để nút vị trí trên bản đồ hoạt động khi người dùng chạm vào, ta cần yêu cầu location permission:

    1. Khai báo trong file Manifest:
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.currentplacedetailsonmap">
        <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    </manifest>
    

    2.Yêu cầu runtime permission trong ứng dụng, cho phép người dùng chấp nhận hoặc từ chối cấp quyên vị trí. Đoạn code dưới đây kiểm tra người dùng đã cấp quyền fine location hay chưa. Nếu chưa yêu cầu người dùng cấp quyền:

    > private void getLocationPermission() {
        /*
         * Request location permission, so that we can get the location of the
         * device. The result of the permission request is handled by a callback,
         * onRequestPermissionsResult.
         */
        if (ContextCompat.checkSelfPermission(this.getApplicationContext(),
                android.Manifest.permission.ACCESS_FINE_LOCATION)
                == PackageManager.PERMISSION_GRANTED) {
            mLocationPermissionGranted = true;
        } else {
            ActivityCompat.requestPermissions(this,
                    new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION},
                    PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION);
        }
    }
    
    1. Override hàm callback onRequestPermissionsResult() để xử lý kết quả xin cấp quyền vị trí:
    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           @NonNull String permissions[],
                                           @NonNull int[] grantResults) {
        mLocationPermissionGranted = false;
        switch (requestCode) {
            case PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION: {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    mLocationPermissionGranted = true;
                }
            }
        }
        updateLocationUI();
    }
    

    Thêm bản đồ

    Để hiển thị bản đồ, ta sử dụng Google Map API

    1. Thêm code xml vào file layout của activity. Đoạn code này định nghĩa một supportMapFragment có nhiệm vụ chứa bản đồ và cung cấp sự truy cập tới đối tượng GoogleMap:
    <fragment xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://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="com.example.currentplacedetailsonmap.MapsActivityCurrentPlace" />
    
    1. Trong hàm onCreate() của activity, ta sử dụng phương thức getMapAsync() để đăng ký cho map callback:
    @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);
    }
    
    1. Implement interface OnMapReadyCallBack và override phương thức onMapReady() để cài đặt map khi đối tượng GoogleMap đã sẵn sàng:
    public void onMapReady(GoogleMap map) {
        mMap = map;
    
        // Do other setup activities here too, as described elsewhere in this tutorial.
    
        // Turn on the My Location layer and the related control on the map.
        updateLocationUI();
    
        // Get the current location of the device and set the position of the map.
        getDeviceLocation();
    }
    
    1. Hàm updateLocationUI(): nếu người dùng đồng ý cấp quyền vị trí, ta sẽ enable MyLocation layer ngược lại disable MyLocation layer và đặt vị trí hiện tại là null:
    private void updateLocationUI() {
        if (mMap == null) {
            return;
        }
        try {
            if (mLocationPermissionGranted) {
                mMap.setMyLocationEnabled(true);
                mMap.getUiSettings().setMyLocationButtonEnabled(true);
            } else {
                mMap.setMyLocationEnabled(false);
                mMap.getUiSettings().setMyLocationButtonEnabled(false);
                mLastKnownLocation = null;
                getLocationPermission();
            }
        } catch (SecurityException e)  {
            Log.e("Exception: %s", e.getMessage());
        }
    }
    

    Lấy vị trí từ thiết bị và xác định vị trí trên bản đồ

    Sử dụng Fused Location Provider để xác định last-know location của thiết bị, sau đó sử dụng vị trí đó để định vị trên bản đồ:

    private void getDeviceLocation() {
        /*
         * Get the best and most recent location of the device, which may be null in rare
         * cases when a location is not available.
         */
        try {
            if (mLocationPermissionGranted) {
                Task locationResult = mFusedLocationProviderClient.getLastLocation();
                locationResult.addOnCompleteListener(this, new OnCompleteListener() {
                    @Override
                    public void onComplete(@NonNull Task task) {
                        if (task.isSuccessful()) {
                            // Set the map's camera position to the current location of the device.
                            mLastKnownLocation = task.getResult();
                            mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(
                                    new LatLng(mLastKnownLocation.getLatitude(),
                                            mLastKnownLocation.getLongitude()), DEFAULT_ZOOM));
                        } else {
                            Log.d(TAG, "Current location is null. Using defaults.");
                            Log.e(TAG, "Exception: %s", task.getException());
                            mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(mDefaultLocation, DEFAULT_ZOOM));
                            mMap.getUiSettings().setMyLocationButtonEnabled(false);
                        }
                    }
                });
            }
        } catch(SecurityException e)  {
            Log.e("Exception: %s", e.getMessage());
        }
    }
    

    Lấy địa điểm hiện tại

    Sau khi thêm và cài đặt bản đồ xong, ta sẽ sử dụng Google Place APi để lấy về danh sách các địa điểm gần với vị trí của thiết bị.

    1. Tạo một menu, khi người dùng click chọn, ứng dụng sẽ show ra các địa điểm.
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == R.id.option_get_place) {
            showCurrentPlace();
        }
        return true;
    }
    
    1. Viết hàm showCurrentPlace() để lấy danh sách các địa điểm gần với vị trí hiện tại:
    private void showCurrentPlace() {
        if (mMap == null) {
            return;
        }
    
        if (mLocationPermissionGranted) {
            // Get the likely places - that is, the businesses and other points of interest that
            // are the best match for the device's current location.
            @SuppressWarnings("MissingPermission") final
            Task<PlaceLikelihoodBufferResponse> placeResult =
                    mPlaceDetectionClient.getCurrentPlace(null);
            placeResult.addOnCompleteListener
                    (new OnCompleteListener<PlaceLikelihoodBufferResponse>() {
                        @Override
                        public void onComplete(@NonNull Task<PlaceLikelihoodBufferResponse> task) {
                            if (task.isSuccessful() && task.getResult() != null) {
                                PlaceLikelihoodBufferResponse likelyPlaces = task.getResult();
    
                                // Set the count, handling cases where less than 5 entries are returned.
                                int count;
                                if (likelyPlaces.getCount() < M_MAX_ENTRIES) {
                                    count = likelyPlaces.getCount();
                                } else {
                                    count = M_MAX_ENTRIES;
                                }
    
                                int i = 0;
                                mLikelyPlaceNames = new String[count];
                                mLikelyPlaceAddresses = new String[count];
                                mLikelyPlaceAttributions = new String[count];
                                mLikelyPlaceLatLngs = new LatLng[count];
    
                                for (PlaceLikelihood placeLikelihood : likelyPlaces) {
                                    // Build a list of likely places to show the user.
                                    mLikelyPlaceNames[i] = (String) placeLikelihood.getPlace().getName();
                                    mLikelyPlaceAddresses[i] = (String) placeLikelihood.getPlace()
                                            .getAddress();
                                    mLikelyPlaceAttributions[i] = (String) placeLikelihood.getPlace()
                                            .getAttributions();
                                    mLikelyPlaceLatLngs[i] = placeLikelihood.getPlace().getLatLng();
    
                                    i++;
                                    if (i > (count - 1)) {
                                        break;
                                    }
                                }
    
                                // Release the place likelihood buffer, to avoid memory leaks.
                                likelyPlaces.release();
    
                                // Show a dialog offering the user the list of likely places, and add a
                                // marker at the selected place.
                                openPlacesDialog();
    
                            } else {
                                Log.e(TAG, "Exception: %s", task.getException());
                            }
                        }
                    });
        } else {
            // The user has not granted permission.
            Log.i(TAG, "The user did not grant location permission.");
    
            // Add a default marker, because the user hasn't selected a place.
            mMap.addMarker(new MarkerOptions()
                    .title(getString(R.string.default_info_title))
                    .position(mDefaultLocation)
                    .snippet(getString(R.string.default_info_snippet)));
    
            // Prompt the user for permission.
            getLocationPermission();
        }
    }
    
    1. Viết hàm openPlacesDialog() để hiển thị form cho phép người dùng chọn địa điểm sau đó hiển thị marker lên bản đồ , marker content bao gồm tên và địa chỉ của địa điểm đó
    private void openPlacesDialog() {
        // Ask the user to choose the place where they are now.
        DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                // The "which" argument contains the position of the selected item.
                LatLng markerLatLng = mLikelyPlaceLatLngs[which];
                String markerSnippet = mLikelyPlaceAddresses[which];
                if (mLikelyPlaceAttributions[which] != null) {
                    markerSnippet = markerSnippet + "\n" + mLikelyPlaceAttributions[which];
                }
    
                // Add a marker for the selected place, with an info window
                // showing information about that place.
                mMap.addMarker(new MarkerOptions()
                        .title(mLikelyPlaceNames[which])
                        .position(markerLatLng)
                        .snippet(markerSnippet));
    
                // Position the map's camera at the location of the marker.
                mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(markerLatLng,
                        DEFAULT_ZOOM));
            }
        };
    
        // Display the dialog.
        AlertDialog dialog = new AlertDialog.Builder(this)
                .setTitle(R.string.pick_place)
                .setItems(mLikelyPlaceNames, listener)
                .show();
    }
    
    1. Custom layout info window giúp hiển thị content của marker trên nhiều dòng. Đầu tiên tạo file layout custom_info_contents.xml chứa 2 textview một để hiển thị info window title, một để hiển thị snippet (nội dung của info window) :
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layoutDirection="locale"
        android:orientation="vertical">
        <TextView
            android:id="@+id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:textColor="#ff000000"
            android:textStyle="bold" />
    
        <TextView
            android:id="@+id/snippet"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="#ff7f7f7f" />
    </LinearLayout>
    
    1. Implement interface InfoWindowAdapter để inflate layout và load nội dung của info window.
    @Override
    public void onMapReady(GoogleMap map) {
        // Do other setup activities here too, as described elsewhere in this tutorial.
        mMap.setInfoWindowAdapter(new GoogleMap.InfoWindowAdapter() {
    
        @Override
        // Return null here, so that getInfoContents() is called next.
        public View getInfoWindow(Marker arg0) {
            return null;
        }
    
        @Override
        public View getInfoContents(Marker marker) {
            // Inflate the layouts for the info window, title and snippet.
            View infoWindow = getLayoutInflater().inflate(R.layout.custom_info_contents, null);
    
            TextView title = ((TextView) infoWindow.findViewById(R.id.title));
            title.setText(marker.getTitle());
    
            TextView snippet = ((TextView) infoWindow.findViewById(R.id.snippet));
            snippet.setText(marker.getSnippet());
    
            return infoWindow;
          }
        });
    }
    

    Mọi người có thể tham khảo thêm về Marker, Infowindow và các thành phần khác của Google Map API tại đây

    Lưu lại trạng thái bản đồ

    Khi người dùng xoay màn hình hoặc thay đổi cấu hình, map activity sẽ bị hủy và tạo lại. Để đảm bảo trải nghiệm người dùng suôn sẻ, nên lưu trữ trạng thái ứng dụng liên quan và khôi phục lại nó khi cần thiết.Ta sẽ lưu lại vị trí của camera và vị trí của thiết bị vào saveInstanceState Bundle.

    1. Trong activity, định nghĩa các key value:
    private static final String KEY_CAMERA_POSITION = "camera_position";
    private static final String KEY_LOCATION = "location";
    
    1. implement onSaveInstanceState() callback để lưu lại trạng thái khi activity tạm dừng
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        if (mMap != null) {
            outState.putParcelable(KEY_CAMERA_POSITION, mMap.getCameraPosition());
            outState.putParcelable(KEY_LOCATION, mLastKnownLocation);
            super.onSaveInstanceState(outState);
        }
    }
    
    1. Trong hàm onCreate() của activity, lấy lại vị trí của camera và thiết bị nếu chúng đã được lưu trước đó:
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState != null) {
            mCurrentLocation = savedInstanceState.getParcelable(KEY_LOCATION);
            mCameraPosition = savedInstanceState.getParcelable(KEY_CAMERA_POSITION);
        }
        ...
    }
    

    Nguồn

    https://developers.google.com/places/android-api/current-place-tutorial
    Nguồn: Viblo


Hãy đăng nhập để trả lời
 

Có vẻ như bạn đã mất kết nối tới LaptrinhX, vui lòng đợi một lúc để chúng tôi thử kết nối lại.