Tạo bộ chọn Địa điểm hiện tại của bạn cho Android (Java)

1. Trước khi bắt đầu

Tìm hiểu cách sử dụng Nền tảng Google Maps và SDK địa điểm dành cho Android để trình bày cho người dùng của bạn danh sách các địa điểm để xác định vị trí hiện tại của họ.

bd07a9ad2cb27a06.png

Điều kiện tiên quyết

  • Kỹ năng Java cơ bản

Bạn sẽ thực hiện

  • Thêm bản đồ vào ứng dụng Android.
  • Sử dụng quyền truy cập thông tin vị trí để xác định vị trí địa lý của người dùng.
  • Tìm nạp địa điểm gần vị trí hiện tại của người dùng.
  • Trình bày các địa điểm có thể cho người dùng để xác định vị trí hiện tại của họ.

Sản phẩm bạn sẽ tạo ra

Bạn tạo ứng dụng Android của mình từ đầu, nhưng bạn có thể tải mã mẫu xuống để so sánh khi gỡ lỗi. Tải mã mẫu từ GitHub hoặc nếu bạn đã thiết lập Git để sử dụng dòng lệnh, hãy nhập những thông tin sau:

git clone https://github.com/googlecodelabs/current-place-picker-android.git

Nếu bạn gặp phải bất kỳ vấn đề nào (lỗi mã, lỗi ngữ pháp, từ ngữ không rõ ràng hoặc các vấn đề khác) khi tham gia lớp học lập trình này, vui lòng báo cáo vấn đề qua đường liên kết Báo cáo lỗi ở góc dưới bên trái của lớp học lập trình.

2. Bắt đầu

Trước khi bắt đầu lớp học lập trình này, bạn cần thiết lập các mục sau:

Android Studio

Tải Android Studio xuống từ https://developer.android.com/studio.

Nếu bạn đã có Android Studio, hãy nhớ cài đặt phiên bản mới nhất bằng cách nhấp vào Android Studio > Kiểm tra bản cập nhật....

1f36bae83b64e33.png

Phòng thí nghiệm này được viết bằng Android Studio 3.4.

SDK Android

Trong Android Studio, bạn có thể định cấu hình các SDK mong muốn bằng Trình quản lý SDK. Phòng thí nghiệm này sử dụng SDK Android Q.

  1. Trên màn hình chào mừng của Android Studio, nhấp vào Định cấu hình > Trình quản lý SDK.

d3fa03c269ec231c.png

  1. Chọn hộp đánh dấu SDK mà bạn muốn, sau đó nhấp vào Áp dụng.

Nếu bạn chưa có SDK, thì quá trình này sẽ bắt đầu tải SDK xuống máy của bạn.

884e0aa1314f70d.png

Dịch vụ Google Play

Từ trình quản lý SDK, bạn cũng cần cài đặt Dịch vụ Google Play.

  1. Nhấp vào thẻ Công cụ SDK rồi chọn hộp đánh dấu Dịch vụ Google Play.

Hãy cập nhật nếu trạng thái là Đã có bản cập nhật.

ad6211fd78f3b629.png

3. Chuẩn bị trình mô phỏng

Để chạy ứng dụng này, bạn có thể kết nối thiết bị của riêng mình hoặc sử dụng Trình mô phỏng Android.

Nếu bạn sử dụng thiết bị của riêng mình, hãy chuyển đến phần Hướng dẫn thực về thiết bị: Cập nhật Dịch vụ Google Play ở cuối trang này.

Thêm trình mô phỏng

  1. Trên màn hình chào mừng của Android Studio, nhấp vào Định cấu hình > Trình quản lý thiết bị ảo Android.

5dd2d14c9c56d3f9.png

Thao tác này sẽ mở hộp thoại Trình quản lý thiết bị ảo Android.

  1. Nhấp vào Tạo thiết bị ảo... để mở danh sách thiết bị mà bạn có thể chọn.

2d44eada384f8b35.png

  1. Chọn một thiết bị có biểu tượng Play d5722488d80cd6be.png trong cột Cửa hàng Play và nhấp vào Tiếp theo.

e0248f1c6e85ab7c.png

Bạn sẽ thấy một tập hợp hình ảnh hệ thống mà bạn có thể cài đặt. Nếu phần Q nhắm mục tiêu Android 9.+ (Google Play) có từ Tải xuống bên cạnh, hãy nhấp vào Tải xuống.

316d0d1efabd9f24.png

  1. Nhấp vào Tiếp theo để đặt tên cho thiết bị ảo, rồi nhấp vào Hoàn tất.

Bạn sẽ quay lại danh sách Thiết bị ảo của mình.

  1. Nhấp vào biểu tượng Bắt đầu ba8adffe56d3b678.png bên cạnh thiết bị mới:

7605864ed27f77ea.png

Sau vài phút, trình mô phỏng sẽ mở ra.

Hướng dẫn về trình mô phỏng – cập nhật Dịch vụ Google Play

  1. Sau khi trình mô phỏng chạy, hãy nhấp vào biểu tượng ... trong thanh điều hướng xuất hiện**.**

2e1156e02643d018.png

Thao tác này sẽ mở hộp thoại Kiểm soát mở rộng.

  1. Nhấp vào Google Play trong trình đơn.

