Android的口罩地圖App–使用Webkit網頁

李政輝 C.H. Lee

  • 精誠資訊/恆逸教育訓練中心-資深講師
  • 技術分類:Mobile行動應用開發

 

 

最近由於COVID-19疫情,大家出門都口罩不離身,所以寫個口罩地圖程式顯示現在各位置口罩剩餘數量,也是最近老師們最常使用的程式範例。在這個文章中,我將製作一個Android APP來顯示口罩地圖。
技術主題:

  1. 建立一個網頁使用GOOGLE Maps JavaScript API
  2. 透過XMLHttpRequest執行AJAX非同步作業存取口罩地圖API
  3. 將每一個口罩購買點及口罩資訊轉成GOOGLE Maps Marker物件呈現在地圖上
  4. 透過Android WebKit使用口罩地圖網頁
  5. 透過Google Play Services取出定位資訊並傳給WebKit網頁顯示口罩地圖

1.建立一個網頁使用GOOGLE Maps JavaScript API
<script async
    src="https://maps.googleapis.com/maps/api/js?key=您申請的API金鑰&callback=initMap">
</script>
  • 網頁MaskMap.html完整程式碼如下:
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Google Maps</title>
  <!--Google Map Javascripy API-->
  <script async src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap"></script>
    <script>
            var map;
            function initialize()
            {
                var lat = 25.0525;//緯度
                var lon = 121.544;//經度

                var latlon = new google.maps.LatLng(lat,lon);
                var mapOptions =
                {
                        zoom: 15,//比例尺
                        mapTypeId: google.maps.MapTypeId.ROADMAP,//街道地圖
                        center: latlon//地圖中心點
                };
                //建立Map物件顯示地圖在網頁指定位置
                map = new google.maps.Map(document.getElementById('map'),mapOptions);
            } 
    </script>
</head>
  • 執行結果如下:

2.透過XMLHttpRequest執行AJAX非同步作業存取口罩地圖API
{
    "type": "FeatureCollection",
    "features": [
        {
                "type": "Feature",
                "properties": {
                    "id": "5937251123",
                    "name": "上明藥局",
                    "phone": "(04)8970600",
                    "address": "彰化縣竹塘鄉竹塘村竹塘街66號",
                    "mask_adult": 6710,
                    "mask_child": 1900,
                    "updated": "2021\/06\/07 09:47:24",
                    "available": "星期一上午看診、星期二上午看診、星期三上午看診、星期四上午看診、星期五上午休診、星期六上午看診、星期日上午看診、星期一下午看診、星期二下午看診、星期三下午看診、星期四下午看診、星期五下午休診、星期六下午看診、星期日下午休診、星期一晚上休診、星期二晚上休診、星期三晚上休診、星期四晚上休診、星期五晚上休診、星期六晚上休診、星期日晚上休診",
                    "note": "-",
                    "custom_note": "",
                    "website": "",
                    "county": "彰化縣",
                    "town": "竹塘鄉",
                    "cunli": "竹塘村",
                    "service_periods": "NNNNYNNNNNNYNYYYYYYYY"
                },
                "geometry": {
                    "type": "Point",
                    "coordinates": [
                        120.42594,
                        23.860806
                    ]
                }
            },
…….
  • 透過XMLHttpRequest執行AJAX非同步作業
//建立XMLHttpRequest物件
var request = new XMLHttpRequest();
function getJSONData() {
    var json_url = 'https://raw.githubusercontent.com/kiang/pharmacies/master/json/points.json?fbclid=IwAR0oowBRjj1goAMqtnugBiXMTMY8OCl14TGmgt3YDJi9w5BXs4VsfZQ9mDI';
    //非同步ASYNC作業觸發readystatechange事件
    request.addEventListener("readystatechange", dotheupdate, false);
    request.open("GET", json_url, true);//true代表非同步ASYNC作業
    request.send();
}
function dotheupdate() {
    if (request.readyState == 4) {//4代表ASYNC作業完成
        //透過JSON.parse反序列化函數將responseText的JSON字串還原成JS物件
        var obj = JSON.parse(request.responseText);
        console.log(obj.features);
        var data = obj.features;
        for (var i = 0; i < data.length; i++) {
            console.log('lon='+data[i].geometry.coordinates[0])
            console.log('lat='+data[i].geometry.coordinates[1])
        }
    }
}
  • 網頁MaskMap.html完整程式碼如下
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Google Maps</title>
<!--Google Map Javascripy API-->
<script async src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBYPvYj5pPwqFFLlS1gLhI-SZPlBGtaFf4&callb
ack=initMap"></script>
<script>
    var map;
    //建立XMLHttpRequest物件
    var request = new XMLHttpRequest();
    function initialize()
    {
        var lat = 25.0525;//緯度
        var lon = 121.544;//經度

        var latlon = new google.maps.LatLng(lat,lon);
        var mapOptions =
        {
                zoom: 15,//比例尺
                mapTypeId: google.maps.MapTypeId.ROADMAP,//街道地圖
                center: latlon//地圖中心點
        };
        //建立Map物件顯示地圖在網頁指定位置
        map = new google.maps.Map(document.getElementById('map'),mapOptions);
        getJSONData()
    } 
    function getJSONData() {
        var json_url = 'https://raw.githubusercontent.com/kiang/pharmacies/master/json/points.json?fbclid=IwAR0oowBRjj1goAMqtnugBiXMTMY8OCl14TGmgt3YDJi9w5BXs4VsfZQ9mDI';
        //非同步ASYNC作業觸發readystatechange事件
        request.addEventListener("readystatechange", dotheupdate, false);
        request.open("GET", json_url, true);//true代表非同步ASYNC作業
        request.send();

    }
    function dotheupdate() {
        if (request.readyState == 4) {//4代表ASYNC作業完成
            //透過JSON.parse反序列化函數將responseText的JSON字串還原成JS物件
            var obj = JSON.parse(request.responseText);
            console.log(obj.features);
            var data = obj.features;
            for (var i = 0; i < data.length; i++) {
            var lon= data[i].geometry.coordinates[0];
            var lat= data[i].geometry.coordinates[1];
            console.log('lon='+lon)
            console.log('lat='+lat)
            var msg = `<fieldset>
                        <legend>${data[i].properties.name}</legend>
                        <div>電話:${data[i].properties.phone}</div>
                        <div>地址:${data[i].properties.address}</div>
                        <div>成人口罩:${data[i].properties.mask_adult}</div>
                        <div>孩童口罩:${data[i].properties.mask_child}</div>
                        </fieldset>
                        `
                console.log(msg);
            }
        }
    }
</script>
</head>
    
<body onload="initialize()">
    <!--顯示地圖-->
    <div id="map" style="width: device-width; height: 800px;"></div>
</body>
</html>
  • 執行結果如下:

3. 將每一個口罩購買點及口罩資訊轉成GOOGLE Maps Marker物件呈現在地圖上
  • 替每個藥局產生GOOGLE Maps Marker物件及InfoWindow物件
function getMarker(lon, lat, title, message) {
    //建立Google Map Marker物件(預設為紅色水滴)
    var marker = new google.maps.Marker(
                                  { position: { lat: lat, lng: lon },//經緯度
                                    title: title });//title顯示藥局名稱
    //建立Google Map InfoWindow物件
    var infowindow = new google.maps.InfoWindow({ content: message });
    //在Marker物件點擊觸發InfoWindow視窗
    marker.addListener("click", function() { infowindow.open(map, marker)});
       //在Map地圖產生Marker物件
       marker.setMap(map);
    }
}           
  • 網頁MaskMap.html完整程式碼
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Google Maps</title>
<!--Google Map Javascripy API-->
<script async src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBYPvYj5pPwqFFLlS1gLhI-SZPlBGtaFf4&callback=initMap"></script>
<script>
    var map;
    //建立XMLHttpRequest物件
    var request = new XMLHttpRequest();
    function initialize()
    {
        var lat = 25.0525;//緯度
        var lon = 121.544;//經度

        var latlon = new google.maps.LatLng(lat,lon);
        var mapOptions =
        {
                zoom: 15,//比例尺
                mapTypeId: google.maps.MapTypeId.ROADMAP,//街道地圖
                center: latlon//地圖中心點
        };
        //建立Map物件顯示地圖在網頁指定位置
        map = new google.maps.Map(document.getElementById('map'),mapOptions);
        getJSONData()
    } 
    function getJSONData() {
        var json_url = 'https://raw.githubusercontent.com/kiang/pharmacies/master/json/points.json?fbclid=IwAR0oowBRjj1goAMqtnugBiXMTMY8OCl14TGmgt3YDJi9w5BXs4VsfZQ9mDI';
        //非同步ASYNC作業觸發readystatechange事件
        request.addEventListener("readystatechange", dotheupdate, false);
        request.open("GET", json_url, true);//true代表非同步ASYNC作業
        request.send();

    }
    function dotheupdate() {
        if (request.readyState == 4) {//4代表ASYNC作業完成
            //透過JSON.parse反序列化函數將responseText的JSON字串還原成JS物件
            var obj = JSON.parse(request.responseText);
            console.log(obj.features);
            var data = obj.features;
            for (var i = 0; i < data.length; i++) {
            var lon= data[i].geometry.coordinates[0];
            var lat= data[i].geometry.coordinates[1];
            //console.log('lon='+lon)
            //console.log('lat='+lat)
            var msg = `<fieldset>
                        <legend>${data[i].properties.name}</legend>
                        <div>電話:${data[i].properties.phone}</div>
                        <div>地址:${data[i].properties.address}</div>
                        <div>成人口罩:${data[i].properties.mask_adult}</div>
                        <div>孩童口罩:${data[i].properties.mask_child}</div>
                        </fieldset>
                        `
                //console.log(msg);
                //替每個藥局產生GOOGLE Maps Marker物件及InfoWindow物件
                getMarker(lon, lat, data[i].properties.name, msg);
            }
        }
    }
    function getMarker(lon, lat, title, message) {
        //建立Google Map Marker物件(預設為紅色水滴)
        var marker = new google.maps.Marker(
    { position: { lat: lat, lng: lon },//經緯度
                                    title: title });//title顯示藥局名稱
        //建立Google Map InfoWindow物件
        var infowindow = new google.maps.InfoWindow({ content: message });
        //在Marker物件點擊觸發InfoWindow視窗
        marker.addListener("click", function() { infowindow.open(map, marker)});
        //在Map地圖產生Marker物件
        marker.setMap(map);
    }
</script>
</head>
        
<body onload="initialize()">
    <!--顯示地圖-->
    <div id="map" style="width: device-width; height: 800px;"></div>
</body>
</html>
  • 執行結果如下:


4.透過Android WebKit使用口罩地圖網頁
  • 在Android 新增一個Empty Activity專案
  • Android專案結構
  • 在專案加入Assets資料夾(按右鍵🠖New🠖folder🠖Assets Folder)
  • 將Maskmap.html複製貼上至Assets資料夾(按右鍵🠖Paste)
  • 修改Maskmap.html 將經緯度改由原生Layout EditText提供
function initialize()
    {
    
        //var lat = 25.0525;//緯度
        //var lon = 121.544;//經度
        var lat = AndroidFunction.GetLat();
        var lon = AndroidFunction.GetLon();
…..
  • 在res🠖layout資料夾新增一個Layout Resouce File: map.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 
xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <WebView
        android:id="@+id/webviewmap"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <ImageView
        android:id="@+id/imageviemap"
        android:layout_width="70dp"
        android:layout_height="70dp"
        android:layout_alignRight="@id/webviewmap"
        android:src="@drawable/cancel" />

</RelativeLayout>
  • 修改activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 
xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <TextView
        android:id="@+id/textViewLat"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="31dp"
        android:layout_marginTop="90dp"
        android:text="緯度"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <TextView
        android:id="@+id/textViewLon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="31dp"
        android:layout_marginTop="140dp"
        android:text="經度"
        android:textAppearance="?android:attr/textAppearanceLarge" />
    <TextView
        android:id="@+id/textViewAddress"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="31dp"
        android:layout_marginTop="190dp"
        android:text="地址"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <EditText
        android:id="@+id/editTextLat"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="90dp"
        android:layout_marginTop="90dp" >
    </EditText>

    <EditText
        android:id="@+id/editTextLon"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="90dp"
        android:layout_marginTop="140dp" >
    </EditText>

    <EditText
        android:id="@+id/editTextAddress"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="90dp"
        android:layout_marginTop="190dp" >
    </EditText>

    <Button
        android:id="@+id/buttonmap"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignRight="@+id/editTextLon"
        android:layout_centerVertical="true"
        android:text="Google Map" />

</RelativeLayout>        
  • 將cancel.jpg複製貼上至res🠖drawable資料夾(按右鍵🠖Paste)
  • 修改ActivityMain.java
function initialize()
    {
    
       //var lat = 25.0525;//緯度
       //var lon = 121.544;//經度
       var lat = AndroidFunction.GetLat();
       var lon = AndroidFunction.GetLon();
…..
  • 在res🠖layout資料夾新增一個Layout Resouce File: map.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 
xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:orientation="vertical" >
    
   <WebView
   android:id="@+id/webviewmap"
   android:layout_width="match_parent"
   android:layout_height="match_parent" />
    
   <ImageView
   android:id="@+id/imageviemap"
   android:layout_width="70dp"
   android:layout_height="70dp"
   android:layout_alignRight="@id/webviewmap"
   android:src="@drawable/cancel" />
    
</RelativeLayout>
  • 修改activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <TextView
        android:id="@+id/textViewLat"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="31dp"
        android:layout_marginTop="90dp"
        android:text="緯度"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <TextView
        android:id="@+id/textViewLon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="31dp"
        android:layout_marginTop="140dp"
        android:text="經度"
        android:textAppearance="?android:attr/textAppearanceLarge" />
    <TextView
        android:id="@+id/textViewAddress"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="31dp"
        android:layout_marginTop="190dp"
        android:text="地址"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <EditText
        android:id="@+id/editTextLat"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="90dp"
        android:layout_marginTop="90dp" >
    </EditText>

    <EditText
        android:id="@+id/editTextLon"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="90dp"
        android:layout_marginTop="140dp" >
    </EditText>

    <EditText
        android:id="@+id/editTextAddress"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="90dp"
        android:layout_marginTop="190dp" >
    </EditText>

    <Button
        android:id="@+id/buttonmap"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignRight="@+id/editTextLon"
        android:layout_centerVertical="true"
        android:text="Google Map" />
    
</RelativeLayout>
  • 將cancel.jpg複製貼上至res🠖drawable資料夾(按右鍵🠖Paste)
  • 修改ActivityMain.java
package com.example.maskmap;

import androidx.appcompat.app.AppCompatActivity;

import android.app.AlertDialog;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.webkit.JavascriptInterface;
import android.webkit.WebView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
    
public class MainActivity extends AppCompatActivity {

    EditText latedittext;
    EditText lonedittext;
    EditText addressedittext;
    private String Lat;
    private String Lon;
    private AlertDialog alerdialog;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button MapButton = (Button) findViewById(R.id.buttonmap);
        MapButton.setOnClickListener(Click);
    }


    @JavascriptInterface
    public String GetLat()
    {
        return Lat;
    }

    @JavascriptInterface
    public String GetLon()
    {
        return Lon;
    }


    View.OnClickListener Click= new View.OnClickListener ()
    {
        @Override
        public void onClick(View view)
        {
            switch(view.getId())
            {
                case R.id.buttonmap:
                    latedittext = (EditText) findViewById(R.id.editTextLat);
                    lonedittext = (EditText) findViewById(R.id.editTextLon);

                    Lat = latedittext.getText().toString();
                    Lon = lonedittext.getText().toString();

                    if (Lat != null && Lon != null)
                    {
                        AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                        LayoutInflater inflater = LayoutInflater.from(MainActivity.this);
                        View mapview = inflater.inflate(R.layout.map,null);
                        builder.setView(mapview);

                        ImageView imageview = (ImageView)mapview.findViewById(R.id.imageviemap);
                        imageview.setOnClickListener(Click);

                        String mapurl = "file:///android_asset/MaskMap.html";

                        WebView webview = (WebView) mapview.findViewById(R.id.webviewmap);
                        webview.getSettings().setJavaScriptEnabled(true);
                        webview.addJavascriptInterface(MainActivity.this , "AndroidFunction");
                        webview.loadUrl(mapurl);
                        alerdialog = builder.show();
                    }
                    break;
                case R.id.imageviemap:
                    alerdialog.cancel();
                    break;
            }
        }
    };
}
  • 修改manifests🠖AndroidManifest.xml 加入Internet使用權限
<uses-permission android:name="android.permission.INTERNET" />
  • 執行結果如下:


5.透過Google Play Services取出定位資訊並傳給WebKit網頁顯示口罩地圖
  • 安裝Google Play Services取得定位(在Gradle Scriptsbuild.gradle(Module.MaskMap.app)檔案加入以下內容)
dependencies {
…
       
       implementation 'com.google.android.gms:play-services:12.0.1'
     
…
}
  • 按一下 工具列 的 Sync Project with Gradle Files下載元件
  • 將以下權限添加到AndroidManifest.xml
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
  • 在Gradle Scripts🠖gradle.properties(ProjectProperties)檔案加入以下內容以避免Duplicate class xxx found in modules錯誤
android.enableJetifier=true
  • 修改ActivityMain.java 加入定位取得地址
package com.example.maskmap;

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

import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.content.IntentSender;
import android.content.pm.PackageManager;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.webkit.JavascriptInterface;
import android.webkit.WebView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Toast;

import com.google.android.gms.common.api.ResolvableApiException;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationAvailability;
import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.location.LocationSettingsRequest;
import com.google.android.gms.location.LocationSettingsResponse;
import com.google.android.gms.location.SettingsClient;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task;

import java.io.IOException;
import java.util.List;
    
public class MainActivity extends AppCompatActivity {


    private static final String TAG = "LocationActivity";

    FusedLocationProviderClient fusedClient;
    private LocationRequest mRequest;
    private LocationCallback mCallback;
    EditText latedittext;
    EditText lonedittext;
    EditText addressedittext;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button MapButton = (Button) findViewById(R.id.buttonmap);
        MapButton.setOnClickListener(Click);


        mCallback = new LocationCallback() {
            //onLocationResult callback用於取得 "streaming"位置更新的地方. 
            //我們可以用來確定是否檢查諸如準確性之類的事情
            //這個最新的更新應該取代我們之前的estimate估計.
            @Override
            public void onLocationResult(LocationResult locationResult) {
                if (locationResult == null) {
                    Log.d(TAG, "locationResult null");
                    return;
                }

                for (Location loc : locationResult.getLocations()) {
                    latedittext = (EditText) findViewById(R.id.editTextLat);
                    lonedittext = (EditText) findViewById(R.id.editTextLon);
                    latedittext.setText(String.format("%g", loc.getLatitude()));
                    lonedittext.setText(String.format("%g", loc.getLongitude()));
                }
            }

            @Override
            public void onLocationAvailability(LocationAvailability locationAvailability) {
                Log.d(TAG, "locationAvailability is " + locationAvailability.isLocationAvailable());
                super.onLocationAvailability(locationAvailability);
            }
        };

        //定位權限
        if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {
            //請求權限permission.
            //但是檢查我們是否需要先顯示一個請求權限的UI
            if (ActivityCompat.shouldShowRequestPermissionRationale(this, android.Manifest.permission.ACCESS_COARSE_LOCATION)) {
                showRationale();
            } else {
                ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION,
                        android.Manifest.permission.ACCESS_NETWORK_STATE}, 2);
            }
        } else {
            //我們已經有權限了,開始定位
            locationWizardry();
        }

    }

    @SuppressLint("MissingPermission")
    private void locationWizardry() {
        fusedClient = LocationServices.getFusedLocationProviderClient(this);
        //最初,獲取最後一個已知位置。我們可以稍後不斷重新取得新的定位
        fusedClient.getLastLocation().addOnSuccessListener(new OnSuccessListener() {
            @Override
            public void onSuccess(Location location) {
                if (location != null) {
                    latedittext = (EditText) findViewById(R.id.editTextLat);
                    lonedittext = (EditText) findViewById(R.id.editTextLon);

                    latedittext.setText(String.format("%g", location.getLatitude()));
                    lonedittext.setText(String.format("%g", location.getLongitude()));

                    //使用Geocoder物件透過定位的經緯度反查地址
                    addressedittext = (EditText) findViewById(R.id.editTextAddress);
                    Geocoder geocoder = new Geocoder(MainActivity.this);
                    List
addresses = null; try { addresses = geocoder.getFromLocation(location.getLatitude(), location.getLongitude(), 1); //放入座標 } catch (IOException e) { e.printStackTrace(); } if (addresses != null && addresses.size() > 0) { Address address = addresses.get(0); String addressText = String.format("%s-%s%s%s%s", address.getCountryName(), //國家 address.getAdminArea(), //城市 address.getLocality(), //區 address.getThoroughfare(), //路 address.getSubThoroughfare() //巷號 ); addressedittext.setText(addressText); } } } }); //現在用於接收不斷的位置更新: createLocRequest(); LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder() .addLocationRequest(mRequest); //這會檢查 GPS 模式(高精度、省電、僅限設備)是否設置了適當的“mRequest”。 //如果當前設置不能滿足mRequest(Google Fused Location Provider 自動確定),然後 //我們偵聽失敗並顯示一個對話框供用戶輕鬆更改這些設置。 SettingsClient client = LocationServices.getSettingsClient(MainActivity.this); Task task = client.checkLocationSettings(builder.build()); task.addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { if (e instanceof ResolvableApiException) { //通過向用戶顯示一個對話框。位置設置不滿足,但是可以修復。 try { //通過調用 startResolutionForResult() 顯示對話框, //並在 onActivityResult() 中檢查結果。 ResolvableApiException resolvable = (ResolvableApiException) e; resolvable.startResolutionForResult(MainActivity.this, 500); } catch (IntentSender.SendIntentException sendEx) { //忽略錯誤。 } } } }); } //實際上開始監聽更新:使用 Resume(),可以便我們從 onPause中回復監聽 @Override protected void onResume() { super.onResume(); startLocationUpdates(); } @Override protected void onPause() { super.onPause(); fusedClient.removeLocationUpdates(mCallback); } @SuppressLint("MissingPermission") protected void startLocationUpdates() { fusedClient.requestLocationUpdates(mRequest, mCallback, null); } @SuppressLint("RestrictedApi") protected void createLocRequest() { mRequest = new LocationRequest(); mRequest.setInterval(10000);//以毫秒為單位的時間;每 10 秒 mRequest.setFastestInterval(5000); mRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); } @SuppressLint("MissingSuperCall") @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode) { case 2: { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { Toast.makeText(this, "謝謝您!", Toast.LENGTH_SHORT).show(); locationWizardry(); } else { Toast.makeText(this, "來吧各位,我們真的需要這個!", Toast.LENGTH_SHORT).show(); } } break; default: break; } } private void showRationale() { AlertDialog dialog = new AlertDialog.Builder(this).setMessage("我們需要這個並授予我們" + "許可權限 :)").setPositiveButton("確定", (dialogInterface, i) -> { ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.ACCESS_COARSE_LOCATION}, 2); dialogInterface.dismiss(); }) .create(); dialog.show(); } @JavascriptInterface public String GetLat() { return Lat; } @JavascriptInterface public String GetLon() { return Lon; } private String Lat; private String Lon; private AlertDialog alerdialog; View.OnClickListener Click= new View.OnClickListener () { @Override public void onClick(View view) { switch(view.getId()) { case R.id.buttonmap: latedittext = (EditText) findViewById(R.id.editTextLat); lonedittext = (EditText) findViewById(R.id.editTextLon); Lat = latedittext.getText().toString(); Lon = lonedittext.getText().toString(); if (Lat != null && Lon != null) { AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); LayoutInflater inflater = LayoutInflater.from(MainActivity.this); View mapview = inflater.inflate(R.layout.map,null); builder.setView(mapview); ImageView imageview = (ImageView)mapview.findViewById(R.id.imageviemap); imageview.setOnClickListener(Click); String mapurl = "file:///android_asset/MaskMap.html"; WebView webview = (WebView) mapview.findViewById(R.id.webviewmap); webview.getSettings().setJavaScriptEnabled(true); webview.addJavascriptInterface(MainActivity.this , "AndroidFunction"); webview.loadUrl(mapurl); alerdialog = builder.show(); } break; case R.id.imageviemap: alerdialog.cancel(); break; } } }; }
  • 執行結果如下:

完整程式碼


您可在下列課程中了解更多技巧喔!