管灌系统巡查员智能手机App
zuoxiao
2024-09-09 f163344ffeb1e699fa1a2dfb8a70055bb1b9be7e
首页界面优化,地图模块优化,部分下载地图瓦片功能代码
16个文件已修改
26个文件已添加
1425 ■■■■ 已修改文件
app/build.gradle 35 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/AndroidManifest.xml 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/assets/img/location.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/assets/img/marker_black.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/assets/img/marker_red.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/assets/index.html 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/assets/js/map.js 243 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/dayu/pipirrapp/MyApplication.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/dayu/pipirrapp/activity/MainActivity.java 105 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/dayu/pipirrapp/fragment/MapFragment.java 165 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/dayu/pipirrapp/js/MyWebViewInterface.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/dayu/pipirrapp/net/ApiManager.java 64 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/dayu/pipirrapp/net/ApiService.java 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/dayu/pipirrapp/service/MyCommonService.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/dayu/pipirrapp/service/MyLocationService.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/dayu/pipirrapp/service/PushService.java 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/dayu/pipirrapp/tool/RocketMQConsumer.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/dayu/pipirrapp/utils/MapJpgUtils.java 150 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/dayu/pipirrapp/utils/WebViewUtils.java 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/bottom_circle_bg.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/bottom_map_black.xml 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/bottom_map_white.xml 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/bottom_my_black.xml 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/bottom_my_white.xml 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/bottom_order_black.xml 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/bottom_order_white.xml 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/vector_adjust.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/vector_btn_date.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/vector_close.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/vector_distance.xml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/vector_list.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/vector_location.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/vector_location_clock.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/vector_loop.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/vector_photo_camera.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/vector_timer.xml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/layout/activity_main.xml 140 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/layout/fragment_map.xml 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/layout/top_title.xml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/menu/main_bottom_nav_menu.xml 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/values/colors.xml 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/values/dimens.xml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/build.gradle
@@ -67,6 +67,18 @@
    viewBinding {
        enabled = true;
    }
    packagingOptions {
        exclude 'META-INF/DEPENDENCIES'
        // 如果你还遇到其他重复的META-INF文件,也可以添加类似的排除规则
        exclude 'META-INF/LICENSE'
        exclude 'META-INF/LICENSE.txt'
        exclude 'META-INF/NOTICE'
        exclude 'META-INF/NOTICE.txt'
        // RocketMQ特有的排除项(如果需要)
        exclude 'META-INF/INDEX.LIST'
        exclude 'META-INF/spring.factories'
        exclude 'META-INF/spring.handlers'
    }
}
@@ -115,31 +127,31 @@
//    //推送相关
//    // 此处以JPush 5.0.0 版本为例,5.0.0 版本开始可以自动拉取 JCore 包,无需另外配置
    implementation 'cn.jiguang.sdk:jpush:5.2.2'
//    implementation 'cn.jiguang.sdk:jpush:5.2.2'
//    //若不集成厂商通道,可直接跳过以下依赖
//    // 极光厂商插件版本与接入 JPush 版本保持一致,下同
//    // 接入华为厂商
    implementation 'com.huawei.hms:push:6.5.0.300'
    implementation 'cn.jiguang.sdk.plugin:huawei:5.2.2'
//    implementation 'com.huawei.hms:push:6.5.0.300'
//    implementation 'cn.jiguang.sdk.plugin:huawei:5.2.2'
//    // 接入 FCM 厂商
//    implementation 'com.google.firebase:firebase-messaging:23.0.5'
//    implementation 'cn.jiguang.sdk.plugin:fcm:5.2.2'
//    // 接入魅族厂商
    implementation 'cn.jiguang.sdk.plugin:meizu:5.2.2'
//    implementation 'cn.jiguang.sdk.plugin:meizu:5.2.2'
//    // 接入 VIVO 厂商
    implementation 'cn.jiguang.sdk.plugin:vivo:5.2.2'
//    implementation 'cn.jiguang.sdk.plugin:vivo:5.2.2'
//    // 接入小米厂商
    implementation 'cn.jiguang.sdk.plugin:xiaomi:5.2.2'
//    implementation 'cn.jiguang.sdk.plugin:xiaomi:5.2.2'
//    // 接入 OPPO 厂商
//    implementation 'cn.jiguang.sdk.plugin:oppo:5.2.2'
//    // JPush Android SDK v4.6.0 开始,需要单独引入 oppo 厂商 aar ,请下载官网 SDK 包并把 jpush-android-xxx-release/third-push/oppo/libs 下的 aar 文件单独拷贝一份到应用 module/libs 下
//    implementation(name: 'com.heytap.msp-push-3.1.0', ext: 'aar')
//    //以下为 OPPO 3.1.0 aar需要依赖
    implementation 'com.google.code.gson:gson:2.6.2'
    implementation 'commons-codec:commons-codec:1.6'
    implementation 'androidx.annotation:annotation:1.1.0'
//    implementation 'com.google.code.gson:gson:2.6.2'
//    implementation 'commons-codec:commons-codec:1.6'
//    implementation 'androidx.annotation:annotation:1.1.0'
//    // 接入荣耀厂商
    implementation 'cn.jiguang.sdk.plugin:honor:5.2.2'
//    implementation 'cn.jiguang.sdk.plugin:honor:5.2.2'
//    apply plugin: 'com.google.gms.google-services'
//    apply plugin: 'com.huawei.agconnect'
@@ -150,4 +162,7 @@
    //广播通信
    implementation 'io.github.jeremyliao:live-event-bus-x:1.8.0'
    //阿里的RocketMQ
    implementation 'org.apache.rocketmq:rocketmq-client:4.9.4'
}
app/src/main/AndroidManifest.xml
@@ -95,13 +95,13 @@
        <!--百度定位-->
        <meta-data
            android:name="com.baidu.lbsapi.API_KEY"
            android:value="Ky5wvrB9A9Rztsh3rQO8v8oNRgQM27NV"></meta-data>
        <service
            android:name="com.baidu.location.f"
            android:enabled="true"
            android:process=":remote"></service>
<!--        <meta-data-->
<!--            android:name="com.baidu.lbsapi.API_KEY"-->
<!--            android:value="Ky5wvrB9A9Rztsh3rQO8v8oNRgQM27NV"></meta-data>-->
<!--        <service-->
<!--            android:name="com.baidu.location.f"-->
<!--            android:enabled="true"-->
<!--            android:process=":remote"></service>-->
    </application>
app/src/main/assets/img/location.png
app/src/main/assets/img/marker_black.svg
New file
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><rect fill="none" height="24" width="24"/><path d="M12,2L12,2C8.13,2,5,5.13,5,9c0,1.74,0.5,3.37,1.41,4.84c0.95,1.54,2.2,2.86,3.16,4.4c0.47,0.75,0.81,1.45,1.17,2.26 C11,21.05,11.21,22,12,22h0c0.79,0,1-0.95,1.25-1.5c0.37-0.81,0.7-1.51,1.17-2.26c0.96-1.53,2.21-2.85,3.16-4.4 C18.5,12.37,19,10.74,19,9C19,5.13,15.87,2,12,2z M12,11.75c-1.38,0-2.5-1.12-2.5-2.5s1.12-2.5,2.5-2.5s2.5,1.12,2.5,2.5 S13.38,11.75,12,11.75z"/></svg>
app/src/main/assets/img/marker_red.svg
New file
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#D81E06"><rect fill="none" height="24" width="24"/><path d="M12,2L12,2C8.13,2,5,5.13,5,9c0,1.74,0.5,3.37,1.41,4.84c0.95,1.54,2.2,2.86,3.16,4.4c0.47,0.75,0.81,1.45,1.17,2.26 C11,21.05,11.21,22,12,22h0c0.79,0,1-0.95,1.25-1.5c0.37-0.81,0.7-1.51,1.17-2.26c0.96-1.53,2.21-2.85,3.16-4.4 C18.5,12.37,19,10.74,19,9C19,5.13,15.87,2,12,2z M12,11.75c-1.38,0-2.5-1.12-2.5-2.5s1.12-2.5,2.5-2.5s2.5,1.12,2.5,2.5 S13.38,11.75,12,11.75z"/></svg>
app/src/main/assets/index.html
New file
@@ -0,0 +1,35 @@
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>天地图</title>
    <style>
       /* 确保 body 和 html 充满整个视口 */
        html, body {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
            overflow: hidden; /* 防止滚动条出现 */
        }
     /* 确保地图容器充满整个视口 */
        #mapDiv {
            width: 100%;
            height: 100%;
            position: absolute; /* 保证地图全屏 */
            top: 0;
            left: 0;
        }
    </style>
    <script src="http://api.tianditu.gov.cn/api?v=4.0&tk=d8beed89b43160a9a185e5aff431f85d"
        type="text/javascript"></script>