Nếu có bản cập nhật, hãy nhấp vào Cập nhật.

5afd2686c5cad0e5.png

  1. Đăng nhập vào trình mô phỏng bằng Tài khoản Google.

Bạn có thể sử dụng tài khoản của mình hoặc tạo tài khoản mới mà không mất phí để tách biệt hoạt động thử nghiệm với thông tin cá nhân của mình.

Sau đó, Google Play sẽ mở ra Dịch vụ Google Play.

  1. Nhấp vào Cập nhật để tải phiên bản Dịch vụ Google Play mới nhất.

f4bc067e80630b9c.png

Nếu bạn được yêu cầu hoàn tất việc thiết lập tài khoản và thêm tùy chọn thanh toán, hãy nhấp vào Bỏ qua.

Đặt vị trí trong trình mô phỏng

  1. Sau khi trình mô phỏng chạy, hãy nhập "maps" vào thanh tìm kiếm trên màn hình chính để hiển thị biểu tượng ứng dụng Google Maps.

2d996aadd53685a6.png

  1. Nhấp vào biểu tượng để mở.

Bạn sẽ thấy một bản đồ mặc định.

  1. Ở dưới cùng bên phải của bản đồ, hãy nhấp vào Vị trí của bạn c5b4e2fda57a7e71.png.

Bạn cần cấp cho điện thoại quyền sử dụng thông tin vị trí.

f2b68044eabca151.png

  1. Nhấp vào ... để mở trình đơn Kiểm soát mở rộng.
  2. Nhấp vào tab Vị trí.
  3. Nhập vĩ độ và kinh độ.

Hãy nhập mọi thứ bạn thích ở đây, nhưng hãy đảm bảo rằng bạn ở khu vực có nhiều địa điểm.

(Sử dụng Latitude 20.7818 và Kinh độ -156.4624 cho thị trấn Kihei trên Maui ở Hawaii để sao chép kết quả từ lớp học lập trình này.)

  1. Nhấp vào Gửi và bản đồ sẽ cập nhật thông tin vị trí này.

f9576b35218f4187.png

Bạn đã sẵn sàng chạy ứng dụng của mình và thử nghiệm ứng dụng dựa trên vị trí.

Hướng dẫn sử dụng thiết bị thực — cập nhật Dịch vụ Google Play

Nếu bạn đang sử dụng thiết bị Android thực, hãy làm như sau:

  1. Sử dụng thanh tìm kiếm trên màn hình chính để tìm kiếm và mở Dịch vụ Google Play.
  2. Nhấp vào Chi tiết khác.

Nếu có, hãy nhấp vào Cập nhật.

ad16cdb975b5c3f7.png baf0379ef8a9c88c.png

4. Tạo giao diện ứng dụng với hoạt động trên Google Maps

  1. Trên màn hình chào mừng của Android Studio, hãy chọn Bắt đầu dự án Android Studio mới.
  2. Trên thẻ Điện thoại và máy tính bảng, hãy chọn Hoạt động trên Google Maps.

c9c80aa8211a8761.png

Hộp thoại Định cấu hình dự án sẽ mở ra. Đây là nơi bạn đặt tên cho ứng dụng của mình và tạo gói dựa trên miền của bạn.

Dưới đây là các tùy chọn cài đặt cho một ứng dụng có tên là Địa điểm hiện tại, tương ứng với gói com.google.codelab.currentplace.

37f5b93b94ee118c.png

  1. Chọn Java làm ngôn ngữ và chọn Sử dụng cấu phần phần mềm androidx*.

Giữ các chế độ cài đặt mặc định còn lại.

  1. Nhấp vào Finish (Hoàn tất).

5. Thêm các phần phụ thuộc Dịch vụ của Google vào tệp bản dựng Gradle

Để truy cập các quyền truy cập thông tin vị trí trong Android, bạn cần có API Vị trí và Nhận dạng hoạt động trên Google từ Dịch vụ Google Play. Để biết thêm thông tin về cách thêm API này và các API Dịch vụ Google Play khác, hãy xem phần Thiết lập Dịch vụ Google Play.

Dự án Android Studio thường có 2 tệp build.gradle. Một là dành cho dự án tổng thể và một là dành cho ứng dụng. Nếu có trình khám phá Dự án Android Studio ở chế độ xem Android, bạn sẽ thấy cả hai dự án trong thư mục Gradle Scripts. Bạn cần chỉnh sửa tệp build.gradle (Module: app) để thêm các dịch vụ của Google.

f3043429cf719c47.png

  1. Thêm hai dòng vào phần dependencies để thêm các dịch vụ của Google cho vị trí và API địa điểm (mã mẫu trong bối cảnh).

build.gradle (Mô-đun: ứng dụng)

plugins {
  id 'com.android.application'
}

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "com.google.codelab.currentplace"
        minSdkVersion 19
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'com.google.android.gms:play-services-maps:16.1.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'

    implementation 'com.google.android.gms:play-services-location:16.0.0'
    implementation 'com.google.android.libraries.places:places:1.1.0'
}

6. Bật API Google Maps Platform và nhận khóa API

Đối với bước bật sau đây , bạn cần bật SDK Maps cho AndroidAPI địa điểm.

