iT邦幫忙

2023 iThome 鐵人賽

DAY 7
0
Mobile Development

[Android Studio & Spring boot 30天挑戰]系列 第 7

[Android Studio & Spring boot 30天挑戰] Day07- 離線地圖與線上地圖(上)

  • 分享至 

  • xImage
  •  

今天要介紹的是地圖功能,這個功能提供了離線地圖和線上地圖兩種功能。離線地圖功能允許使用者下載地圖資料,並在沒有網路連接的情況下使用,這對於在偏遠地區或網路信號較弱的地方使用地圖非常有用。而線上地圖功能允許使用者即時連接到網路,使用地圖服務提供商的資源和功能,來找尋到附近的資源或醫療設施等,來獲取進一步的幫助。

流程圖

https://ithelp.ithome.com.tw/upload/images/20230819/20150369AacEFM5YDb.png

UI畫面

https://ithelp.ithome.com.tw/upload/images/20230819/20150369PtE1KmZOvg.pnghttps://ithelp.ithome.com.tw/upload/images/20230819/20150369wZzsWONPQQ.png

程式碼

首先先講一下大概是怎麼設計的,這邊我使用了 Fragment 來呈現線上與離線地圖,要切換地圖時只要按下,下面兩按鈕就可切換。

   <androidx.constraintlayout.widget.ConstraintLayout 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"
    tools:context=".ui.map.MapsActivity"
    android:background="@color/theme">

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/offline_map_toolbar"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
        app:layout_constraintBottom_toTopOf="@+id/offline_map_guideline1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/offline_map_guideline1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.06" />


    <FrameLayout
        android:id="@+id/map_frameLayout"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@+id/linearLayout8"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/offline_map_toolbar">

    </FrameLayout>

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.93" />

    <LinearLayout
        android:id="@+id/linearLayout8"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:orientation="horizontal"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@+id/guideline2">

        <Button
            android:id="@+id/online_map_button"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_margin="5dp"
            android:layout_weight="1"
            android:background="@drawable/custom_button"
            android:fontFamily="@font/fonts"
            android:textSize="16sp"
            android:textStyle="bold"
            android:text="地圖"/>

        <Button
            android:id="@+id/offline_map_button"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_margin="5dp"
            android:layout_weight="1"
            android:textColor="@color/black"
            android:background="@drawable/custom_button"
            android:fontFamily="@font/fonts"
            android:textSize="16sp"
            android:text="離線地圖"
            android:textStyle="bold"/>
    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

接下來是 Activity,首先是切換按鈕的寫法。

下面的「檢查離線地圖文件是否存在」是到手機裡的 Download 取找有沒有下載。

private void offlineMapClicked(View view) {
file = new File(Environment.getExternalStorageDirectory().getPath()+"/Download", "taiwan.mbtiles");
    // 檢查離線地圖文件是否存在
    if (!file.exists()) {
        // 如果文件不存在,顯示警告對話框,提示用戶下載地圖
        AlertDialog.Builder dialog = new AlertDialog.Builder(this);
        dialog.setPositiveButton("下載", downloadClicked) // 定義「下載」按鈕的點擊操作
              .setNegativeButton("取消", cancelClicked) // 定義「取消」按鈕的點擊操作
              .setMessage("請先下載離線地圖!!") // 設置對話框消息
              .show(); // 顯示警告對話框
    } else {
        // 如果離線地圖文件存在
        if (offlineMapFragment == null) {
            // 如果離線地圖片段尚未初始化,則初始化它
            offlineMapFragment = new OfflineMapFragment(this);
        }
        // 將離線地圖片段替換到指定的佈局容器中
        getSupportFragmentManager().beginTransaction().replace(R.id.map_frameLayout, offlineMapFragment).commit();
    }
}

接下來就是下載離線地圖的程式碼,這裡是用 DownloadManager 去下載台灣通用離線地圖,這裡的出發方法就是上方 dialog 跳出時按下下載。

private DialogInterface.OnClickListener downloadClicked = new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
        // 定義要下載的地圖文件的 URI
        Uri uri = Uri.parse("https://data.moi.gov.tw/MoiOD/System/DownloadFile.aspx?DATA=4BE02238-E336-4F91-91F3-869FBA4CF4DB");

        // 獲取下載管理器(DownloadManager)
        DownloadManager downloadManager = (DownloadManager) MapsActivity.this.getSystemService(DOWNLOAD_SERVICE);

        // 創建下載請求(DownloadManager.Request)
        DownloadManager.Request request = new DownloadManager.Request(uri);

        // 設定下載請求的一些屬性
        request.setAllowedOverRoaming(true); // 允許在漫遊時下載
        request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); // 顯示下載通知
        request.setTitle("TaiwanMapDownload"); // 設定通知標題
        request.setDestinationUri(Uri.fromFile(file)); // 設定下載文件的保存位置

        // 將下載請求加入到下載隊列並獲取下載任務的 ID
        long download_id = downloadManager.enqueue(request);

        // 創建用於查詢下載狀態的查詢對象(DownloadManager.Query)
        DownloadManager.Query query = new DownloadManager.Query();
        query.setFilterById(download_id);

        // 創建下載完成時的廣播接收器,以便在下載完成後執行相應的操作
        IntentFilter filter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
        DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
        receiver.getIdAndFile(download_id, file); // 傳遞下載 ID 和文件
        registerReceiver(receiver, filter); // 註冊廣播接收器

        // 創建一個線程用於監視下載進度
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                boolean downloading = true;
                while (downloading) {
                    // 創建新的查詢對象以查詢下載狀態
                    DownloadManager.Query query = new DownloadManager.Query();
                    query.setFilterById(download_id);
                    Cursor cursor = downloadManager.query(query);

                    if (cursor != null && cursor.moveToFirst()) {
                        int downloadedBytesIndex = cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR);
                        int totalBytesIndex = cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES);

                        // 獲取已下載和總大小的字節數
                        long downloadedBytes = cursor.getLong(downloadedBytesIndex);
                        long totalBytes = cursor.getLong(totalBytesIndex);

                        // 計算下載進度
                        int progress = (int) ((downloadedBytes * 100) / totalBytes);

                        // 在 UI 線程上更新進度按鈕的文本和禁用按鈕
                        activity.runOnUiThread(() -> {
                            offline_map_button.setText(progress + "%");
                            offline_map_button.setEnabled(false);
                        });

                        int statusIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
                        int status = cursor.getInt(statusIndex);

                        // 如果下載成功,設置標誌以結束循環
                        if (status == DownloadManager.STATUS_SUCCESSFUL) {
                            downloading = false;
                        }
                    } else {
                        downloading = false;
                        // 在 UI 線程上啟用按鈕
                        activity.runOnUiThread(() -> {
                            offline_map_button.setEnabled(true);
                        });
                    }

                    if (cursor != null) {
                        cursor.close();
                    }

                    try {
                        // 每隔一秒更新進度
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                // 下載完成後,恢復按鈕的文本
                activity.runOnUiThread(() -> {
                    offline_map_button.setText("離線地圖");
                });
            }
        });
        thread.start(); // 開始監視下載進度的線程
    }
};

這就是今天介紹的部分,明天會繼續介紹地圖的使用~!!!


上一篇
[Android Studio & Spring boot 30天挑戰] Day06 - 指南針
下一篇
[Android Studio & Spring boot 30天挑戰] Day08- 離線地圖與線上地圖(中)
系列文
[Android Studio & Spring boot 30天挑戰]30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言