</head>
<body>
    <div id="mapDiv"></div>
    <script src="js/map.js"></script>
</body>
</html>
app/src/main/assets/js/map.js
New file
@@ -0,0 +1,243 @@
(function () {
    // 图片路径
    const locationIMGPath = 'img/location.png';
    const markerBalckPath = 'img/marker_black.svg';
    const markerRedPath = 'img/marker_red.svg';
    let map;
    let lastMarker = null;
    let lastClickedMarker = null;
    let isShowWaterIntakeDetail = false;
    let zoom=12;
    document.addEventListener('DOMContentLoaded', function () {
        initMap();
        // buttonOverLay();
        mountMethodToWindow();
    });
    window.onload = function() {
          //加载坐标点
        window.Android.loadMarker();
    };
    // 初始化地图
    function initMap() {
        var imageURL = "http://t0.tianditu.gov.cn/img_w/wmts?" +
            "SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles" +
            "&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=d8beed89b43160a9a185e5aff431f85d";
        //创建自定义图层对象
        var lay = new T.TileLayer(imageURL, { minZoom: 1, maxZoom: 18 });
        var config = { layers: [lay] };
        map = new T.Map("mapDiv", config);
        map.centerAndZoom(new T.LngLat(116.40769, 39.89945), zoom); //地图的初始化中心点,此为北京的经纬度
        map.addEventListener("click", closeWaterIntakeDetail);
    }
    // 地图点击后获取经纬度
    function getLngLat(lnglat) {
        console.log(lnglat.lng.toFixed(6) + "," + lnglat.lat.toFixed(6));
        addClickOverLay(lnglat);
    }
    // 点击后添加坐标点
    function addClickOverLay(lnglat) {
        removeMyOverLay();
        let marker = new T.Marker(
            new T.LngLat(lnglat.lng, lnglat.lat) // 创建标注
        );
        map.addOverLay(marker); // 将标注添加到地图中
        // 设置弹出的窗口样式
        let infoWin = new T.InfoWindow();
        let sContent =
            "<div style='margin:0px;'>" +
            "<div style='margin:10px 10px; '>" +
            "<div >电话 : (010)88187700 <br>地址:北京市顺义区机场东路国门商务区地理信息产业园2号楼天地图大厦" +
            "</div>" +
            "<input width: 80px;height: 24px; text-align: center; background: #5596de;color: #FFF;border: none;display: block;cursor: pointer;' type='button' value='跳转详情' onClick=\"showToast()\">" +
            "</div>" +
            "</div>";
        infoWin.setContent(sContent);
        // 添加点击事件
        marker.addEventListener("click", function () {
            marker.openInfoWindow(infoWin);
        });
        lastMarker = marker;
    }
    // 删除上次点击后添加的标注
    function removeMyOverLay() {
        if (lastMarker !== null) {
            map.removeOverLay(lastMarker); // 将标注从地图中移除
        }
    }
    // 模拟添加取水口标注
    function buttonOverLay() {
        map.clearOverLays(); // 清理地图上的所有覆盖物
        let data_info = [
            [116.417854, 39.921988, "235取水口"],
            [116.406605, 39.921585, "237取水口"],
            [116.412222, 39.912345, "236取水口"]
        ];
        for (let i = 0; i < data_info.length; i++) {
            let icon = new T.Icon({
                iconUrl: markerBalckPath,
                iconSize: new T.Point(27, 27),
                iconAnchor: new T.Point(10, 25)
            });
            let marker = new T.Marker(
                new T.LngLat(data_info[i][0], data_info[i][1]), // 创建标注
                { icon: icon }
            );
            marker.addEventListener("click", function (data) {
                addMarkerListener(data);
            });
            map.addOverLay(marker); // 将标注添加到地图中
            let label = new T.Label({
                text: data_info[i][2],
                position: marker.getLngLat(),
                offset: new T.Point(-50, 10), // 设置标注文字的位置
                opacity: 1, // 设置文本的显示不透明度(范围0-1)
            });
            label.setBorderLine(0); // 设置文本的边框线宽
            label.setBackgroundColor("transparent"); // 设置文本的背景色(透明色)
            label.setFontColor("#0000FF");
            label.setFontSize(10);
            map.addOverLay(label);
        }
    }
    // 显示标注点击后的窗口
    function openInfo(content, e) {
        let point = e.lnglat;
        let markerInfoWin = new T.InfoWindow(content, {
            offset: new T.Point(0, -30)
        });
        map.openInfoWindow(markerInfoWin, point); // 开启信息窗口
    }
    // 手机获取到定位后显示定位
    function locationOverLay(lng, lag) {
        map.centerAndZoom(new T.LngLat(lng, lag), 18);
        let icon = new T.Icon({
            iconUrl: locationIMGPath,
            iconSize: new T.Point(27, 27),
            iconAnchor: new T.Point(10, 25)
        });
        let marker = new T.Marker(new T.LngLat(lng, lag), { icon: icon });
        map.addOverLay(marker);
    }
    // 将方法挂载到 window 上
    function mountMethodToWindow() {
        window.locationOverLay = locationOverLay;
        window.showToast = showToast;
        window.addMarker = addMarker;
    }
    // 调用原生安卓方法显示取水口详情
    function showWaterIntakeDetail(data) {
        isShowWaterIntakeDetail = true;
        window.Android.showWaterIntakeView(data);
    }
    // 调用原生安卓方法隐藏取水口详情
    function closeWaterIntakeDetail(data) {
        if (lastClickedMarker!== null) {
            // 如果有点击取水口则将取水口恢复成黑色
            // 上次点击的标注改为黑色
            var defaulticon = new T.Icon({
              iconUrl: markerBalckPath,
              iconSize: new T.Point(27, 27),
              iconAnchor: new T.Point(10, 25)
            });
            lastClickedMarker.setIcon(defaulticon);
          }
          if (isShowWaterIntakeDetail) {
            // 假如显示了取水口详情则隐藏取水口详情
            isShowWaterIntakeDetail = false;
            window.Android.closeWaterIntakeView();
          } else {
            // 假如没有显示取水口则添加点击的坐标
            getLngLat(data.lnglat);
          }
    }
    // 点击标注的事件
    function addMarkerListener(data) {
        chageMarkerIcon(data);
        showWaterIntakeDetail("模拟数据");
    }
    // 修改点击标注的图标
    function chageMarkerIcon(data) {
     // 点击的标注改为红色
     var currentMarker = data.target;
     // 点击后图标
     var clickedIcon = new T.Icon({
       iconUrl: markerRedPath,
       iconSize: new T.Point(27, 27),
       iconAnchor: new T.Point(10, 25)
     });
     // 设置当前点击的 marker 为点击后图标
     currentMarker.setIcon(clickedIcon);
     // 判断点击的不是同一个坐标
     if (lastClickedMarker!== null) {
       if (!isEqualsLngLat(data.target.getLngLat(), lastClickedMarker.getLngLat())) {
         var defaulticon = new T.Icon({
           iconUrl: markerBalckPath,
           iconSize: new T.Point(27, 27),
           iconAnchor: new T.Point(10, 25)
         });
         lastClickedMarker.setIcon(defaulticon);
       }
     }
     lastClickedMarker = data.target;
    }
    //调安卓原生
    function showToast() {
        // 调用 JavaScript 接口对象的方法
        window.Android.showToast('Hello, Android!');
    }
    // 判断两个坐标是否一致
    function isEqualsLngLat(data1, data2) {
        return data1.lat === data2.lat && data1.lng === data2.lng;
    }
    //添加从原生传过来的坐标并显示在地图上
    function addMarker(jsonData) {
        console.log("function》》》》》addMarker");
        var array = JSON.parse(jsonData);
        var icon = new T.Icon({
            iconUrl: markerBalckPath,
            iconSize: new T.Point(27, 27),
            iconAnchor: new T.Point(10, 25)
        });
        array.forEach(element => {
            let marker = new T.Marker(
                new T.LngLat(element[0], element[1]) // 创建标注
                , { icon: icon });
            //添加点击事件
            marker.addEventListener("click", (data) => {
                addMarkerListener(data)
            });
            map.addOverLay(marker); // 将标注添加到地图中
        });
        return "addMarker加载成功"
    }
})();
app/src/main/java/com/dayu/pipirrapp/MyApplication.java
@@ -5,7 +5,7 @@
import com.dayu.pipirrapp.net.ApiManager;
import com.tencent.bugly.crashreport.CrashReport;
import cn.jpush.android.api.JPushInterface;
//import cn.jpush.android.api.JPushInterface;
/**
 * author: zuo
@@ -23,8 +23,8 @@
        ApiManager.init();
        JPushInterface.setDebugMode(true);
        JPushInterface.init(this);
//        JPushInterface.setDebugMode(true);
//        JPushInterface.init(this);
        CrashReport.initCrashReport(getApplicationContext(), "3d4bcf7046", false);
    }
}
app/src/main/java/com/dayu/pipirrapp/activity/MainActivity.java
@@ -3,16 +3,15 @@
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import com.dayu.pipirrapp.R;
import com.dayu.pipirrapp.adapter.TabAdapter;
import com.dayu.pipirrapp.databinding.ActivityMainBinding;
import com.dayu.pipirrapp.fragment.BaseFragment;
import com.dayu.pipirrapp.fragment.MainFragment;
import com.dayu.pipirrapp.fragment.MapFragment;
import com.dayu.pipirrapp.fragment.MyFragment;
@@ -20,47 +19,49 @@
import java.util.ArrayList;
import java.util.List;
/**
 *
 * 首页
 */