Thiết lập Nền tảng Google Maps

Nếu bạn chưa có tài khoản Google Cloud Platform và một dự án đã bật tính năng thanh toán, vui lòng xem hướng dẫn Bắt đầu sử dụng Google Maps Platform để tạo tài khoản thanh toán và một dự án.

  1. Trong Cloud Console, hãy nhấp vào trình đơn thả xuống dự án và chọn dự án mà bạn muốn sử dụng cho lớp học lập trình này.

  1. Bật API và SDK của Nền tảng Google Maps bắt buộc cho lớp học lập trình này trong Google Cloud Marketplace. Để làm như vậy, hãy làm theo các bước trong video này hoặc tài liệu này.
  2. Tạo khoá API trong trang Thông tin xác thực của Cloud Console. Bạn có thể làm theo các bước trong video này hoặc tài liệu này. Tất cả các yêu cầu gửi đến Google Maps Platform đều yêu cầu khóa API.

Sao chép khoá API mà bạn vừa tạo. Quay lại Android Studio và tìm tệp google_maps_api.xml trong phần Android > app > res > value.

Thay thế YOUR_KEY_HERE bằng khóa API mà bạn đã sao chép.

aa576e551a7a1009.png

Ứng dụng của bạn hiện đã được định cấu hình.

7. Chỉnh sửa tệp bố cục

  1. Trong trình khám phá dự án, hãy mở tệp activity_maps.xml trong Android > app > res > layout.

4e0d986480c57efa.png

  1. Bạn sẽ thấy giao diện người dùng cơ bản mở ở bên phải màn hình, với các thẻ ở dưới cùng cho phép bạn chọn trình chỉnh sửa Thiết kế hoặc Văn bản cho bố cục của mình. Chọn Văn bản và thay thế toàn bộ nội dung của tệp bố cục bằng tiêu chí sau:

activity_maps.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical">

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:minHeight="?attr/actionBarSize"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:titleTextColor="@android:color/white"
        android:background="@color/colorPrimary" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <fragment
            android:id="@+id/map"
            android:name="com.google.android.gms.maps.SupportMapFragment"
            android:layout_width="match_parent"
            android:layout_height="349dp"
            tools:context=".MapsActivity" />

        <ListView
            android:id="@+id/listPlaces"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </LinearLayout>

</LinearLayout>

Bạn sẽ thấy giao diện người dùng như sau:

1bf786808a4697ce.png

8. Thiết lập thanh ứng dụng

Để người dùng có thể nhấp vào nút khi họ muốn chọn địa điểm hiện tại của mình, hãy thêm thanh ứng dụng có biểu tượng tìm địa điểm hiện tại của người dùng và hiển thị các địa điểm có thể ở gần đó. Giao diện đó sẽ giống như sau:

3a17c92b613a26c5.png

Trên điện thoại, chỉ có biểu tượng hiển thị. Trên một máy tính bảng có thêm không gian, văn bản cũng được bao gồm.

Tạo biểu tượng

  1. Trong trình khám phá dự án, hãy nhấp vào Android và gt; app, sau đó nhấp chuột phải vào thư mục res rồi chọn New > Image Asset.

Asset Studio mở ra.

  1. Trong trình đơn Loại biểu tượng, hãy nhấp vào Biểu tượng thanh hành động và thanh.
  2. Đặt tên cho tài sản của bạn ic_geolocate.
  3. Chọn Clip Art làm loại tài sản**.**
  4. Nhấp vào hình đồ họa bên cạnh Nghệ thuật đoạn video.

Thao tác này sẽ mở cửa sổ Chọn biểu tượng.

  1. Chọn một biểu tượng.

Bạn có thể sử dụng thanh tìm kiếm để tìm biểu tượng liên quan đến ý định của mình.

  1. Tìm kiếm location và chọn một biểu tượng liên quan đến vị trí.

Biểu tượng vị trí của tôi giống với biểu tượng dùng trong ứng dụng Google Maps khi người dùng muốn gắn máy ảnh vào vị trí hiện tại.

  1. Nhấp vào OK > Tiếp theo > Hoàn tất,rồi xác nhận có một thư mục mới có tên là drawable chứa các tệp biểu tượng mới của bạn.

b9e0196137ed18ae.png

Thêm tài nguyên chuỗi

  1. Trong trình khám phá dự án, hãy nhấp vào Android > app > res > values và mở tệp strings.xml.
  2. Thêm các dòng sau sau <string name="title_activity_maps">Map</string>:

strings.xml

    <string name="action_geolocate">Pick Place</string>
    <string name="default_info_title">Default Location</string>
    <string name="default_info_snippet">No places found, because location permission is disabled.</string>

Dòng đầu tiên được sử dụng trong thanh ứng dụng khi có khoảng trống để bao gồm nhãn văn bản bên cạnh biểu tượng. Những số khác được dùng cho các điểm đánh dấu mà bạn thêm vào bản đồ.

Bây giờ, mã trong tệp sẽ như sau:

<resources>
    <string name="app_name">Current Place</string>
    <string name="title_activity_maps">Map</string>
    <string name="action_geolocate">Pick Place</string>
    <string name="default_info_title">Default Location</string>
    <string name="default_info_snippet">No places found, because location permission is disabled.</string>
</resources>

Thêm thanh ứng dụng

  1. Trong trình khám phá dự án, hãy nhấp vào Android và gt; app, rồi nhấp chuột phải vào thư mục res rồi chọn New > Directory để tạo một thư mục con mới trong app/src/main/res.
  2. Đặt tên cho thư mục là menu.
  3. Nhấp chuột phải vào thư mục menu rồi chọn New > File.
  4. Đặt tên menu.xml cho tệp.
  5. Dán mã này:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <!-- "Locate me", should appear as action button if possible -->
    <item
        android:id="@+id/action_geolocate"
        android:icon="@drawable/ic_geolocate"
        android:title="@string/action_geolocate"
        app:showAsAction="always|withText" />

</menu>

Cập nhật kiểu thanh ứng dụng

  1. Trong trình khám phá dự án, hãy mở rộng Android > app > res > values rồi mở tệp styles.xml bên trong.
  2. Trong thẻ <style>, hãy chỉnh sửa thuộc tính mẹ thành "Theme.AppCompat.NoActionBar".
  3. Hãy lưu ý đến thuộc tính name mà bạn sử dụng trong bước tiếp theo.

styles.xml

<style name="AppTheme" parent="Theme.AppCompat.NoActionBar">

Cập nhật giao diện ứng dụng trong AndroidManifest.xml

  1. Nhấp vào Android > app > manifests và mở tệp AndroidManifest.xml.
  2. Tìm dòng android:theme và chỉnh sửa hoặc xác nhận giá trị thành @style/AppTheme.

AndroidManifest.xml

   <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">

Bây giờ, bạn đã sẵn sàng bắt đầu lập trình!

9. Khởi chạy ứng dụng

  1. Trong trình khám phá dự án, hãy tìm tệp MapsActivity.java.

Phần này nằm trong thư mục tương ứng với gói mà bạn đã tạo cho ứng dụng của mình ở bước 1.

8b0fa27d417f5f55.png

  1. Mở tệp và bạn &trong trình chỉnh sửa mã Java.

Nhập SDK địa điểm và các phần phụ thuộc khác

Thêm các dòng này ở đầu MapsActivity.java, thay thế các câu lệnh nhập hiện có.

Các quy tắc này bao gồm các lần nhập hiện có và thêm nhiều lần nhập được sử dụng trong mã trong lớp học lập trình này.

MapsActivity.java

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import com.google.android.gms.common.api.ApiException;
import com.google.android.gms.location.FusedLocationProviderClient;
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.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;

import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task;
import com.google.android.libraries.places.api.Places;
import com.google.android.libraries.places.api.model.Place;
import com.google.android.libraries.places.api.model.PlaceLikelihood;
import com.google.android.libraries.places.api.net.FindCurrentPlaceRequest;
import com.google.android.libraries.places.api.net.FindCurrentPlaceResponse;
import com.google.android.libraries.places.api.net.PlacesClient;

import java.util.Arrays;
import java.util.List;

Cập nhật chữ ký của lớp học

API Địa điểm sử dụng các thành phần AndroidX để hỗ trợ khả năng tương thích ngược. Do đó, bạn cần xác định API này để mở rộng AppCompatActivity. Thay thế tiện ích FragmentActivity được xác định theo mặc định cho một hoạt động trên bản đồ.

public class MapsActivity extends AppCompatActivity implements OnMapReadyCallback {

Thêm biến lớp

Tiếp theo, hãy khai báo các biến lớp được dùng trong các phương thức lớp khác nhau. Các thành phần này bao gồm các thành phần trên giao diện người dùng và mã trạng thái. Các giá trị này phải ngay bên dưới phần khai báo biến cho GoogleMap mMap.

    // New variables for Current Place picker
    private static final String TAG = "MapsActivity";
    ListView lstPlaces;
    private PlacesClient mPlacesClient;
    private FusedLocationProviderClient mFusedLocationProviderClient;

    // The geographical location where the device is currently located. That is, the last-known
    // location retrieved by the Fused Location Provider.
    private Location mLastKnownLocation;

    // A default location (Sydney, Australia) and default zoom to use when location permission is
    // not granted.
    private final LatLng mDefaultLocation = new LatLng(-33.8523341, 151.2106085);
    private static final int DEFAULT_ZOOM = 15;
    private static final int PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 1;
    private boolean mLocationPermissionGranted;

    // Used for selecting the Current Place.
    private static final int M_MAX_ENTRIES = 5;
    private String[] mLikelyPlaceNames;
    private String[] mLikelyPlaceAddresses;
    private String[] mLikelyPlaceAttributions;
    private LatLng[] mLikelyPlaceLatLngs;

Cập nhật phương thức onCreate

Bạn cần cập nhật phương thức onCreate để xử lý quyền của người dùng trong thời gian chạy đối với các dịch vụ vị trí, thiết lập các thành phần giao diện người dùng và tạo ứng dụng API địa điểm.

Thêm các dòng mã sau liên quan đến thanh công cụ hành động, thiết lập chế độ xem và ứng dụng Địa điểm vào cuối phương thức onCreate() hiện tại.

MapsActivity.java onCreate()