public class MainActivity extends BaseActivity {
    ActivityMainBinding binding;
    List<Fragment> mFragment = new ArrayList<>();
public class MainActivity extends AppCompatActivity {
    private ActivityMainBinding binding;
    private List<Fragment> fragments = new ArrayList<>();
    private long mExitTime;
    private enum Tab {
        ORDER, MAP, MY
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityMainBinding.inflate(LayoutInflater.from(this));
        MapFragment mapFragment = new MapFragment();
        setContentView(binding.getRoot());
        mFragment.add(mapFragment);
        mFragment.add(new MainFragment());
        mFragment.add(new MyFragment());
        setupFragments();
        initView();
        initTab();
    }
    private void getPermission(){
    private void setupFragments() {
        fragments.add(new MainFragment());
        fragments.add(new MapFragment());
        fragments.add(new MyFragment());
    }
    private void initView() {
        binding.orderLL.setOnClickListener(v -> changeBottomState(Tab.ORDER));
        binding.mapLL.setOnClickListener(v -> changeBottomState(Tab.MAP));
        binding.myLL.setOnClickListener(v -> changeBottomState(Tab.MY));
    }
    private void initTab() {
        TabAdapter adapter = new TabAdapter(getSupportFragmentManager(), mFragment);
        TabAdapter adapter = new TabAdapter(getSupportFragmentManager(), fragments);
        binding.viewPager.setAdapter(adapter);
        binding.viewPager.setPagingEnabled(false);
        //设置缓存view 的个数
        binding.viewPager.setOffscreenPageLimit(3);
        // 将 ViewPager 与 TabLayout 关联
        binding.tabLayout.setupWithViewPager(binding.viewPager);
        binding.viewPager.setOffscreenPageLimit(fragments.size());
        binding.viewPager.setCurrentItem(1); // 默认显示地图页
    }
    long mExitTime;
    //点击两次退出程序 有时间间隔 间隔内点击则退出程序 否则 则提示
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
@@ -68,11 +69,63 @@
                Toast.makeText(MainActivity.this, "再按一次退出程序", Toast.LENGTH_SHORT).show();
                mExitTime = System.currentTimeMillis();
            } else {
                MainActivity.this.finish();
                finish();
            }
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }
}
    /**
     * 修改底部状态
     */
    private void changeBottomState(Tab tab) {
        resetTabState();
        switch (tab) {
            case ORDER:
                updateTabUI(0, R.drawable.bottom_order_white, R.color.white);
                break;
            case MAP:
                updateTabUI(1, R.drawable.bottom_map_white, R.color.white);
                break;
            case MY:
                updateTabUI(2, R.drawable.bottom_my_white, R.color.white);
                break;
        }
    }
    /**
     * 更新某个 Tab 的 UI 状态
     */
    private void updateTabUI(int position, int iconResId, int textColorResId) {
        binding.viewPager.setCurrentItem(position);
        switch (position) {
            case 0:
                binding.orderImg.setImageDrawable(ContextCompat.getDrawable(this, iconResId));
                binding.orderText.setTextColor(ContextCompat.getColor(this, textColorResId));
                break;
            case 1:
                binding.mapImg.setImageDrawable(ContextCompat.getDrawable(this, iconResId));
                binding.mapText.setTextColor(ContextCompat.getColor(this, textColorResId));
                break;
            case 2:
                binding.myImg.setImageDrawable(ContextCompat.getDrawable(this, iconResId));
                binding.myText.setTextColor(ContextCompat.getColor(this, textColorResId));
                break;
        }
    }
    /**
     * 重置所有 Tab 的默认状态
     */
    private void resetTabState() {
        binding.orderImg.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.bottom_order_black));
        binding.orderText.setTextColor(ContextCompat.getColor(this, R.color.black));
        binding.mapImg.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.bottom_map_black));
        binding.mapText.setTextColor(ContextCompat.getColor(this, R.color.black));
        binding.myImg.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.bottom_my_black));
        binding.myText.setTextColor(ContextCompat.getColor(this, R.color.black));
    }
}
app/src/main/java/com/dayu/pipirrapp/fragment/MapFragment.java
@@ -4,30 +4,32 @@
import android.content.Intent;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.ValueCallback;
import android.webkit.WebResourceError;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import com.dayu.pipirrapp.databinding.FragmentMapBinding;
import com.dayu.pipirrapp.js.MyWebViewInterface;
import com.dayu.pipirrapp.observer.MapFragmenObserver;
import com.dayu.pipirrapp.service.MyLocationService;
import com.dayu.pipirrapp.utils.MapJpgUtils;
import com.dayu.pipirrapp.utils.MyLog;
import com.dayu.pipirrapp.utils.ToastUtil;
import com.dayu.pipirrapp.utils.WebViewUtils;
import java.util.List;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.Random;
/**
 * author: zuo
@@ -60,14 +62,54 @@
        binding = FragmentMapBinding.inflate(inflater, container, false);
        Log.i("MapFragment", "onCreateView");
        mWebView = binding.webView;
        initWebView();
        mWebView.loadUrl("http://192.168.3.178:3000/");
        mWebView = WebViewUtils.initWebView(mWebView);
        MyWebViewInterface myWebViewInterface = new MyWebViewInterface(MapFragment.this);
        mWebView.addJavascriptInterface(myWebViewInterface, "Android");
        mWebView.loadUrl("file:///android_asset/index.html");
        initView();
        //开启定位
        Intent location = new Intent(this.getActivity(), MyLocationService.class);
        location.putExtra("isSingle", false);
        this.getActivity().startService(location);
        return binding.getRoot();
    }
    /**
     * 添加标注点
     */
    public void addMarker() {
        Random random = new Random();
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("[");
        // 中国经纬度范围
        double minLongitude = 73.43;
        double maxLongitude = 135.05;
        double minLatitude = 3.52;
        double maxLatitude = 53.57;
        for (int i = 0; i < 1000; i++) {
            stringBuilder.append("[");
            // 生成随机经度
            double longitude = minLongitude + (maxLongitude - minLongitude) * random.nextDouble();
            stringBuilder.append(longitude);
            stringBuilder.append(",");
            // 生成随机纬度
            double latitude = minLatitude + (maxLatitude - minLatitude) * random.nextDouble();
            stringBuilder.append(latitude);
            stringBuilder.append(",\"237取水口\"],");
        }
        stringBuilder.append("[116.417854,39.921988,\"235取水口\"]]");
        String jsonData = stringBuilder.toString().replace("\\", "\\\\").replace("\"", "\\\"");
//        String jsonData = "[[116.417854,39.921988,\"235取水口\"],[116.406605,39.921585,\"237取水口\"],[116.412222,39.912345,\"236取水口\"]]";
        Log.i("mWebView", "addMarker????????????" + jsonData);
        mWebView.evaluateJavascript("javascript:addMarker(\"" + jsonData + "\")", new ValueCallback<String>() {
            @Override
            public void onReceiveValue(String value) {
                Log.i("mWebView", "addMarker!!!!!!!!!" + value);
            }
        });
    }