   @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.
        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);

        //
        // PASTE THE LINES BELOW THIS COMMENT
        //
        
        // Set up the action toolbar
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        // Set up the views
        lstPlaces = (ListView) findViewById(R.id.listPlaces);

        // Initialize the Places client
        String apiKey = getString(R.string.google_maps_key);
        Places.initialize(getApplicationContext(), apiKey);
        mPlacesClient = Places.createClient(this);
        mFusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this);
    }

Thêm mã cho trình đơn thanh ứng dụng

Hai phương thức này thêm trình đơn thanh ứng dụng (với một mục duy nhất, biểu tượng Chọn địa điểm) và xử lý việc người dùng nhấp vào biểu tượng.

Hãy sao chép hai phương thức này vào tệp của bạn sau phương thức onCreate.

MapsActivity.java onCreateOptionsMenu() và onOptionsItemSelected()

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu, menu);

        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
           case R.id.action_geolocate:
                
                // COMMENTED OUT UNTIL WE DEFINE THE METHOD
                // Present the current place picker
                // pickCurrentPlace();
                return true;

            default:
                // If we got here, the user's action was not recognized.
                // Invoke the superclass to handle it.
                return super.onOptionsItemSelected(item);

        }
    }

Thử nghiệm

  1. Từ Android Studio, nhấp vào Chạy hoặc Chạy trình đơn > Chạy ‘ứng dụng\39;.

28bea91c68c36fb2.png

  1. Bạn được yêu cầu chọn mục tiêu triển khai. Trình mô phỏng đang chạy sẽ xuất hiện trong danh sách này. Hãy chọn biến thể này và Android Studio sẽ triển khai ứng dụng tới trình mô phỏng cho bạn.

f44658ca91f6f41a.png

Sau vài phút, ứng dụng sẽ chạy. Bạn thấy bản đồ tập trung ở Sydney, Úc, với một nút và danh sách địa điểm chưa xuất hiện.

68eb8c70f4748350.png

Tâm điểm của bản đồ sẽ không di chuyển đến vị trí của người dùng trừ khi bạn yêu cầu quyền truy cập vị trí của thiết bị.

10. Yêu cầu và xử lý quyền truy cập thông tin vị trí

Yêu cầu quyền truy cập thông tin vị trí sau khi bản đồ sẵn sàng

  1. Xác định một phương thức có tên là getLocationPermission yêu cầu quyền của người dùng.

Dán mã này bên dưới phương thức onOptionsSelected mà bạn vừa tạo.

MapsActivity.java getLocationPermission()

    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.
         */
        mLocationPermissionGranted = false;
        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. Thêm hai dòng vào cuối phương thức onMapReady hiện tại để bật các tùy chọn điều khiển thu phóng và yêu cầu người dùng cấp quyền truy cập thông tin vị trí.

MapsActivity.java onMapReady()

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

        // Add a marker in Sydney and move the camera
        LatLng sydney = new LatLng(-34, 151);
        mMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney"));
        mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney));

        //
        // PASTE THE LINES BELOW THIS COMMENT
        //

        // Enable the zoom controls for the map
        mMap.getUiSettings().setZoomControlsEnabled(true);

        // Prompt the user for permission.
        getLocationPermission();

    }

Xử lý kết quả từ quyền được yêu cầu

Khi người dùng trả lời hộp thoại yêu cầu, lệnh gọi lại này sẽ do Android gọi.

Dán mã này sau phương thức getLocationPermission():

MapsActivity.java onRequestPermissionResult()

   /**
     * Handles the result of the request for location permissions
     */
    @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;
                }
            }
        }
    }

11. Tìm vị trí hiện tại và tìm nạp địa điểm có thể

Khi người dùng nhấp vào Chọn địa điểm trong thanh ứng dụng, ứng dụng sẽ gọi phương thức pickCurrentPlace() để gọi phương thức getDeviceLocation() mà bạn đã xác định trước đó. Phương thức getDeviceLocation gọi một phương thức khác, getCurrentPlaceLikelihoods, sau khi truy xuất vị trí mới nhất của thiết bị.

Gọi API FindCurrentPlace và xử lý phản hồi

getCurrentPlaceLikelihoods tạo một findCurrentPlaceRequest và gọi tác vụ API Địa điểm findCurrentPlace. Nếu việc cần làm đó thành công, hệ thống sẽ trả về thông báo findCurrentPlaceResponse chứa danh sách đối tượng placeLikelihood. Mỗi mục trong số này có một số thuộc tính, bao gồm tên và địa chỉ của địa điểm, và xác suất có khả năng bạn đang ở địa điểm đó (giá trị kép từ 0 đến 1). Phương thức này xử lý phản hồi bằng cách tạo danh sách thông tin chi tiết về địa điểm từ placeLikelihoods.

Mã này sẽ lặp lại qua 5 địa điểm có nhiều khả năng nhất và thêm những địa điểm có khả năng thu thập dữ liệu lớn hơn 0 vào danh sách mà sau đó mã này sẽ hiển thị. Nếu bạn muốn hiển thị nhiều hơn hoặc ít hơn 5, hãy chỉnh sửa hằng số M_MAX_ENTRIES.

Dán mã này sau phương thức onMapReady.

MapsActivity.java getCurrentPlaceLikeliturs()

   private void getCurrentPlaceLikelihoods() {
        // Use fields to define the data types to return.
        List<Place.Field> placeFields = Arrays.asList(Place.Field.NAME, Place.Field.ADDRESS,
                Place.Field.LAT_LNG);

        // 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 FindCurrentPlaceRequest request =
                FindCurrentPlaceRequest.builder(placeFields).build();
        Task<FindCurrentPlaceResponse> placeResponse = mPlacesClient.findCurrentPlace(request);
        placeResponse.addOnCompleteListener(this,
                new OnCompleteListener<FindCurrentPlaceResponse>() {
                    @Override
                    public void onComplete(@NonNull Task<FindCurrentPlaceResponse> task) {
                        if (task.isSuccessful()) {
                            FindCurrentPlaceResponse response = task.getResult();
                            // Set the count, handling cases where less than 5 entries are returned.
                            int count;
                            if (response.getPlaceLikelihoods().size() < M_MAX_ENTRIES) {
                                count = response.getPlaceLikelihoods().size();
                            } 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 : response.getPlaceLikelihoods()) {
                                Place currPlace = placeLikelihood.getPlace();
                                mLikelyPlaceNames[i] = currPlace.getName();
                                mLikelyPlaceAddresses[i] = currPlace.getAddress();
                                mLikelyPlaceAttributions[i] = (currPlace.getAttributions() == null) ?
                                        null : TextUtils.join(" ", currPlace.getAttributions());
                                mLikelyPlaceLatLngs[i] = currPlace.getLatLng();

                                String currLatLng = (mLikelyPlaceLatLngs[i] == null) ?
                                        "" : mLikelyPlaceLatLngs[i].toString();

                                Log.i(TAG, String.format("Place " + currPlace.getName()
                                        + " has likelihood: " + placeLikelihood.getLikelihood()
                                        + " at " + currLatLng));

                                i++;
                                if (i > (count - 1)) {
                                    break;
                                }
                            }


                            // COMMENTED OUT UNTIL WE DEFINE THE METHOD
                            // Populate the ListView
                            // fillPlacesList();
                        } else {
                            Exception exception = task.getException();
                            if (exception instanceof ApiException) {
                                ApiException apiException = (ApiException) exception;
                                Log.e(TAG, "Place not found: " + apiException.getStatusCode());
                            }
                        }
                    }
                });
    }

Di chuyển máy ảnh bản đồ đến vị trí hiện tại của thiết bị

Nếu người dùng cấp quyền, ứng dụng sẽ tìm nạp vị trí mới nhất của người dùng và di chuyển máy ảnh để căn giữa vị trí đó.

Nếu người dùng từ chối cấp quyền, ứng dụng sẽ chỉ di chuyển máy ảnh đến vị trí mặc định được xác định giữa các hằng số ở đầu trang này (trong mã mẫu, đó là Sydney, Úc).

Dán mã này sau phương thức getPlaceLikelihoods():

MapsActivity.java getDeviceLocation()

    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<Location> locationResult = mFusedLocationProviderClient.getLastLocation();
                locationResult.addOnCompleteListener(this, new OnCompleteListener<Location>() {
                    @Override
                    public void onComplete(@NonNull Task<Location> task) {
                        if (task.isSuccessful()) {
                            // Set the map's camera position to the current location of the device.
                            mLastKnownLocation = task.getResult();
                            Log.d(TAG, "Latitude: " + mLastKnownLocation.getLatitude());
                            Log.d(TAG, "Longitude: " + mLastKnownLocation.getLongitude());
                            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));
                        }

                       getCurrentPlaceLikelihoods();
                    }
                });
            }
        } catch (SecurityException e)  {
            Log.e("Exception: %s", e.getMessage());
        }
    }

Kiểm tra quyền truy cập thông tin vị trí khi người dùng nhấp vào Chọn địa điểm

Khi người dùng nhấn vào Chọn địa điểm, phương thức này sẽ kiểm tra quyền truy cập thông tin vị trí và nhắc người dùng cấp quyền nếu địa điểm đó chưa được cấp.

Nếu người dùng đã cấp quyền, thì phương thức này sẽ gọi getDeviceLocation để bắt đầu quy trình nhận các địa điểm có khả năng hiện có.

  1. Thêm phương thức này sau getDeviceLocation():