@@ -81,7 +123,9 @@
    @Override
    public void onPause() {
        super.onPause();
        //
        mWebView.onPause(); // 通过 onPause 动作通知内核暂停所有的动作,如 DOM 的解析、plugin 的执行、JavaScript 执行等
    }
    @Override
@@ -91,73 +135,66 @@
        mWebView.destroy(); // 当 Activity 要 destroy 时,应先将 WebView 移除,再 destroy 掉
    }
    void initWebView() {
//        getActivity().getWindow().setFlags(
//                WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
//                WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
        // WebView 配置
        WebSettings webSettings = mWebView.getSettings();
        // 生命周期
// 前进后退
        if (mWebView.canGoBack()) {
            mWebView.goBack();
        }
        if (mWebView.canGoForward()) {
            mWebView.goForward();
        }
        // 缓存相关
        mWebView.clearCache(false); // 清除缓存
//        mWebView.clearHistory(); // 清除历史
//        mWebView.clearFormData(); // 清除表单数据
        webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);// 设置缓存模式
        webSettings.setDatabaseEnabled(true);
        webSettings.setDomStorageEnabled(true);//开启DOM缓存,关闭的话H5自身的一些操作是无效的
// 缓存模式
//        LOAD_DEFAULT: 默认,根据 cache-control 决定是否从网络上取数据
//        LOAD_NORMAL: API level 17 中已经废弃, 从API level 11开始作用同 LOAD_DEFAULT 模式
//        LOAD_CACHE_ELSE_NETWORK: 只要本地有,无论是否过期,或者 no-cache,都使用缓存中的数据
//        LOAD_NO_CACHE: 不使用缓存,只从网络获取数据
//        LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据
        // js 相关
        webSettings.setJavaScriptEnabled(true); // 支持 js。如果碰到后台无法释放 js 导致耗电,应在 onStop 和 onResume 里分别设成 false 和 true
       //        mWebView.addJavascriptInterface(new WebAppInterface(this), "android"); // js 接口
        //        webSettings.setPluginsEnabled(true); // 支持插件
        // 设置自适应屏幕,两者合用
        webSettings.setUseWideViewPort(true); // 将图片调整到适合 WebView 的大小
        webSettings.setLoadWithOverviewMode(true); // 缩放至屏幕的大小
        // 缩放操作
        webSettings.setSupportZoom(true); // 支持缩放,默认为 true
        webSettings.setBuiltInZoomControls(true); // 设置内置的缩放控件,若为 false,则该 WebView 不可缩放
        webSettings.setDisplayZoomControls(false); // 隐藏原生的缩放控件
        webSettings.setLoadsImagesAutomatically(true);//预加载
        //webView弹JsAlert
        mWebView.setWebChromeClient(new WebChromeClient() {
            @Override
            public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
                return super.onJsAlert(view, url, message, result);
            }
        });
        MyWebViewInterface myWebViewInterface=new MyWebViewInterface(this);
        mWebView.addJavascriptInterface(myWebViewInterface,"Android");
    }
    void initView() {
        //跳转到指定位置
        binding.flyBtn.setOnClickListener(v -> {
            mWebView.evaluateJavascript("javascript:locationOverLay(116.399565,39.89432)", value -> {
            });
        });
        mWebView.setWebViewClient(new WebViewClient() {
            @Override
            public WebResourceResponse  shouldInterceptRequest(WebView view, WebResourceRequest request) {
                String url = request.getUrl().toString();
                // 检查本地缓存
                File cachedTile = MapJpgUtils.getInsatance(MapFragment.this.getContext()).getCachedTile(url);
                if (cachedTile != null && cachedTile.exists()) {
                    // 判断缓存是否过期
                    if (!MapJpgUtils.getInsatance(MapFragment.this.getContext()).isCacheExpired(cachedTile)) {
                        try {
                            // 从缓存加载瓦片
                            return new WebResourceResponse("image/jpg", "utf-8", new FileInputStream(cachedTile));
                        } catch (FileNotFoundException e) {
                            e.printStackTrace();
                        }
                    }
                }
                return super.shouldInterceptRequest(view, request);
            }
            @Override
            public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
                super.onReceivedError(view, request, error);
                // 捕获加载过程中发生的错误
                int errorCode = error.getErrorCode();
                String description = error.getDescription().toString();
                String failingUrl = request.getUrl().toString();
                Log.e("setWebViewClient", "errorCode:" + errorCode + ">>>>description:" + description + ">>>>failingUrl:" + failingUrl);
                // 处理错误,例如显示错误页面或提示用户
                // view.loadUrl("file:///android_asset/error.html");
            }
            @Override
            public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
                super.onReceivedHttpError(view, request, errorResponse);
                // 捕获HTTP错误(如404, 500等)
                int statusCode = errorResponse.getStatusCode();
                String description = errorResponse.getReasonPhrase();
                Log.e("setWebViewClient", "statusCode:" + statusCode + ">>>>description:" + description);
                // 根据HTTP状态码处理错误
            }
        });
    }
    public void showWaterIntakeDetail(String data){
    public void showWaterIntakeDetail(String data) {
        MyLog.i(data);
        binding.bottomLL.setVisibility(View.VISIBLE);
    }
    public void closeWaterIntakeDetail(){
    public void closeWaterIntakeDetail() {
        binding.bottomLL.setVisibility(View.GONE);
    }
app/src/main/java/com/dayu/pipirrapp/js/MyWebViewInterface.java
@@ -1,6 +1,5 @@
package com.dayu.pipirrapp.js;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.webkit.JavascriptInterface;
@@ -66,4 +65,22 @@
        }
    }
    @JavascriptInterface
    public void loadMarker(){
        if (myContext instanceof MapFragment) {
            new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                    // 在主线程上执行UI操作
                    // 更新或操作UI元素的代码
                    myContext.addMarker();
                }
            });
        }
    }
}
app/src/main/java/com/dayu/pipirrapp/net/ApiManager.java
@@ -1,38 +1,27 @@
package com.dayu.pipirrapp.net;
import android.content.Context;
import android.util.Log;
import com.dayu.pipirrapp.bean.WeatherResponse;
import com.dayu.pipirrapp.net.subscribers.BaseProgressSubscriber;
import com.dayu.pipirrapp.net.subscribers.ProgressSubscriber;
import com.dayu.pipirrapp.net.subscribers.SubscriberListener;
import com.dayu.pipirrapp.utils.MapJpgUtils;
import com.dayu.pipirrapp.utils.MyJsonParser;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.annotations.NonNull;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.core.Observer;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.functions.Function;
import io.reactivex.rxjava3.schedulers.Schedulers;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
/**
@@ -43,11 +32,14 @@
 */
public class ApiManager {
    static String TAG = "ApiManager";
    static ApiManager apiManager;
    //文件上传失败重复次数
    int uplodFilerepeatSize = 3;
    ApiService apiService;
    private CompositeDisposable compositeDisposable = new CompositeDisposable(); // 管理订阅事件
    /**
     * 初始化通信框架
@@ -60,6 +52,7 @@
    public ApiManager() {
        apiService = RetrofitClient.getInstance().getApiService();
    }
    public static ApiManager getInstance() {
@@ -157,14 +150,14 @@
    }
    /**
     *  获取天气
     * @param data 经纬度(格式是 纬度:经度,英文冒号分隔)
     * 获取天气
     *
     * @param data     经纬度(格式是 纬度:经度,英文冒号分隔)
     * @param callback
     * @param <T>
     */
    public <T> void requestWeather(String data,Callback<WeatherResponse> callback) {
    public <T> void requestWeather(String data, Callback<WeatherResponse> callback) {
        Map<String, Object> params = new HashMap<>();
        params.put("key", "S6Tq4pvOakMuWRrg0");
        params.put("location", data);
@@ -176,4 +169,39 @@
    }
    public void donwLoadTile(Context context, String urlPath) {
        Observable<ResponseBody> observable;
        observable = apiService.downloadTile(urlPath);
        compositeDisposable.add(
                observable
                        .subscribeOn(Schedulers.io())  // 网络请求在 I/O 线程中进行
                        .observeOn(Schedulers.io())    // 回调处理也在 I/O 线程
                        .subscribe(
                                responseBody -> {
                                    boolean success = false;
                                    if (!MapJpgUtils.getInsatance(context).isHasFiles(urlPath)) {
                                        success = MapJpgUtils.getInsatance(context).saveTileToCache(urlPath, responseBody);
                                    }
                                    if (success) {
                                        Log.d(TAG, "Download success for tile: " + urlPath);
                                    } else {
                                        Log.e(TAG, "Failed to save tile to disk: " + urlPath);
                                    }
                                },
                                throwable -> Log.e(TAG, "Download failed for tile: " + urlPath)
                        )
        );
    }
    // 停止线程池,释放资源
    public void clearDisposables() {
        if (!compositeDisposable.isDisposed()) {
            compositeDisposable.dispose();
        }
    }
}
app/src/main/java/com/dayu/pipirrapp/net/ApiService.java
@@ -1,7 +1,6 @@
package com.dayu.pipirrapp.net;
import com.dayu.pipirrapp.bean.WeatherResponse;
import java.util.Map;
@@ -58,4 +57,8 @@
    @POST(Constants.BASE_URL + "file/upload")
    Call<BaseResponse> uploadFile(@Part("description") RequestBody description, @Part MultipartBody.Part file);
    @GET("{url}}")
    Observable<ResponseBody> downloadTile(@Path("url") String url);
}
app/src/main/java/com/dayu/pipirrapp/service/MyCommonService.java
@@ -1,6 +1,6 @@
package com.dayu.pipirrapp.service;
import cn.jpush.android.service.JCommonService;
//import cn.jpush.android.service.JCommonService;
/**
 * author: zuo
@@ -8,5 +8,5 @@
 * Time: 11:51
 * 备注:
 */
public class MyCommonService extends JCommonService {
}
//public class MyCommonService extends JCommonService {
//}
app/src/main/java/com/dayu/pipirrapp/service/MyLocationService.java
@@ -87,7 +87,7 @@
        super.onCreate();
        createNativeLocation();
        createBDLocation();
//        createBDLocation();
    }
@@ -252,10 +252,10 @@
            if (locationManager != null) {
                locationManager.removeUpdates(listener);
            }
            if (mBDLocationClient != null) {
                mBDLocationClient.stop();
                mBDLocationClient.unRegisterLocationListener(mBDLocationListener);
            }
//            if (mBDLocationClient != null) {
//                mBDLocationClient.stop();
//                mBDLocationClient.unRegisterLocationListener(mBDLocationListener);
//            }
        } catch (Exception e) {
            CrashReport.postCatchedException(e);
app/src/main/java/com/dayu/pipirrapp/service/PushService.java
@@ -1,12 +1,6 @@
package com.dayu.pipirrapp.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import androidx.annotation.Nullable;
import cn.jpush.android.service.JPushMessageService;
//import cn.jpush.android.service.JPushMessageService;
/**
 * author: zuo
@@ -14,6 +8,6 @@
 * Time: 10:29
 * 备注:推送的服务
 */
public class PushService extends JPushMessageService {
}
//public class PushService extends JPushMessageService {
//
//}
app/src/main/java/com/dayu/pipirrapp/tool/RocketMQConsumer.java
New file
@@ -0,0 +1,62 @@
package com.dayu.pipirrapp.tool;
import android.util.Log;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
import java.util.List;
/**
 * author: zuo
 * Date: 2024-09-03
 * Time: 13:51
 * 备注:MQ消息队列模块
 */
public class RocketMQConsumer {
    private DefaultMQPushConsumer consumer;
    public void start() {
        try {
            // 创建一个消费者实例
            consumer = new DefaultMQPushConsumer("YourConsumerGroup");
            // 设置NameServer地址
            consumer.setNamesrvAddr("YourNameServerAddress");
            // 订阅一个或多个Topic
            consumer.subscribe("YourTopic", "*");
            // 注册消息监听器
            consumer.registerMessageListener(new MessageListenerConcurrently() {
                @Override
                public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
                    for (MessageExt msg : msgs) {
                        // 处理消息
                        Log.d("RocketMQ", "Receive message: " + new String(msg.getBody()));
                        // 这里可以触发本地通知或者更新UI
                    }
                    return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                }
            });
            // 启动消费者
            consumer.start();
        } catch (MQClientException e) {
            e.printStackTrace();
            Log.e("RocketMQ", "Consumer failed to start: " + e.getMessage());
        }
    }
    public void stop() {
        if (consumer != null) {
            consumer.shutdown();
        }
    }
}
app/src/main/java/com/dayu/pipirrapp/utils/MapJpgUtils.java
New file
@@ -0,0 +1,150 @@
package com.dayu.pipirrapp.utils;
import android.content.Context;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import okhttp3.ResponseBody;
/**
 * author: zuo
 * Date: 2024-09-06
 * Time: 17:59
 * 备注: 完成地图缓存策略
 */
public class MapJpgUtils {
    static MapJpgUtils mapJpgUtils;
    Context context;
    public static MapJpgUtils getInsatance(Context context) {
        if (mapJpgUtils == null) {
            mapJpgUtils = new MapJpgUtils();
        }
        mapJpgUtils.context = context;
        return mapJpgUtils;
    }
    // 获取缓存瓦片文件
    public File getCachedTile(String url) {
        String fileName = hashUrl(url);  // URL 转换为唯一的哈希值或文件名
        File cacheDir = new File(context.getCacheDir(), "mapCache");  // 瓦片存储目录
        if (!cacheDir.exists()) {
            cacheDir.mkdirs();
        }
        return new File(cacheDir, fileName);
    }
    // 使用 MD5 哈希 URL 作为文件名
    private String hashUrl(String url) {
        try {
            MessageDigest digest = MessageDigest.getInstance("MD5");
            digest.update(url.getBytes());
            byte[] hashBytes = digest.digest();
            StringBuilder sb = new StringBuilder();
            for (byte b : hashBytes) {
                sb.append(String.format("%02x", b));
            }
            return sb.toString();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return String.valueOf(url.hashCode());
        }
    }
    // 保存瓦片到缓存
    public boolean saveTileToCache(String url, ResponseBody body) {
        try {
            File tileFile = new File(context.getCacheDir() + File.separator + hashUrl(url) + ".png");
            if (!tileFile.getParentFile().exists()) {
                tileFile.getParentFile().mkdirs();
            }
            InputStream inputStream = null;
            OutputStream outputStream = null;
            try {
                byte[] fileReader = new byte[4096];
                long fileSizeDownloaded = 0;
                inputStream = body.byteStream();
                outputStream = new FileOutputStream(tileFile);
                while (true) {
                    int read = inputStream.read(fileReader);
                    if (read == -1) {
                        break;
                    }
                    outputStream.write(fileReader, 0, read);
                    fileSizeDownloaded += read;
                }
                outputStream.flush();
                // 保存完成后,记录缓存创建时间
                tileFile.setLastModified(System.currentTimeMillis());
                return true;
            } catch (Exception e) {
                return false;
            } finally {
                if (inputStream != null) {
                    inputStream.close();
                }
                if (outputStream != null) {
                    outputStream.close();
                }
            }
        } catch (Exception e) {
            return false;
        }
    }
    /**
     * 判断文件是否存在
     *
     * @param url
     * @return
     */
    public boolean isHasFiles(String url) {
        String fileName = hashUrl(url);  // URL 转换为唯一的哈希值或文件名
        File cacheDir = new File(context.getCacheDir(), "mapCache");  // 瓦片存储目录
        if (!cacheDir.exists()) {
            cacheDir.mkdirs();
        }
        if (new File(cacheDir, fileName).exists()) {
            return true;
        }
        return false;
    }
    // 判断缓存是否过期
    public boolean isCacheExpired(File cachedFile) {
        final long cacheValidity = 24 * 60 * 60 * 1000; // 1 天有效期
        long lastModified = cachedFile.lastModified();
        return (System.currentTimeMillis() - lastModified) > cacheValidity;
    }
    // 清理旧缓存
    private void clearOldCacheFiles() {
        File cacheDir = new File(context.getCacheDir(), "mapCache");
        if (cacheDir.exists()) {
            File[] files = cacheDir.listFiles();
            if (files != null) {
                for (File file : files) {
                    // 清理超过 7 天的缓存文件
                    if (System.currentTimeMillis() - file.lastModified() > 7 * 24 * 60 * 60 * 1000) {
                        file.delete();
                    }
                }
            }
        }
    }
}
app/src/main/java/com/dayu/pipirrapp/utils/WebViewUtils.java
New file
@@ -0,0 +1,85 @@
package com.dayu.pipirrapp.utils;
import android.util.Log;
import android.webkit.ConsoleMessage;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
/**
 * author: zuo
 * Date: 2024-09-03
 * Time: 15:43
 * 备注:
 */