MapsActivity.java pickCurrentPlace()

   private void pickCurrentPlace() {
        if (mMap == null) {
            return;
        }

        if (mLocationPermissionGranted) {
            getDeviceLocation();
        } 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. Giờ đây, pickCurrentPlace đã được xác định, hãy tìm dòng trong onOptionsItemSelected() gọi pickCurrentPlace và hủy nhận xét.

MapsActivity.java onOptionItemSelected()

           case R.id.action_geolocate:

                // COMMENTED OUT UNTIL WE DEFINE THE METHOD
                // Present the Current Place picker
                pickCurrentPlace();
                return true;

Thử nghiệm

Nếu bạn chạy ứng dụng ngay bây giờ và nhấn vào Chọn địa điểm, ứng dụng sẽ nhắc nhắc quyền truy cập thông tin vị trí.

  • Nếu bạn cho phép, tùy chọn đó sẽ được lưu và bạn sẽ không nhận được lời nhắc. Nếu từ chối cấp quyền thì bạn sẽ được nhắc vào lần tiếp theo nhấn vào nút.
  • Mặc dù getPlaceLikelihoods đã tìm nạp những địa điểm hiện có, nhưng ListView chưa hiển thị những địa điểm đó. Trong Android Studio, bạn có thể nhấp vào ⌘6 để kiểm tra nhật ký trong Logcat cho các câu lệnh được gắn thẻ MapsActivity để xác minh rằng các phương pháp mới của bạn đang hoạt động đúng.
  • Nếu bạn đã cấp quyền thì nhật ký bao gồm một câu lệnh cho Latitude: và một câu lệnh cho Longitude: cho biết vị trí phát hiện được của thiết bị. Nếu bạn đã sử dụng Google Maps và trình đơn mở rộng của trình mô phỏng trước đó để chỉ định vị trí cho trình mô phỏng, thì các câu lệnh này sẽ hiển thị vị trí đó.
  • Nếu cuộc gọi đến findCurrentPlace được thực hiện thành công, nhật ký bao gồm 5 tuyên bố in tên và vị trí của 5 địa điểm có nhiều khả năng nhất.

d9896a245b81bf3.png

12. Điền bộ chọn Địa điểm hiện tại

Thiết lập trình xử lý cho các địa điểm đã chọn

Hãy suy nghĩ về những gì chúng ta muốn xảy ra khi người dùng nhấp vào một mục trong ListView. Để xác nhận lựa chọn của người dùng về địa điểm hiện tại của họ, bạn có thể thêm một điểm đánh dấu vào bản đồ tại địa điểm đó. Nếu người dùng nhấp vào điểm đánh dấu đó, một cửa sổ thông tin sẽ bật lên hiển thị tên và địa chỉ của địa điểm.

Dán trình xử lý lượt nhấp này sau phương thức pickCurrentPlace.

MapsActivity.java ListClickedHandler

    private AdapterView.OnItemClickListener listClickedHandler = new AdapterView.OnItemClickListener() {
        public void onItemClick(AdapterView parent, View v, int position, long id) {
            // position will give us the index of which place was selected in the array
            LatLng markerLatLng = mLikelyPlaceLatLngs[position];
            String markerSnippet = mLikelyPlaceAddresses[position];
            if (mLikelyPlaceAttributions[position] != null) {
                markerSnippet = markerSnippet + "\n" + mLikelyPlaceAttributions[position];
            }

            // Add a marker for the selected place, with an info window
            // showing information about that place.
            mMap.addMarker(new MarkerOptions()
                    .title(mLikelyPlaceNames[position])
                    .position(markerLatLng)
                    .snippet(markerSnippet));

           // Position the map's camera at the location of the marker.
            mMap.moveCamera(CameraUpdateFactory.newLatLng(markerLatLng));
        }
    };

Điền Chế độ xem danh sách

Bây giờ, bạn đã có danh sách các địa điểm có nhiều khả năng nhất mà người dùng hiện đang ghé thăm, bạn có thể hiển thị các tùy chọn đó cho người dùng trong ListView. bạn cũng có thể đặt trình xử lý lượt nhấp ListView để sử dụng trình xử lý lượt nhấp mà bạn vừa xác định.

Dán phương thức này sau trình xử lý lượt nhấp:

MapsActivity.javafill PlacesList()

    private void fillPlacesList() {
        // Set up an ArrayAdapter to convert likely places into TextViews to populate the ListView
        ArrayAdapter<String> placesAdapter =
                new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mLikelyPlaceNames);
        lstPlaces.setAdapter(placesAdapter);
        lstPlaces.setOnItemClickListener(listClickedHandler);
    }

Giờ đây, fillPlacesList đã được xác định, hãy tìm đường kẻ ở cuối findPlaceLikelihoods gọi fillPlacesList và hủy nhận xét.

MapsActivity.javafillPlaceLikeliancys()

               // COMMENTED OUT UNTIL WE DEFINE THE METHOD
                // Populate the ListView
                fillPlacesList();

Đó là tất cả mã cần thiết cho bộ chọn Địa điểm hiện tại!

13. Chạy ứng dụng

Thử chọn địa điểm

  1. Chạy lại ứng dụng.

Lần này khi bạn nhấn vào Chọn địa điểm, ứng dụng sẽ điền sẵn các danh sách có địa điểm được đặt tên gần với vị trí. Gần vị trí này trên Maui là những địa điểm như Ululani\39;s Hawaiian Shave Ice và Sugar Beach Bake Shop. Vì có nhiều địa điểm rất gần với tọa độ vị trí, nên đây là danh sách các địa điểm mà bạn có thể ghé thăm.

  1. Nhấp vào tên địa điểm trong ListView.

Bạn sẽ thấy một điểm đánh dấu được thêm vào bản đồ.

  1. Nhấn vào điểm đánh dấu.

Bạn có thể xem Thông tin chi tiết về địa điểm.

e52303cc0de6a513.png 864c74342fb52a01.png

Thử nghiệm một vị trí khác

Nếu bạn muốn thay đổi vị trí của mình và đang sử dụng trình mô phỏng, vị trí thiết bị sẽ không tự động cập nhật khi bạn cập nhật tọa độ vị trí trong trình đơn mở rộng của trình mô phỏng.