public class WebViewUtils {
    public static WebView initWebView(WebView mWebView) {
//        getActivity().getWindow().setFlags(
//                WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
//                WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
        // WebView 配置
        WebSettings webSettings = mWebView.getSettings();
        // 生命周期
// 前进后退
        if (mWebView.canGoBack()) {
            mWebView.goBack();
        }
        if (mWebView.canGoForward()) {
            mWebView.goForward();
        }
        // 缓存相关
        mWebView.clearCache(false); // 清除缓存
//        mWebView.clearHistory(); // 清除历史
//        mWebView.clearFormData(); // 清除表单数据
        webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);// 设置缓存模式
        webSettings.setDatabaseEnabled(true);
        webSettings.setDomStorageEnabled(true);//开启DOM缓存,关闭的话H5自身的一些操作是无效的
// 缓存模式
//        LOAD_DEFAULT: 默认,根据 cache-control 决定是否从网络上取数据
//        LOAD_NORMAL: API level 17 中已经废弃, 从API level 11开始作用同 LOAD_DEFAULT 模式
//        LOAD_CACHE_ELSE_NETWORK: 只要本地有,无论是否过期,或者 no-cache,都使用缓存中的数据
//        LOAD_NO_CACHE: 不使用缓存,只从网络获取数据
//        LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据
        // js 相关
        webSettings.setJavaScriptEnabled(true); // 支持 js。如果碰到后台无法释放 js 导致耗电,应在 onStop 和 onResume 里分别设成 false 和 true
        //        mWebView.addJavascriptInterface(new WebAppInterface(this), "android"); // js 接口
        //        webSettings.setPluginsEnabled(true); // 支持插件
        // 设置自适应屏幕,两者合用
        webSettings.setUseWideViewPort(true); // 将图片调整到适合 WebView 的大小
        webSettings.setLoadWithOverviewMode(true); // 缩放至屏幕的大小
        // 缩放操作
        webSettings.setSupportZoom(true); // 支持缩放,默认为 true
        webSettings.setBuiltInZoomControls(true); // 设置内置的缩放控件,若为 false,则该 WebView 不可缩放
        webSettings.setDisplayZoomControls(false); // 隐藏原生的缩放控件
        webSettings.setLoadsImagesAutomatically(true);//预加载
        //添加访问权限
        webSettings.setAllowFileAccess(true);
        webSettings.setAllowFileAccessFromFileURLs(true);
        webSettings.setAllowUniversalAccessFromFileURLs(true);
        //webView弹JsAlert
        mWebView.setWebChromeClient(new WebChromeClient() {
            @Override
            public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
                return super.onJsAlert(view, url, message, result);
            }
            @Override
            public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
                Log.d("WebView", consoleMessage.message()
                        + " -- From line "
                        + consoleMessage.lineNumber() + " of "
                        + consoleMessage.sourceId());
                return true;
            }
        });
        return mWebView;
    }
}
app/src/main/res/drawable/bottom_circle_bg.xml
New file
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="@color/bottom_color" /> <!-- 按钮的背景色 -->
    <corners android:radius="90dp" /> <!-- 让按钮的四个角变成圆形 -->
    <size android:width="200dp" android:height="100dp" /> <!-- 设置按钮的宽高 -->
</shape>
app/src/main/res/drawable/bottom_map_black.xml
New file
@@ -0,0 +1,28 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="48"
    android:viewportHeight="48">
  <path
      android:pathData="M24,44C35.046,44 44,35.046 44,24C44,12.954 35.046,4 24,4C12.954,4 4,12.954 4,24C4,35.046 12.954,44 24,44Z"
      android:strokeWidth="4"
      android:fillColor="#00000000"
      android:strokeColor="#333"/>
  <path
      android:pathData="M4,20.84C7.015,19.417 9.245,19.219 10.689,20.245C12.857,21.786 13.128,28.064 18.058,25.063C22.987,22.063 15.947,20.84 17.475,16.494C19.003,12.147 24.008,15.524 24.506,10.763C24.837,7.589 21.041,6.374 13.116,7.119"
      android:strokeWidth="4"
      android:fillColor="#00000000"
      android:strokeColor="#333"/>
  <path
      android:pathData="M36,8C30.286,12.989 28.29,16.001 30.013,17.037C32.597,18.592 33.693,16.403 36.847,17.037C40,17.671 39.317,21.946 37.659,21.946C36,21.946 27.41,20.852 27.843,25.865C28.275,30.878 33.442,31.62 33.442,34.421C33.442,36.288 32.299,39.146 30.013,42.994"
      android:strokeWidth="4"
      android:fillColor="#00000000"
      android:strokeColor="#333"
      android:strokeLineCap="round"/>
  <path
      android:pathData="M6.104,32.926C7.016,32.529 7.701,32.237 8.16,32.052C12.007,30.498 14.862,30.131 16.724,30.953C20.016,32.406 18.75,35.34 19.782,36.421C20.813,37.502 23.388,37.188 23.388,39.244C23.388,40.615 22.927,42.164 22.007,43.89"
      android:strokeWidth="4"
      android:fillColor="#00000000"
      android:strokeColor="#333"
      android:strokeLineCap="round"/>
</vector>
app/src/main/res/drawable/bottom_map_white.xml
New file
@@ -0,0 +1,28 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="48"
    android:viewportHeight="48">
    <path
        android:fillColor="#00000000"
        android:pathData="M24,44C35.046,44 44,35.046 44,24C44,12.954 35.046,4 24,4C12.954,4 4,12.954 4,24C4,35.046 12.954,44 24,44Z"
        android:strokeWidth="4"
        android:strokeColor="#fff" />
    <path
        android:fillColor="#00000000"
        android:pathData="M4,20.84C7.015,19.417 9.245,19.219 10.689,20.245C12.857,21.786 13.128,28.064 18.058,25.063C22.987,22.063 15.947,20.84 17.475,16.494C19.003,12.147 24.008,15.524 24.506,10.763C24.837,7.589 21.041,6.374 13.116,7.119"
        android:strokeWidth="4"
        android:strokeColor="#fff" />
    <path
        android:fillColor="#00000000"
        android:pathData="M36,8C30.286,12.989 28.29,16.001 30.013,17.037C32.597,18.592 33.693,16.403 36.847,17.037C40,17.671 39.317,21.946 37.659,21.946C36,21.946 27.41,20.852 27.843,25.865C28.275,30.878 33.442,31.62 33.442,34.421C33.442,36.288 32.299,39.146 30.013,42.994"
        android:strokeWidth="4"
        android:strokeColor="#fff"
        android:strokeLineCap="round" />
    <path
        android:fillColor="#00000000"
        android:pathData="M6.104,32.926C7.016,32.529 7.701,32.237 8.16,32.052C12.007,30.498 14.862,30.131 16.724,30.953C20.016,32.406 18.75,35.34 19.782,36.421C20.813,37.502 23.388,37.188 23.388,39.244C23.388,40.615 22.927,42.164 22.007,43.89"
        android:strokeWidth="4"
        android:strokeColor="#fff"
        android:strokeLineCap="round" />
</vector>
app/src/main/res/drawable/bottom_my_black.xml
New file
@@ -0,0 +1,27 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="48"
    android:viewportHeight="48">
  <path
      android:pathData="M24,44C35.046,44 44,35.046 44,24C44,12.954 35.046,4 24,4C12.954,4 4,12.954 4,24C4,35.046 12.954,44 24,44Z"
      android:strokeLineJoin="round"
      android:strokeWidth="4"
      android:fillColor="#00000000"
      android:fillType="evenOdd"
      android:strokeColor="#333"
      android:strokeLineCap="round"/>
  <path
      android:pathData="M24,23C26.761,23 29,20.761 29,18C29,15.239 26.761,13 24,13C21.239,13 19,15.239 19,18C19,20.761 21.239,23 24,23Z"
      android:strokeLineJoin="round"
      android:strokeWidth="4"
      android:fillColor="#00000000"
      android:strokeColor="#333"/>
  <path
      android:pathData="M10.022,38.332C10.366,33.121 14.702,29 20,29H28C33.291,29 37.623,33.11 37.977,38.311"
      android:strokeLineJoin="round"
      android:strokeWidth="4"
      android:fillColor="#00000000"
      android:strokeColor="#333"
      android:strokeLineCap="round"/>
</vector>
app/src/main/res/drawable/bottom_my_white.xml
New file
@@ -0,0 +1,27 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="48"
    android:viewportHeight="48">
    <path
        android:pathData="M24,44C35.046,44 44,35.046 44,24C44,12.954 35.046,4 24,4C12.954,4 4,12.954 4,24C4,35.046 12.954,44 24,44Z"
        android:strokeLineJoin="round"
        android:strokeWidth="4"
        android:fillColor="#00000000"
        android:fillType="evenOdd"
        android:strokeColor="#fff"
        android:strokeLineCap="round"/>
    <path
        android:pathData="M24,23C26.761,23 29,20.761 29,18C29,15.239 26.761,13 24,13C21.239,13 19,15.239 19,18C19,20.761 21.239,23 24,23Z"
        android:strokeLineJoin="round"
        android:strokeWidth="4"
        android:fillColor="#00000000"
        android:strokeColor="#fff"/>
    <path
        android:pathData="M10.022,38.332C10.366,33.121 14.702,29 20,29H28C33.291,29 37.623,33.11 37.977,38.311"
        android:strokeLineJoin="round"
        android:strokeWidth="4"
        android:fillColor="#00000000"
        android:strokeColor="#fff"
        android:strokeLineCap="round"/>
</vector>
app/src/main/res/drawable/bottom_order_black.xml
New file
@@ -0,0 +1,26 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="48"
    android:viewportHeight="48">
  <path
      android:pathData="M33.05,7H38C39.105,7 40,7.895 40,9V42C40,43.105 39.105,44 38,44H10C8.895,44 8,43.105 8,42L8,9C8,7.895 8.895,7 10,7H16H17V10H31V7H33.05Z"
      android:strokeLineJoin="round"
      android:strokeWidth="4"
      android:fillColor="#00000000"
      android:strokeColor="#333"/>
  <path
      android:pathData="M17,4h14v6h-14z"
      android:strokeLineJoin="round"
      android:strokeWidth="4"
      android:fillColor="#00000000"
      android:strokeColor="#333"
      android:strokeLineCap="round"/>
  <path
      android:pathData="M27,19L19,27.001H29.004L21,35.002"
      android:strokeLineJoin="round"
      android:strokeWidth="4"
      android:fillColor="#00000000"
      android:strokeColor="#333"
      android:strokeLineCap="round"/>
</vector>
app/src/main/res/drawable/bottom_order_white.xml
New file
@@ -0,0 +1,26 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="48"
    android:viewportHeight="48">
    <path
        android:pathData="M33.05,7H38C39.105,7 40,7.895 40,9V42C40,43.105 39.105,44 38,44H10C8.895,44 8,43.105 8,42L8,9C8,7.895 8.895,7 10,7H16H17V10H31V7H33.05Z"
        android:strokeLineJoin="round"
        android:strokeWidth="4"
        android:fillColor="#00000000"
        android:strokeColor="#ffffff"/>
    <path
        android:pathData="M17,4h14v6h-14z"
        android:strokeLineJoin="round"
        android:strokeWidth="4"
        android:fillColor="#00000000"
        android:strokeColor="#fff"
        android:strokeLineCap="round"/>
    <path
        android:pathData="M27,19L19,27.001H29.004L21,35.002"
        android:strokeLineJoin="round"
        android:strokeWidth="4"
        android:fillColor="#00000000"
        android:strokeColor="#fff"
        android:strokeLineCap="round"/>
</vector>
app/src/main/res/drawable/vector_adjust.xml
New file
@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
    android:viewportHeight="24" android:viewportWidth="24"
    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
    <path android:fillColor="@android:color/white" android:pathData="M12,2C6.49,2 2,6.49 2,12s4.49,10 10,10 10,-4.49 10,-10S17.51,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM15,12c0,1.66 -1.34,3 -3,3s-3,-1.34 -3,-3 1.34,-3 3,-3 3,1.34 3,3z"/>
</vector>
app/src/main/res/drawable/vector_btn_date.xml
New file
@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#CCCCCC"
    android:viewportHeight="24" android:viewportWidth="24"
    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
    <path android:fillColor="@android:color/white" android:pathData="M9,11L7,11v2h2v-2zM13,11h-2v2h2v-2zM17,11h-2v2h2v-2zM19,4h-1L18,2h-2v2L8,4L8,2L6,2v2L5,4c-1.11,0 -1.99,0.9 -1.99,2L3,20c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,6c0,-1.1 -0.9,-2 -2,-2zM19,20L5,20L5,9h14v11z"/>
</vector>
app/src/main/res/drawable/vector_close.xml
New file
@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#CCCCCC"
    android:viewportHeight="24" android:viewportWidth="24"
    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
    <path android:fillColor="@android:color/white" android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
</vector>
app/src/main/res/drawable/vector_distance.xml
New file
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24"
    android:tint="?attr/colorControlNormal">
  <path
      android:fillColor="@android:color/white"
      android:pathData="M12,10.9c-0.61,0 -1.1,0.49 -1.1,1.1s0.49,1.1 1.1,1.1c0.61,0 1.1,-0.49 1.1,-1.1s-0.49,-1.1 -1.1,-1.1zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM14.19,14.19L6,18l3.81,-8.19L18,6l-3.81,8.19z"/>
</vector>
app/src/main/res/drawable/vector_list.xml
New file
@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
    android:viewportHeight="24" android:viewportWidth="24"
    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
    <path android:fillColor="@android:color/white" android:pathData="M19,3L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM14,17L7,17v-2h7v2zM17,13L7,13v-2h10v2zM17,9L7,9L7,7h10v2z"/>
</vector>
app/src/main/res/drawable/vector_location.xml
New file
@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
    android:viewportHeight="24" android:viewportWidth="24"
    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
    <path android:fillColor="@android:color/white" android:pathData="M12,2C8.14,2 5,5.14 5,9c0,5.25 7,13 7,13s7,-7.75 7,-13c0,-3.86 -3.14,-7 -7,-7zM16,10h-3v3h-2v-3L8,10L8,8h3L11,5h2v3h3v2z"/>
</vector>
app/src/main/res/drawable/vector_location_clock.xml
New file
@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#606060"
    android:viewportHeight="24" android:viewportWidth="24"
    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
    <path android:fillColor="@android:color/white" android:pathData="M12,2C8.13,2 5,5.13 5,9c0,5.25 7,13 7,13s7,-7.75 7,-13c0,-3.87 -3.13,-7 -7,-7zM12,11.5c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5 2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5z"/>