Để thực hiện việc này, hãy làm theo các bước sau để sử dụng ứng dụng Google Maps gốc để buộc cập nhật vị trí của trình mô phỏng:

  1. Mở Google Maps.
  2. Nhấn vào ... > Vị trí để thay đổi vĩ độ và kinh độ thành các tọa độ mới, sau đó nhấn vào Gửi.
  3. Ví dụ: bạn có thể sử dụng Vĩ độ: 49.2768 và Kinh độ: -123.1142 để đặt vị trí thành trung tâm thành phố Vancouver, Canada.
  4. Xác minh rằng Google Maps đã căn cứ vào tọa độ mới của bạn. Bạn có thể cần nhấn vào nút Vị trí của tôi trong ứng dụng Google Maps để yêu cầu tính năng gần đây.
  5. Quay lại ứng dụng Địa điểm hiện tại và nhấn vào Chọn địa điểm để tải bản đồ trên tọa độ mới và xem danh sách mới về các địa điểm hiện có.

9adb99d1ce25c184.png

Và vậy là bạn đã xong! Bạn đã tạo một ứng dụng đơn giản để kiểm tra các địa điểm tại vị trí hiện tại và cung cấp cho bạn khả năng địa điểm nào bạn đang ở. Chúc bạn học vui!

Bây giờ, hãy tiếp tục và chạy ứng dụng có các sửa đổi bạn đã thực hiện để hoàn tất bước thưởng này!

14. Các bước tiếp theo

Để ngăn chặn hành vi lấy cắp khóa API, bạn cần bảo mật để chỉ ứng dụng Android của bạn mới có thể sử dụng khóa này. Nếu không bị hạn chế, bất kỳ ai có khóa của bạn có thể sử dụng khóa đó để gọi API Google Maps Platform và khiến bạn bị tính phí.

Nhận chứng chỉ SHA-1 của bạn

Bạn cần vào lúc khác khi hạn chế khoá API. Sau đây là nhóm hướng dẫn về cách lấy chứng chỉ gỡ lỗi.

Đối với Linux hoặc macOS, hãy mở cửa sổ dòng lệnh rồi nhập các thông tin sau:

keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android

Đối với Windows Vista và Windows 7, hãy chạy lệnh sau:

keytool -list -v -keystore "%USERPROFILE%\.android\debug.keystore" -alias androiddebugkey -storepass android -keypass android

Bạn sẽ thấy kết quả xuất ra có dạng như sau:

Alias name: androiddebugkey
Creation date: Jan 01, 2013
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=Android Debug, O=Android, C=US
Issuer: CN=Android Debug, O=Android, C=US
Serial number: 4aa9b300
Valid from: Mon Jan 01 08:04:04 UTC 2013 until: Mon Jan 01 18:04:04 PST 2033
Certificate fingerprints:
     MD5:  AE:9F:95:D0:A6:86:89:BC:A8:70:BA:34:FF:6A:AC:F9
     SHA1: BB:0D:AC:74:D3:21:E1:43:07:71:9B:62:90:AF:A1:66:6E:44:5D:75
     Signature algorithm name: SHA1withRSA
     Version: 3

Dòng bắt đầu SHA1 chứa chứng chỉ SHA-1 của chứng chỉ. Vân tay là chuỗi 20 số thập lục phân có 2 chữ số phân cách bằng dấu hai chấm.

Khi bạn đã sẵn sàng phát hành ứng dụng, hãy sử dụng hướng dẫn trong tài liệu này để truy xuất chứng chỉ phát hành của bạn.

Thêm các quy định hạn chế cho khoá API

  1. Trong Cloud Console, hãy chuyển đến API & Dịch vụ > Thông tin xác thực.

Khóa mà bạn đã sử dụng cho ứng dụng này phải được liệt kê trong Khóa API.

  1. Hãy nhấp vào 6454a04865d551e6.png để chỉnh sửa các chế độ cài đặt quan trọng.

316b052c621ee91c.png

  1. Trên trang khóa API, sau khi Hạn chế khóa, hãy đặt Hạn chế ứng dụng bằng cách làm như sau:
  2. Chọn Ứng dụng Android rồi làm theo hướng dẫn.
  3. Nhấp vào Thêm mặt hàng.
  4. Nhập tên gói và vân tay số của chứng chỉ SHA-1 (truy xuất trong phần trước).

Ví dụ:

com.google.codelab.currentplace
BB:0D:AC:74:D3:21:E1:43:07:71:9B:62:90:AF:A1:66:6E:44:5D:75s
  1. Để tăng cường bảo vệ, hãy đặt các quy định hạn chế về API bằng cách làm như sau.
  2. Sau khi hạn chế API, hãy chọn Hạn chế khoá.
  3. Chọn SDK Maps dành cho Android và API Địa điểm.
  4. Nhấp vào Xong rồi Lưu.

15. Xin chúc mừng

Bạn đã tạo một ứng dụng đơn giản để kiểm tra các địa điểm có nhiều khả năng nhất tại vị trí hiện tại và thêm điểm đánh dấu vào bản đồ cho địa điểm mà người dùng chọn.

Tìm hiểu thêm