</vector>
app/src/main/res/drawable/vector_loop.xml
New file
@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
    android:viewportHeight="24" android:viewportWidth="24"
    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
    <path android:fillColor="@android:color/white" android:pathData="M12,4L12,1L8,5l4,4L12,6c3.31,0 6,2.69 6,6 0,1.01 -0.25,1.97 -0.7,2.8l1.46,1.46C19.54,15.03 20,13.57 20,12c0,-4.42 -3.58,-8 -8,-8zM12,18c-3.31,0 -6,-2.69 -6,-6 0,-1.01 0.25,-1.97 0.7,-2.8L5.24,7.74C4.46,8.97 4,10.43 4,12c0,4.42 3.58,8 8,8v3l4,-4 -4,-4v3z"/>
</vector>
app/src/main/res/drawable/vector_photo_camera.xml
New file
@@ -0,0 +1,6 @@
<vector android:height="10dp" android:tint="#D4D4D6"
    android:viewportHeight="24" android:viewportWidth="24"
    android:width="10dp" xmlns:android="http://schemas.android.com/apk/res/android">
    <path android:fillColor="@android:color/white" android:pathData="M12,12m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"/>
    <path android:fillColor="@android:color/white" android:pathData="M9,2L7.17,4L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2h-3.17L15,2L9,2zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5z"/>
</vector>
app/src/main/res/drawable/vector_timer.xml
New file
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24"
    android:tint="?attr/colorControlNormal">
  <path
      android:fillColor="@android:color/white"
      android:pathData="M15,1L9,1v2h6L15,1zM11,14h2L13,8h-2v6zM19.03,7.39l1.42,-1.42c-0.43,-0.51 -0.9,-0.99 -1.41,-1.41l-1.42,1.42C16.07,4.74 14.12,4 12,4c-4.97,0 -9,4.03 -9,9s4.02,9 9,9 9,-4.03 9,-9c0,-2.12 -0.74,-4.07 -1.97,-5.61zM12,20c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z"/>
</vector>
app/src/main/res/layout/activity_main.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
@@ -12,35 +11,128 @@
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@id/tabLayout" />
        android:layout_above="@id/bottom_navigation" />
    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tabLayout"
    <LinearLayout
        android:id="@+id/bottom_navigation"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_height="70dp"
        android:layout_alignParentBottom="true"
        android:background="?attr/colorPrimary"
        app:tabGravity="fill"
        app:tabIndicatorColor="#FFC107"
        app:tabMode="fixed"
        app:tabSelectedTextColor="#FFC107"
        app:tabTextColor="#000000">
        android:layout_marginTop="-20dp"
        android:background="#00ffffff"
        android:gravity="bottom"
        android:orientation="horizontal">
<!--        <com.google.android.material.tabs.TabItem-->
<!--            android:layout_width="match_parent"-->
<!--            android:layout_height="match_parent"-->
<!--            android:text="首页" />-->
        <LinearLayout
            android:id="@+id/orderLL"
            android:layout_width="0dp"
            android:layout_height="50dp"
            android:layout_weight="1"
            android:background="@color/bottom_color"
            android:gravity="center"
            android:orientation="vertical">
<!--        <com.google.android.material.tabs.TabItem-->
<!--            android:layout_width="match_parent"-->
<!--            android:layout_height="match_parent"-->
<!--            android:text="地图" />-->
            <ImageView
                android:id="@+id/orderImg"
                android:layout_width="wrap_content"
                android:layout_height="25dp"
                android:layout_marginTop="8dp"
                android:src="@drawable/bottom_order_black" />
<!--        <com.google.android.material.tabs.TabItem-->
<!--            android:layout_width="match_parent"-->
<!--            android:layout_height="match_parent"-->
<!--            android:text="我的" />-->
    </com.google.android.material.tabs.TabLayout>
            <TextView
                android:id="@+id/orderText"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginBottom="3dp"
                android:text="工单"
                android:textSize="12sp" />
        </LinearLayout>
        <FrameLayout
            android:id="@+id/mapLL"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="bottom"
            android:orientation="vertical">
            <View
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:layout_gravity="bottom"
                android:background="@color/bottom_color"
                android:orientation="vertical" />
            <LinearLayout
                android:layout_width="80dp"
                android:layout_height="70dp"
                android:layout_gravity="center"
                android:background="@drawable/bottom_circle_bg"
                android:gravity="center"
                android:orientation="vertical">
                <ImageView
                    android:id="@+id/mapImg"
                    android:layout_width="40dp"
                    android:layout_height="40dp"
                    android:layout_marginTop="8dp"
                    android:src="@drawable/bottom_map_white" />
                <TextView
                    android:id="@+id/mapText"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="地图"
                    android:textColor="@color/white"
                    android:textSize="15sp" />
            </LinearLayout>
        </FrameLayout>
        <LinearLayout
            android:id="@+id/myLL"
            android:layout_width="0dp"
            android:layout_height="50dp"
            android:layout_weight="1"
            android:background="@color/bottom_color"
            android:gravity="center"
            android:orientation="vertical">
            <ImageView
                android:id="@+id/myImg"
                android:layout_width="wrap_content"
                android:layout_height="25dp"
                android:layout_marginTop="8dp"
                android:src="@drawable/bottom_my_black" />
            <TextView
                android:id="@+id/myText"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginBottom="3dp"
                android:text="我的"
                android:textSize="12sp" />
        </LinearLayout>
    </LinearLayout>
    <!--    &lt;!&ndash; BottomNavigationView 用于底部导航 &ndash;&gt;-->
    <!--    <com.google.android.material.bottomnavigation.BottomNavigationView-->
    <!--        android:id="@+id/bottom_navigation"-->
    <!--        android:layout_width="match_parent"-->
    <!--        android:layout_height="wrap_content"-->
    <!--        android:layout_alignParentBottom="true"-->
    <!--        android:background="@color/bottom_color"-->
    <!--        app:menu="@menu/main_bottom_nav_menu"-->
    <!--        app:itemIconTint="@color/bottom_selector_nav_item"-->
    <!--        app:itemTextColor="@color/bottom_selector_nav_item"-->
    <!--        />-->
</RelativeLayout>
app/src/main/res/layout/fragment_map.xml
@@ -3,11 +3,14 @@
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <com.github.lzyzsd.jsbridge.BridgeWebView
        android:id="@+id/webView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
<!--    <com.github.lzyzsd.jsbridge.BridgeWebView-->
<!--        android:id="@+id/webView"-->
<!--        android:layout_width="match_parent"-->
<!--        android:layout_height="match_parent" />-->
        <WebView
            android:id="@+id/webView"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    <TextView
        android:id="@+id/flyBtn"
app/src/main/res/layout/top_title.xml
@@ -27,7 +27,6 @@
            android:id="@+id/tv_title_middle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:textColor="@color/white"
            android:textSize="@dimen/title_center_text_size" />
app/src/main/res/menu/main_bottom_nav_menu.xml
New file
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/navigation_order"
       android:icon="@drawable/bottom_order_black"
        android:title="工单"/>
    <item
        android:id="@+id/navigation_map"
        android:icon="@drawable/bottom_map_black"
        android:title="地图"/>
    <item
        android:id="@+id/navigation_profile"
        android:icon="@drawable/bottom_my_black"
        android:title="我的"/>
</menu>
app/src/main/res/values/colors.xml
@@ -8,4 +8,7 @@
    <color name="title_color">#1890FF</color>
    <color name="bg_color">#F5F5F5</color>
    <color name="grey">#757575</color>
    <color name="colorSelected">#ffffff</color>
    <color name="colorUnselected">#000000</color>
    <color name="bottom_color">#1890FF</color>
</resources>
app/src/main/res/values/dimens.xml
@@ -6,4 +6,5 @@
    <dimen name="my_item_text_size">17sp</dimen>
    <dimen name="common_btn_text_size">20sp</dimen>
    <dimen name="title_height">45dp</dimen>
    <dimen name="title_center_text_size">18sp</dimen>
</resources>