From e5a090685df3d5e96a37b141d9a1d002d36f16f2 Mon Sep 17 00:00:00 2001 From: zuoxiao <470321431@qq.com> Date: 星期五, 14 二月 2025 16:53:43 +0800 Subject: [PATCH] 1.web地图添加管网显示。 2.安卓原生添加管网信息的获取。 3.安卓原生添加管网信息的本地持久化(SQLite数据库)。 4.实现图例用户的选择状态按钮的持久化。 5.实现自定义搜索按钮的实现。 6.实现搜索后弹出界面的相关功能开发。 --- app/src/main/res/drawable/location_marker_bg.xml | 28 + expand_button/src/main/res/drawable/bg_search_bar.xml | 6 app/src/main/java/com/dayu/pipirrapp/view/SearchResultDialog.java | 129 ++++ app/src/main/res/drawable/ic_pipenetwork_line_unselected.xml | 12 app/src/main/res/values/styles.xml | 10 app/src/main/assets/js/map.js | 172 ++--- app/src/main/java/com/dayu/pipirrapp/dao/PipeNetDao.java | 2 expand_button/src/main/java/com/example/expand_button/CustomSearchBar.kt | 178 ++++++ app/src/main/res/layout/dialog_search_result.xml | 21 expand_button/src/main/res/drawable/ic_search.xml | 9 app/src/main/res/layout/item_search_result.xml | 36 + app/src/main/java/com/dayu/pipirrapp/dao/DivideDao.java | 11 app/src/main/java/com/dayu/pipirrapp/dao/CenterPointDao.java | 3 expand_button/src/main/res/values/attrs.xml | 13 app/src/main/assets/img/location_blue.svg | 13 app/src/main/java/com/dayu/pipirrapp/bean/db/PipeNetworkBean.java | 60 - app/src/main/java/com/dayu/pipirrapp/db/converter/PipeNetworkConverter.java | 26 expand_button/src/main/res/layout/layout_search_bar.xml | 42 + app/src/main/java/com/dayu/pipirrapp/bean/db/PipeNetWorkDataBean.java | 34 + app/src/main/java/com/dayu/pipirrapp/utils/CommonKeyName.java | 11 app/src/main/res/drawable/walking_worker_animation.xml | 10 app/src/main/res/drawable/worker_frame1.xml | 15 expand_button/src/main/java/com/example/expand_button/ExpandSearchView.kt | 449 ++++++++++++++++ app/src/main/java/com/dayu/pipirrapp/dao/MarkerDao.java | 2 /dev/null | 12 app/src/main/res/layout/fragment_map.xml | 35 app/src/main/java/com/dayu/pipirrapp/bean/net/PipeNetworkResult.java | 22 app/src/main/res/drawable/ic_pipenetwork_line.xml | 12 expand_button/src/main/java/com/example/expand_button/ExpandButton.kt | 57 - app/src/main/java/com/dayu/pipirrapp/fragment/MapFragment.java | 205 +++++- app/src/main/java/com/dayu/pipirrapp/bean/db/PipeNetworkDataCoordinateBean.java | 29 + app/src/main/res/drawable/worker_frame2.xml | 15 32 files changed, 1,402 insertions(+), 277 deletions(-) diff --git a/app/src/main/assets/img/location_blue.svg b/app/src/main/assets/img/location_blue.svg new file mode 100644 index 0000000..135e3e9 --- /dev/null +++ b/app/src/main/assets/img/location_blue.svg @@ -0,0 +1,13 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="160" height="160" viewBox="0 0 160 160"> + <!-- 澶栧彂鍏� (Outer Glow) --> + <defs> + <radialGradient id="outerGlow" cx="50%" cy="50%" r="50%" fx="50%" fy="50%"> + <stop offset="0%" style="stop-color:#803D8BFF; stop-opacity:0.5" /> + <stop offset="100%" style="stop-color:#003D8BFF; stop-opacity:0" /> + </radialGradient> + </defs> + <circle cx="80" cy="80" r="40" fill="url(#outerGlow)" /> + + <!-- 鍐呭渾鐜� (Inner Circle Ring) --> + <circle cx="80" cy="80" r="30" fill="white" stroke="#3D8BFF" stroke-width="4" /> +</svg> diff --git a/app/src/main/assets/js/map.js b/app/src/main/assets/js/map.js index bfd0f8d..af17e60 100644 --- a/app/src/main/assets/js/map.js +++ b/app/src/main/assets/js/map.js @@ -143,7 +143,7 @@ // 鎵嬫満鑾峰彇鍒板畾浣嶅悗鏄剧ず瀹氫綅 function locationOverLay(lng, lag) { - console.log("function銆嬨�嬨�嬨�嬨�媗ocationOverLay"); +// console.log("function銆嬨�嬨�嬨�嬨�媗ocationOverLay"); map.centerAndZoom(new T.LngLat(lng, lag), map.getZoom()); let icon = new T.Icon({ iconUrl: CONFIG.IMAGES.LOCATION, @@ -161,7 +161,7 @@ //璁剧疆鍦板浘涓績鐐� function setCenterAndZoom(lng, lat, thiszoom) { zoom = thiszoom; - console.log("function銆嬨�嬨�嬨�嬨�媠etCenterAndZoom>>>>lng:" + lng + ",lat:" + lat); +// console.log("function銆嬨�嬨�嬨�嬨�媠etCenterAndZoom>>>>lng:" + lng + ",lat:" + lat); map.centerAndZoom(new T.LngLat(lng, lat), zoom); } @@ -180,10 +180,9 @@ if (lastClickedMarker !== null) { lastClickedMarker.setIcon(createIcon(CONFIG.IMAGES.MARKER_BLUE)); } - if(lastClickedDivide!==null) - { - lastClickedDivide.setIcon(createIcon(CONFIG.IMAGES.DIVIDE_BLUE)); - } + if (lastClickedDivide !== null) { + lastClickedDivide.setIcon(createIcon(CONFIG.IMAGES.DIVIDE_BLUE)); + } if (isShowWaterIntakeDetail || isShowDivideDetail) { // 鍋囧鏄剧ず浜嗗彇姘村彛璇︽儏鍒欓殣钘忓彇姘村彛璇︽儏 isShowWaterIntakeDetail = false; @@ -232,13 +231,11 @@ function isEqualsLngLat(data1, data2) { return data1.lat === data2.lat && data1.lng === data2.lng; } - function addMarker(id, lng, lat, name) { - addMarker(id, lng, lat, name, false) + function addMarker(id, lng, lat, name, isShow) { + addMyMarker(id, lng, lat, name, false, isShow) } //娣诲姞浠庡師鐢熶紶杩囨潵鐨勫潗鏍囧苟鏄剧ず鍦ㄥ湴鍥句笂 - function addMarker(id, lng, lat, name, isRed) { - console.log("function銆嬨�嬨�嬨�嬨�媋ddMarker>>>id:" + id); - + function addMyMarker(id, lng, lat, name, isRed, isShow) { const iconUrl = isRed ? CONFIG.IMAGES.MARKER_RED : CONFIG.IMAGES.MARKER_BLUE; let marker = new T.Marker( new T.LngLat(lng, lat), @@ -263,15 +260,16 @@ if (isRed) { lastClickedMarker = marker; } - + // 灏嗘爣璁板拰鏍囩瀛樺偍鍒版暟缁勪腑 waterIntakeMarkers.push({ marker: marker, label: label }); - - map.addOverLay(label); - map.addOverLay(marker); + if (isShow === "true" || isShow === true) { + map.addOverLay(label); + map.addOverLay(marker); + } return "addMarker鍔犺浇鎴愬姛 id:" + id } //鏇存柊浣嶅潗鏍� @@ -311,96 +309,67 @@ // 绠$綉绾胯矾绠$悊 const PipelineManager = { - lines: [], // 瀛樺偍鎵�鏈夊畬鎴愮殑绾胯矾 + lines: [], currentLine: { - points: [], // 褰撳墠绾胯矾鐨勭偣 - overlay: null // 褰撳墠绾胯矾鐨勫浘灞傚璞� + points: [], + overlay: null }, - - // 绾胯矾鏍峰紡閰嶇疆 + style: { color: '#1890FF', weight: 3, opacity: 0.8 }, - - /** - * 娣诲姞鐐瑰埌绠$綉绾胯矾 - * @param {number} lng 缁忓害 - * @param {number} lat 绾害 - * @param {boolean} isNewLine 鏄惁寮�濮嬫柊鐨勭嚎璺� - */ - addPoint(lng, lat, isNewLine) { - if (isNewLine) { - this.finishCurrentLine(); - } - + + addPoint(lng, lat, isNewLine, isShow) { +// console.log(`Adding point: ${lng}, ${lat}, isNewLine: ${isNewLine}`); // 娣诲姞鏃ュ織 + const point = new T.LngLat(lng, lat); - this.currentLine.points.push(point); - - if (this.currentLine.points.length > 1) { - this.updateCurrentLineDisplay(); - } - }, - - /** - * 鏇存柊褰撳墠绾胯矾鐨勬樉绀� - */ - updateCurrentLineDisplay() { - if (!this.currentLine.overlay) { - // 鍒涘缓鏂扮殑绾胯矾鍥惧眰 - this.currentLine.overlay = new T.Polyline(this.currentLine.points, this.style); - map.addOverLay(this.currentLine.overlay); - } else { - // 鏇存柊鐜版湁绾胯矾鐨勭偣 - this.currentLine.overlay.setLngLats(this.currentLine.points); - } - }, - - /** - * 瀹屾垚褰撳墠绾胯矾 - */ - finishCurrentLine() { - if (this.currentLine.points.length > 1) { - if (this.currentLine.overlay) { - // 灏嗗綋鍓嶇嚎璺坊鍔犲埌瀹屾垚鍒楄〃 - this.lines.push(this.currentLine.overlay); + + // 淇敼鍒ゆ柇閫昏緫锛岀‘淇濆瓧绗︿覆"false"琚纭鐞� + const shouldCreateNewLine = isNewLine === true || isNewLine === "true" || !this.currentLine.overlay; + + if (shouldCreateNewLine) { + // 鍒涘缓鏂扮嚎璺� + this.currentLine.points = [point]; + this.currentLine.overlay = new T.Polyline([point], this.style); + if (isShow === true || isShow === "true") { + map.addOverLay(this.currentLine.overlay); } - } else if (this.currentLine.overlay) { - // 濡傛灉鐐规暟涓嶈冻锛屾竻闄ゅ浘灞� - map.removeOverLay(this.currentLine.overlay); + } else { + // 娣诲姞鐐瑰埌鐜版湁绾胯矾 + this.currentLine.points.push(point); + this.currentLine.overlay.setLngLats(this.currentLine.points); + } - - // 閲嶇疆褰撳墠绾胯矾 - this.currentLine = { - points: [], - overlay: null - }; + // 濡傛灉鏄柊绾胯矾锛屽皢涔嬪墠鐨勭嚎璺繚瀛樺埌 lines 鏁扮粍 + if (isNewLine === true || isNewLine === "true") { + this.lines.push({ + points: [...this.currentLine.points], + overlay: this.currentLine.overlay + }); + } }, - - /** - * 鏄剧ず鎵�鏈夌嚎璺� - */ + showAll() { - this.lines.forEach(line => map.addOverLay(line)); - if (this.currentLine.overlay) { - map.addOverLay(this.currentLine.overlay); - } +// console.log("showAllpipe" + this.lines.length); + this.lines.forEach(line => { + if (line.overlay) { + map.addOverLay(line.overlay); + } + }); + }, - - /** - * 闅愯棌鎵�鏈夌嚎璺� - */ + hideAll() { - this.lines.forEach(line => map.removeOverLay(line)); - if (this.currentLine.overlay) { - map.removeOverLay(this.currentLine.overlay); - } +// console.log("hideAllpipe" + this.lines.length); + this.lines.forEach(line => { + if (line.overlay) { + map.removeOverLay(line.overlay); + } + }); }, - - /** - * 娓呴櫎鎵�鏈夌嚎璺� - */ + clearAll() { this.hideAll(); this.lines = []; @@ -414,8 +383,8 @@ /** * 娣诲姞绠$綉绾胯矾鐐� */ - function addPipeNetwork(lng, lat, isNewLine) { - PipelineManager.addPoint(lng, lat, isNewLine); + function addPipeNetwork(lng, lat, isNewLine, isShow) { + PipelineManager.addPoint(lng, lat, isNewLine, isShow); } /** @@ -491,14 +460,14 @@ function refreshMarker(id, lng, lat, name) { map.removeOverLay(lastClickedMarker.label); map.removeOverLay(lastClickedMarker); - addMarker(id, lng, lat, name, true); + addMarker(id, lng, lat, name, true, true); } function mapMoveEnd(e) { - const center = e.target.getCenter(); - window.Android.refreshCenter(center.getLng(), center.getLat()); + const center = e.target.getCenter(); + window.Android.refreshCenter(center.getLng(), center.getLat()); } @@ -524,11 +493,11 @@ }); } //娣诲姞鍒嗘按鎴� - function addDivide(id, lng, lat, name) { - addDivide(id, lng, lat, name, false) + function addDivide(id, lng, lat, name, isShow) { + addMyDivide(id, lng, lat, name, false, isShow) } //娣诲姞鍒嗘按鎴� - function addDivide(id, lng, lat, name, isRed) { + function addMyDivide(id, lng, lat, name, isRed, isShow) { console.log("function銆嬨�嬨�嬨�嬨�媋ddMarker>>>id:" + id); const iconUrl = isRed ? CONFIG.IMAGES.DIVIDE_RED : CONFIG.IMAGES.DIVIDE_BLUE; @@ -561,9 +530,10 @@ marker: marker, label: label }); - - map.addOverLay(label); - map.addOverLay(marker); // 灏嗘爣娉ㄦ坊鍔犲埌鍦板浘涓� + if (isShow === true || isShow === "true") { + map.addOverLay(label); + map.addOverLay(marker); // 灏嗘爣娉ㄦ坊鍔犲埌鍦板浘涓� + } return "addMarker鍔犺浇鎴愬姛 id:" + id } // 淇敼鐐瑰嚮鏍囨敞鐨勫浘鏍� diff --git a/app/src/main/java/com/dayu/pipirrapp/bean/db/PipeNetWorkDataBean.java b/app/src/main/java/com/dayu/pipirrapp/bean/db/PipeNetWorkDataBean.java new file mode 100644 index 0000000..e0a9f54 --- /dev/null +++ b/app/src/main/java/com/dayu/pipirrapp/bean/db/PipeNetWorkDataBean.java @@ -0,0 +1,34 @@ +package com.dayu.pipirrapp.bean.db; + +import java.util.List; + +/** + * PipNetWorkDataBean - + * + * @author zuoxiao + * @version 1.0 + * @since 2025-02-11 + */ +public class PipeNetWorkDataBean { + List<PipeNetworkDataCoordinateBean> coordinates; + String type; + + + + + public List<PipeNetworkDataCoordinateBean> getCoordinates() { + return coordinates; + } + + public void setCoordinates(List<PipeNetworkDataCoordinateBean> coordinates) { + this.coordinates = coordinates; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } +} diff --git a/app/src/main/java/com/dayu/pipirrapp/bean/db/PipeNetworkBean.java b/app/src/main/java/com/dayu/pipirrapp/bean/db/PipeNetworkBean.java index 8dbcf2b..fe15ece 100644 --- a/app/src/main/java/com/dayu/pipirrapp/bean/db/PipeNetworkBean.java +++ b/app/src/main/java/com/dayu/pipirrapp/bean/db/PipeNetworkBean.java @@ -2,8 +2,10 @@ import androidx.room.Entity; import androidx.room.PrimaryKey; +import androidx.room.TypeConverters; import com.dayu.pipirrapp.bean.net.PipeNetworkResult; +import com.dayu.pipirrapp.db.converter.PipeNetworkConverter; import java.util.List; @@ -15,65 +17,17 @@ * @since 2025-01-17 */ @Entity +@TypeConverters(PipeNetworkConverter.class) public class PipeNetworkBean { @PrimaryKey(autoGenerate = true) public long id; String type; String networkId; - List<PipeNetworkResult.Data> data; + @TypeConverters(PipeNetworkConverter.class) + PipeNetWorkDataBean data; - public class Data { - public class Coordinate { - String lat; - String lng; - - public String getLat() { - return lat; - } - - public void setLat(String lat) { - this.lat = lat; - } - - public String getLng() { - return lng; - } - - public void setLng(String lng) { - this.lng = lng; - } - } - - List<PipeNetworkResult.Data.Coordinate> coordinates; - String type; - String networkId; - - public List<PipeNetworkResult.Data.Coordinate> getCoordinates() { - return coordinates; - } - - public void setCoordinates(List<PipeNetworkResult.Data.Coordinate> coordinates) { - this.coordinates = coordinates; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getNetworkId() { - return networkId; - } - - public void setNetworkId(String networkId) { - this.networkId = networkId; - } - } public String getType() { return type; @@ -91,11 +45,11 @@ this.networkId = networkId; } - public List<PipeNetworkResult.Data> getData() { + public PipeNetWorkDataBean getData() { return data; } - public void setData(List<PipeNetworkResult.Data> data) { + public void setData(PipeNetWorkDataBean data) { this.data = data; } diff --git a/app/src/main/java/com/dayu/pipirrapp/bean/db/PipeNetworkDataCoordinateBean.java b/app/src/main/java/com/dayu/pipirrapp/bean/db/PipeNetworkDataCoordinateBean.java new file mode 100644 index 0000000..b8de500 --- /dev/null +++ b/app/src/main/java/com/dayu/pipirrapp/bean/db/PipeNetworkDataCoordinateBean.java @@ -0,0 +1,29 @@ +package com.dayu.pipirrapp.bean.db; + +/** + * PipeNetworkDataCoordinateBean - + * + * @author zuoxiao + * @version 1.0 + * @since 2025-02-11 + */ +public class PipeNetworkDataCoordinateBean { + String lat; + String lng; + + public String getLat() { + return lat; + } + + public void setLat(String lat) { + this.lat = lat; + } + + public String getLng() { + return lng; + } + + public void setLng(String lng) { + this.lng = lng; + } +} diff --git a/app/src/main/java/com/dayu/pipirrapp/bean/net/PipeNetworkResult.java b/app/src/main/java/com/dayu/pipirrapp/bean/net/PipeNetworkResult.java index 01105ef..bbcb2aa 100644 --- a/app/src/main/java/com/dayu/pipirrapp/bean/net/PipeNetworkResult.java +++ b/app/src/main/java/com/dayu/pipirrapp/bean/net/PipeNetworkResult.java @@ -12,10 +12,12 @@ public class PipeNetworkResult { String type; String networkId; - List<Data> data; + Data data; - public class Data { + public static class Data { + List<Coordinate> coordinates; + String type; public class Coordinate { String lat; String lng; @@ -37,9 +39,8 @@ } } - List<Coordinate> coordinates; - String type; - String networkId; + + public List<Coordinate> getCoordinates() { return coordinates; @@ -57,13 +58,6 @@ this.type = type; } - public String getNetworkId() { - return networkId; - } - - public void setNetworkId(String networkId) { - this.networkId = networkId; - } } public String getType() { @@ -82,11 +76,11 @@ this.networkId = networkId; } - public List<Data> getData() { + public Data getData() { return data; } - public void setData(List<Data> data) { + public void setData(Data data) { this.data = data; } } diff --git a/app/src/main/java/com/dayu/pipirrapp/dao/CenterPointDao.java b/app/src/main/java/com/dayu/pipirrapp/dao/CenterPointDao.java index 05cb87e..ddc6372 100644 --- a/app/src/main/java/com/dayu/pipirrapp/dao/CenterPointDao.java +++ b/app/src/main/java/com/dayu/pipirrapp/dao/CenterPointDao.java @@ -32,4 +32,7 @@ @Delete void delete(CenterPointBean adminData); + @Query("DELETE FROM CenterPointBean") + void deleteAll(); + } diff --git a/app/src/main/java/com/dayu/pipirrapp/dao/DivideDao.java b/app/src/main/java/com/dayu/pipirrapp/dao/DivideDao.java index a574aa0..464f24b 100644 --- a/app/src/main/java/com/dayu/pipirrapp/dao/DivideDao.java +++ b/app/src/main/java/com/dayu/pipirrapp/dao/DivideDao.java @@ -9,6 +9,7 @@ import com.dayu.pipirrapp.bean.db.DivideBean; import com.dayu.pipirrapp.bean.db.MarkerBean; +import com.dayu.pipirrapp.bean.db.SearchResultBean; import java.util.List; @@ -54,4 +55,14 @@ @Query("SELECT * FROM DivideBean") Maybe<List<DivideBean>> getAll(); // 鏀逛负杩斿洖Maybe<List<DivideBean>> + + @Query("SELECT * FROM DivideBean WHERE divideName LIKE '%' || :name || '%'") + Single<List<DivideBean>> findByDivideNameLike(String name); + + @Query("SELECT divideName as name, 'divide' as type, lat, lng, address, divideId as id FROM DivideBean " + + "WHERE divideName LIKE '%' || :keyword || '%' " + + "UNION " + + "SELECT name, 'marker' as type, lat, lng, address, id FROM MarkerBean " + + "WHERE name LIKE '%' || :keyword || '%'") + Single<List<SearchResultBean>> searchAllByKeyword(String keyword); } diff --git a/app/src/main/java/com/dayu/pipirrapp/dao/MarkerDao.java b/app/src/main/java/com/dayu/pipirrapp/dao/MarkerDao.java index 4bd9344..c34cb55 100644 --- a/app/src/main/java/com/dayu/pipirrapp/dao/MarkerDao.java +++ b/app/src/main/java/com/dayu/pipirrapp/dao/MarkerDao.java @@ -53,4 +53,6 @@ @Query("SELECT * FROM MarkerBean") Maybe<List<MarkerBean>> getAll(); + @Query("SELECT * FROM MarkerBean WHERE name LIKE '%' || :name || '%'") + Single<List<MarkerBean>> findByNameLike(String name); } diff --git a/app/src/main/java/com/dayu/pipirrapp/dao/PipeNetDao.java b/app/src/main/java/com/dayu/pipirrapp/dao/PipeNetDao.java index 2c2fa9f..e4cf130 100644 --- a/app/src/main/java/com/dayu/pipirrapp/dao/PipeNetDao.java +++ b/app/src/main/java/com/dayu/pipirrapp/dao/PipeNetDao.java @@ -1,5 +1,6 @@ package com.dayu.pipirrapp.dao; +import androidx.room.Dao; import androidx.room.Delete; import androidx.room.Insert; import androidx.room.OnConflictStrategy; @@ -17,6 +18,7 @@ /** * 绠$綉鐩稿叧dao */ +@Dao public interface PipeNetDao { @Insert(onConflict = OnConflictStrategy.REPLACE) void insert(PipeNetworkBean pipeNetworkBean); diff --git a/app/src/main/java/com/dayu/pipirrapp/db/converter/PipeNetworkConverter.java b/app/src/main/java/com/dayu/pipirrapp/db/converter/PipeNetworkConverter.java new file mode 100644 index 0000000..55d4641 --- /dev/null +++ b/app/src/main/java/com/dayu/pipirrapp/db/converter/PipeNetworkConverter.java @@ -0,0 +1,26 @@ +package com.dayu.pipirrapp.db.converter; + +import androidx.room.TypeConverter; +import com.dayu.pipirrapp.bean.db.PipeNetWorkDataBean; +import com.google.gson.Gson; + +public class PipeNetworkConverter { + + @TypeConverter + public static String fromPipeNetWorkDataBean(PipeNetWorkDataBean data) { + if (data == null) { + return null; + } + Gson gson = new Gson(); + return gson.toJson(data); + } + + @TypeConverter + public static PipeNetWorkDataBean toPipeNetWorkDataBean(String dataString) { + if (dataString == null) { + return null; + } + Gson gson = new Gson(); + return gson.fromJson(dataString, PipeNetWorkDataBean.class); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/dayu/pipirrapp/fragment/MapFragment.java b/app/src/main/java/com/dayu/pipirrapp/fragment/MapFragment.java index 27486c3..e01d39b 100644 --- a/app/src/main/java/com/dayu/pipirrapp/fragment/MapFragment.java +++ b/app/src/main/java/com/dayu/pipirrapp/fragment/MapFragment.java @@ -32,7 +32,9 @@ import com.dayu.pipirrapp.bean.db.InspectionLocationBean; import com.dayu.pipirrapp.bean.db.LatLonBean; import com.dayu.pipirrapp.bean.db.MarkerBean; +import com.dayu.pipirrapp.bean.db.PipeNetWorkDataBean; import com.dayu.pipirrapp.bean.db.PipeNetworkBean; +import com.dayu.pipirrapp.bean.db.PipeNetworkDataCoordinateBean; import com.dayu.pipirrapp.bean.net.CenterPointResult; import com.dayu.pipirrapp.bean.net.DivideListResult; import com.dayu.pipirrapp.bean.net.DivideResult; @@ -61,7 +63,9 @@ import com.dayu.pipirrapp.utils.ToastUtil; import com.dayu.pipirrapp.utils.WebViewUtils; import com.dayu.pipirrapp.view.ConfirmDialog; +import com.dayu.pipirrapp.view.SearchResultDialog; import com.dayu.pipirrapp.view.TipUtil; +import com.example.expand_button.ExpandButton; import com.hjq.permissions.OnPermissionCallback; import com.hjq.permissions.Permission; import com.hjq.permissions.XXPermissions; @@ -81,6 +85,8 @@ import io.reactivex.rxjava3.disposables.CompositeDisposable; import io.reactivex.rxjava3.schedulers.Schedulers; import kotlin.Triple; +import kotlin.Unit; +import kotlin.jvm.functions.Function1; /** * author: zuo @@ -108,6 +114,7 @@ //web鍔犺浇鏃剁綉椤佃繕娌″姞杞藉畬鏃剁殑鏁版嵁 List<MarkerBean> webNoFinishMarkerData = new ArrayList<>(); List<DivideBean> webNoFinishDivideData = new ArrayList<>(); + List<PipeNetworkBean> webNoFinishPipeNetworkData = new ArrayList<>(); //鎵�鏈夌殑Marker鏁版嵁閿负marker鐨処d Map<String, MarkerBean> markerBeanSet = new HashMap<>(); //鎵�鏈夊垎姘存埧 @@ -117,6 +124,9 @@ public double centerLat; MarkerBean mMarkerBean; + + //鏄惁鏄剧ずMarker + boolean isShowMarker, isShowPipeNetwork, isShowDivide; // 娣诲姞CompositeDisposable鏉ョ鐞嗘墍鏈夎闃� private CompositeDisposable compositeDisposable = new CompositeDisposable(); @@ -178,6 +188,8 @@ * 寮傛鍔犺浇鏈湴鏁版嵁 */ private void loadLocalData() { + + // 寮傛鍔犺浇涓績鐐规暟鎹� compositeDisposable.add( DaoSingleton.getAsynchInstance(this.getContext()).centerPointDao().findFirst() @@ -198,7 +210,7 @@ getCenterPoint(); }) ); - + isShowMarker = SharedPreferencesHelper.getInstance(this.getContext()).get(CommonKeyName.markerKeyIsShow, true); // 寮傛鍔犺浇鍙栨按鍙f暟鎹� compositeDisposable.add( DaoSingleton.getAsynchInstance(this.getContext()).markerDao().getAll() @@ -221,7 +233,7 @@ getMarkerData(); }) ); - + isShowDivide = SharedPreferencesHelper.getInstance(this.getContext()).get(CommonKeyName.divideIsShow, true); // 寮傛鍔犺浇鍒嗘按鎴挎暟鎹� compositeDisposable.add( DaoSingleton.getAsynchInstance(this.getContext()).divideDao().getAll() @@ -244,6 +256,8 @@ getDivideList(); }) ); + + isShowPipeNetwork = SharedPreferencesHelper.getInstance(this.getContext()).get(CommonKeyName.pipeNetworkIsShow, true); //寮傛鍔犺浇绠$綉鏁版嵁 compositeDisposable.add( DaoSingleton.getAsynchInstance(this.getContext()).pipeNetDao().getAll() @@ -254,16 +268,15 @@ getPipeNetworkList(); } else { for (PipeNetworkBean pipeNetworkBean : pipeNetworkBeans) { - - + setMapPipe(pipeNetworkBean); } } }, throwable -> { - Log.e(TAG, "Load divides error: " + throwable.getMessage()); - getDivideList(); + Log.e(TAG, "Load PipeNetworkBean error: " + throwable.getMessage()); + getPipeNetworkList(); }, () -> { // 褰揗aybe涓虹┖鏃惰皟鐢� - getDivideList(); + getPipeNetworkList(); }) ); } @@ -353,6 +366,12 @@ setMapDivide(bean); } } + if (!webNoFinishPipeNetworkData.isEmpty()) { + for (PipeNetworkBean bean : webNoFinishPipeNetworkData) { + setMapPipe(bean); + } + } + //鏄剧ず鎵�鏈夊彇姘村彛 MarkerUtils.showLocoMarks(MapFragment.this); //鏄剧ず宸℃鐘舵�佸苟涓旀樉绀哄洜鎰忓鍏抽棴鐨勫巻鍙叉暟鎹� @@ -391,7 +410,7 @@ return markerBean; }) .collect(Collectors.toList()); - + DaoSingleton.getInstance(MapFragment.this.getContext()).markerDao().deleteAll(); // 浣跨敤 CompositeDisposable 绠$悊鏁版嵁搴撴彃鍏ユ搷浣� compositeDisposable.add( DaoSingleton.getAsynchInstance(MapFragment.this.getContext()).markerDao().insertAll(markerBeans) @@ -424,7 +443,7 @@ //宸℃鎸夐挳 binding.inspectButton.setOnClickListener(v -> { if (XXPermissions.isGranted(MapFragment.this.getContext(), Permission.ACCESS_BACKGROUND_LOCATION)) { - new ConfirmDialog(MapFragment.this.getActivity(), (confirmDialog, v1) -> { + new ConfirmDialog(MapFragment.this.getActivity(), "纭寮�濮嬪贰妫�鍚楋紵",(confirmDialog, v1) -> { chageInspecState(InspectionUtils.STAT_INSPECTION_ONCLICK); confirmDialog.dismiss(); }).show(); @@ -456,28 +475,88 @@ Intent issue = new Intent(MapFragment.this.getActivity(), AddIssueActivity.class); MapFragment.this.getActivity().startActivity(issue); }); - binding.expandButton.setLegendsArray(new Triple<>( + binding.expandButton.setLegendsArray(new ExpandButton.Quadruple( ContextCompat.getDrawable(requireContext(), R.drawable.marker_blue), ContextCompat.getDrawable(requireContext(), R.drawable.marker_unselected), - "鍙栨按鍙�" + "鍙栨按鍙�", isShowMarker ), - new Triple<>( + new ExpandButton.Quadruple( ContextCompat.getDrawable(requireContext(), R.drawable.divide_home_blue), ContextCompat.getDrawable(requireContext(), R.drawable.divide_home_unselected), - "鍒嗘按鎴�" - )); + "鍒嗘按鎴�", isShowDivide + ), + new ExpandButton.Quadruple( + ContextCompat.getDrawable(requireContext(), R.drawable.ic_pipenetwork_line), + ContextCompat.getDrawable(requireContext(), R.drawable.ic_pipenetwork_line_unselected), + "绠$綉", isShowPipeNetwork + ) + ); binding.expandButton.setOnLegendItemClickListener((position, isSelected) -> { switch (position) { case 0: showMarkers(isSelected); + SharedPreferencesHelper.getInstance(this.getContext()).put(CommonKeyName.markerKeyIsShow, isSelected); break; case 1: showDivideMarkers(isSelected); + SharedPreferencesHelper.getInstance(this.getContext()).put(CommonKeyName.divideIsShow, isSelected); + break; + case 2: + showPipeLine(isSelected); + SharedPreferencesHelper.getInstance(this.getContext()).put(CommonKeyName.pipeNetworkIsShow, isSelected); break; } }); + binding.searchButton.setOnSearchClickListener(s -> { + if (s != null && !s.isEmpty()) { + SearchResultDialog searchDialog = new SearchResultDialog(requireContext()); + searchDialog.setOnItemClickListener(item -> { + // 鏍规嵁绫诲瀷澶勭悊鐐瑰嚮浜嬩欢 +// if ("marker".equals(item.getType())) { +// MarkerBean markerBean = new MarkerBean(); +// markerBean.setId(item.getId()); +// getInstakeDetail(markerBean); +// } else { +// DivideBean divideBean = new DivideBean(); +// divideBean.setDivideId(item.getId()); +// getDivideDetail(divideBean); +// } + // 璺宠浆鍒伴�夋嫨鐨勪綅缃� + mWebView.evaluateJavascript( + String.format("javascript:setCenterAndZoom(\"%s\",\"%s\",\"17\")", + item.getLng(), + item.getLat()), + null + ); + }); + + // 鎵ц鎼滅储 + compositeDisposable.add( + DaoSingleton.getAsynchInstance(requireContext()) + .divideDao() + .searchAllByKeyword(s) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + results -> { + if (results != null && !results.isEmpty()) { + searchDialog.setData(results); + searchDialog.show(); + } else { + ToastUtil.showToast(requireContext(), "鏈壘鍒扮浉鍏崇粨鏋�"); + } + }, + throwable -> { + ToastUtil.showToast(requireContext(), "鎼滅储澶辫触锛�" + throwable.getMessage()); + } + ) + ); + } + return null; + }); } + /** * 鏄剧ず鍙栨按鍙h鎯� @@ -522,6 +601,7 @@ centerPointBean.setLat(t.getContent().getLat()); centerPointBean.setLng(t.getContent().getLng()); centerPointBean.setZoomMp(t.getContent().getZoomMp()); + DaoSingleton.getInstance(MapFragment.this.getContext()).centerPointDao().deleteAll(); DaoSingleton.getInstance(MapFragment.this.getContext()).centerPointDao().insert(centerPointBean); jumpCenterPoint(); } else { @@ -715,13 +795,13 @@ } /** - * 娣诲姞鏍囨敞鐐� + * 娣诲姞鍙栨按鍙� */ public void setMapMarker(MarkerBean markerBean) { if (markerBean != null) { if (webViewIsFinished) { if (!TextUtils.isEmpty(markerBean.getLng()) && !TextUtils.isEmpty(markerBean.getLat())) { - mWebView.evaluateJavascript("javascript:addMarker(\"" + markerBean.getId() + "\",\"" + markerBean.getLng() + "\",\"" + markerBean.getLat() + "\",\"" + markerBean.getName() + "\")", new ValueCallback<String>() { + mWebView.evaluateJavascript("javascript:addMarker(\"" + markerBean.getId() + "\",\"" + markerBean.getLng() + "\",\"" + markerBean.getLat() + "\",\"" + markerBean.getName() + "\",\"" + isShowMarker + "\")", new ValueCallback<String>() { @Override public void onReceiveValue(String value) { } @@ -737,13 +817,13 @@ } /** - * 娣诲姞鍙栨按鍙f爣娉� + * 娣诲姞鍒嗘按鎴� */ public void setMapDivide(DivideBean divide) { if (divide != null) { if (webViewIsFinished) { if (!TextUtils.isEmpty(divide.getLng()) && !TextUtils.isEmpty(divide.getLat())) { - mWebView.evaluateJavascript("javascript:addDivide(\"" + divide.getId() + "\",\"" + divide.getLng() + "\",\"" + divide.getLat() + "\",\"" + divide.getDivideName() + "\")", new ValueCallback<String>() { + mWebView.evaluateJavascript("javascript:addDivide(\"" + divide.getId() + "\",\"" + divide.getLng() + "\",\"" + divide.getLat() + "\",\"" + divide.getDivideName() + "\",\"" + isShowDivide + "\")", new ValueCallback<String>() { @Override public void onReceiveValue(String value) { } @@ -761,24 +841,24 @@ /** * 娣诲姞绠$綉鏍囨敞 */ - public void setMapDivide(PipeNetworkBean pipeNetworkBean) { -// if (divide != null) { -// if (webViewIsFinished) { -// for () -// if (!TextUtils.isEmpty(divide.getLng()) && !TextUtils.isEmpty(divide.getLat())) { -// mWebView.evaluateJavascript("javascript:addDivide(\"" + divide.getId() + "\",\"" + divide.getLng() + "\",\"" + divide.getLat() + "\",\"" + divide.getDivideName() + "\")", new ValueCallback<String>() { -// @Override -// public void onReceiveValue(String value) { -// } -// }); -// divideBeanMap.put(divide.getId(), divide); -// } else { -// MyLog.d("setMapDivide>" + divide.getDivideName() + "缁忕含搴︿负绌�"); -// } -// } else { -// webNoFinishDivideData.add(divide); -// } -// } + public void setMapPipe(PipeNetworkBean pipeNetworkBean) { + if (pipeNetworkBean != null) { + if (webViewIsFinished) { + boolean isfrist = true; + for (PipeNetworkDataCoordinateBean data : pipeNetworkBean.getData().getCoordinates()) { + if (!TextUtils.isEmpty(data.getLat()) && !TextUtils.isEmpty(data.getLng())) { + mWebView.evaluateJavascript("javascript:addPipeNetwork(\"" + data.getLng() + "\",\"" + data.getLat() + "\",\"" + isfrist + "\",\"" + isShowPipeNetwork +"\")", new ValueCallback<String>() { + @Override + public void onReceiveValue(String value) { + } + }); + isfrist = false; + } + } + } else { + webNoFinishPipeNetworkData.add(pipeNetworkBean); + } + } } /** @@ -1053,7 +1133,7 @@ setMapDivide(divideBean); divideBeans.add(divideBean); } - + DaoSingleton.getInstance(MapFragment.this.getContext()).divideDao().deleteAll(); // 浣跨敤 CompositeDisposable 绠$悊鏁版嵁搴撴彃鍏ユ搷浣� compositeDisposable.add( DaoSingleton.getAsynchInstance(MapFragment.this.getContext()).divideDao().insertAll(divideBeans) @@ -1094,23 +1174,40 @@ if (t.getContent() != null && t.getContent().size() > 0) { List<PipeNetworkBean> pipeNetBeans = new ArrayList<>(); - PipeNetworkBean pipeNetBean = new PipeNetworkBean(); + for (PipeNetworkResult pipeNetworkResult : t.getContent()) { + PipeNetworkBean pipeNetBean = new PipeNetworkBean(); pipeNetBean.setNetworkId(pipeNetworkResult.getNetworkId()); pipeNetBean.setType(pipeNetworkResult.getType()); - pipeNetBean.setData(pipeNetworkResult.getData()); + PipeNetWorkDataBean data = new PipeNetWorkDataBean(); + data.setType(pipeNetworkResult.getData().getType()); + List<PipeNetworkDataCoordinateBean> coordinates = new ArrayList<>(); + for (PipeNetworkResult.Data.Coordinate dataBean : pipeNetworkResult.getData().getCoordinates()) { + PipeNetworkDataCoordinateBean coordinate = new PipeNetworkDataCoordinateBean(); + coordinate.setLat(dataBean.getLat()); + coordinate.setLng(dataBean.getLng()); + coordinates.add(coordinate); + } + data.setCoordinates(coordinates); + pipeNetBean.setData(data); + setMapPipe(pipeNetBean); + pipeNetBeans.add(pipeNetBean); } + + DaoSingleton.getInstance(MapFragment.this.getContext()).pipeNetDao().deleteAll(); // 浣跨敤 RxJava 寮傛鎻掑叆鏁版嵁 - DaoSingleton.getAsynchInstance(MapFragment.this.getContext()).pipeNetDao().insertAll(pipeNetBeans) - .subscribeOn(Schedulers.io()) // 鍦� IO 绾跨▼涓婃墽琛� - .observeOn(AndroidSchedulers.mainThread()) // 鍦ㄤ富绾跨▼涓婅瀵� - .subscribe(() -> { - // 鎻掑叆鎴愬姛 - Log.i("mWebView", "鏁版嵁鎻掑叆鎴愬姛"); - }, throwable -> { - // 鎻掑叆澶辫触 - Log.e("mWebView", "鏁版嵁鎻掑叆澶辫触: " + throwable.getMessage()); - }); + compositeDisposable.add( + DaoSingleton.getAsynchInstance(MapFragment.this.getContext()).pipeNetDao().insertAll(pipeNetBeans) + .subscribeOn(Schedulers.io()) // 鍦� IO 绾跨▼涓婃墽琛� + .observeOn(AndroidSchedulers.mainThread()) // 鍦ㄤ富绾跨▼涓婅瀵� + .subscribe(() -> { + // 鎻掑叆鎴愬姛 + Log.i("mWebView", "鏁版嵁鎻掑叆鎴愬姛"); + }, throwable -> { + // 鎻掑叆澶辫触 + Log.e("mWebView", "鏁版嵁鎻掑叆澶辫触: " + throwable.getMessage()); + }) + ); } @@ -1233,4 +1330,14 @@ } } + private void showPipeLine(boolean isSelected) { + if (isSelected) { + mWebView.evaluateJavascript("javascript:showAllPipeLines()", value -> { + }); + } else { + mWebView.evaluateJavascript("javascript:hideAllPipeLines()", value -> { + }); + } + } + } diff --git a/app/src/main/java/com/dayu/pipirrapp/service/MyCommonService.java b/app/src/main/java/com/dayu/pipirrapp/service/MyCommonService.java deleted file mode 100644 index 35b0fe4..0000000 --- a/app/src/main/java/com/dayu/pipirrapp/service/MyCommonService.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.dayu.pipirrapp.service; - -//import cn.jpush.android.service.JCommonService; - -/** - * author: zuo - * Date: 2024-01-10 - * Time: 11:51 - * 澶囨敞锛� - */ -//public class MyCommonService extends JCommonService { -//} diff --git a/app/src/main/java/com/dayu/pipirrapp/utils/CommonKeyName.java b/app/src/main/java/com/dayu/pipirrapp/utils/CommonKeyName.java index 1adf529..bd2f6fe 100644 --- a/app/src/main/java/com/dayu/pipirrapp/utils/CommonKeyName.java +++ b/app/src/main/java/com/dayu/pipirrapp/utils/CommonKeyName.java @@ -24,13 +24,20 @@ public final static String NetworkCallback = "NetworkCallback"; //鍒锋柊鏂板伐鍗曞皬绾㈢偣 - public final static String RedLotRefresh="RedLotRefresh"; + public final static String RedLotRefresh = "RedLotRefresh"; //鍒涘缓閫氱煡 - public final static String CreateNotification="CreateNotification"; + public final static String CreateNotification = "CreateNotification"; /** * 鍒锋柊鏁版嵁浜嬩欢 */ public static final String refreshData = "refreshData"; + + //鍙栨按鍙f槸鍚︽樉绀虹殑key + public static final String markerKeyIsShow = "markerKeyIsShow"; + + public static final String divideIsShow = "divideIsShow"; + + public static final String pipeNetworkIsShow = "pipeNetworkIsShow"; } diff --git a/app/src/main/java/com/dayu/pipirrapp/view/SearchResultDialog.java b/app/src/main/java/com/dayu/pipirrapp/view/SearchResultDialog.java new file mode 100644 index 0000000..3ecd1b6 --- /dev/null +++ b/app/src/main/java/com/dayu/pipirrapp/view/SearchResultDialog.java @@ -0,0 +1,129 @@ +package com.dayu.pipirrapp.view; + +import android.app.Dialog; +import android.content.Context; +import android.os.Bundle; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.TextView; + +import androidx.annotation.NonNull; + +import com.dayu.pipirrapp.R; +import com.dayu.pipirrapp.bean.db.SearchResultBean; + +import java.util.ArrayList; +import java.util.List; + +public class SearchResultDialog extends Dialog { + private Context context; + private List<SearchResultBean> searchResults = new ArrayList<>(); + private OnItemClickListener onItemClickListener; + + public interface OnItemClickListener { + void onItemClick(SearchResultBean item); + } + + public SearchResultDialog(@NonNull Context context) { + super(context, R.style.CustomDialog); + this.context = context; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.dialog_search_result); + + // 璁剧疆瀵硅瘽妗嗕綅缃拰澶у皬 + Window window = getWindow(); + if (window != null) { + WindowManager.LayoutParams params = window.getAttributes(); + params.gravity = Gravity.TOP; + params.width = WindowManager.LayoutParams.MATCH_PARENT; + params.height = WindowManager.LayoutParams.WRAP_CONTENT; + window.setAttributes(params); + } + + ListView listView = findViewById(R.id.listView); + SearchResultAdapter adapter = new SearchResultAdapter(); + listView.setAdapter(adapter); + + listView.setOnItemClickListener((parent, view, position, id) -> { + if (onItemClickListener != null) { + onItemClickListener.onItemClick(searchResults.get(position)); + } + dismiss(); + }); + } + + public void setData(List<SearchResultBean> results) { + this.searchResults.clear(); + this.searchResults.addAll(results); + if (isShowing()) { + ListView listView = findViewById(R.id.listView); + ((SearchResultAdapter) listView.getAdapter()).notifyDataSetChanged(); + } + } + + public void setOnItemClickListener(OnItemClickListener listener) { + this.onItemClickListener = listener; + } + + private class SearchResultAdapter extends BaseAdapter { + @Override + public int getCount() { + return searchResults.size(); + } + + @Override + public SearchResultBean getItem(int position) { + return searchResults.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + ViewHolder holder; + if (convertView == null) { + convertView = LayoutInflater.from(context).inflate(R.layout.item_search_result, parent, false); + holder = new ViewHolder(); + holder.nameText = convertView.findViewById(R.id.nameText); + holder.addressText = convertView.findViewById(R.id.addressText); + holder.typeIcon = convertView.findViewById(R.id.typeIcon); + convertView.setTag(holder); + } else { + holder = (ViewHolder) convertView.getTag(); + } + + SearchResultBean item = getItem(position); + holder.nameText.setText(item.getName()); + holder.addressText.setText(item.getAddress()); + + // 鏍规嵁绫诲瀷璁剧疆涓嶅悓鐨勫浘鏍� + if ("marker".equals(item.getType())) { + holder.typeIcon.setImageResource(R.drawable.marker_blue); + } else { + holder.typeIcon.setImageResource(R.drawable.divide_home_blue); + } + + return convertView; + } + + class ViewHolder { + TextView nameText; + TextView addressText; + ImageView typeIcon; + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_pipenetwork_line.xml b/app/src/main/res/drawable/ic_pipenetwork_line.xml new file mode 100644 index 0000000..a60a4c1 --- /dev/null +++ b/app/src/main/res/drawable/ic_pipenetwork_line.xml @@ -0,0 +1,12 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="2dp" + android:viewportWidth="24" + android:viewportHeight="2"> + + <path + android:pathData="M0,1L24,1" + android:strokeWidth="2" + android:strokeColor="#1890FF" + android:strokeLineCap="round"/> +</vector> \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_pipenetwork_line_unselected.xml b/app/src/main/res/drawable/ic_pipenetwork_line_unselected.xml new file mode 100644 index 0000000..7093974 --- /dev/null +++ b/app/src/main/res/drawable/ic_pipenetwork_line_unselected.xml @@ -0,0 +1,12 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="2dp" + android:viewportWidth="24" + android:viewportHeight="2"> + + <path + android:pathData="M0,1L24,1" + android:strokeWidth="2" + android:strokeColor="#757575" + android:strokeLineCap="round"/> +</vector> \ No newline at end of file diff --git a/app/src/main/res/drawable/location_marker_bg.xml b/app/src/main/res/drawable/location_marker_bg.xml new file mode 100644 index 0000000..41bc759 --- /dev/null +++ b/app/src/main/res/drawable/location_marker_bg.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <!-- 澶栧彂鍏� --> + <item> + <shape android:shape="oval"> + <gradient + android:type="radial" + android:gradientRadius="40dp" + android:startColor="#803D8BFF" + android:endColor="#003D8BFF"/> + <size android:width="80dp" android:height="80dp"/> + </shape> + </item> + <!-- 鍐呭渾鐜� --> + <item + android:left="20dp" + android:top="20dp" + android:right="20dp" + android:bottom="20dp"> + <shape android:shape="oval"> + <solid android:color="#FFFFFF"/> + <stroke + android:width="4dp" + android:color="#3D8BFF"/> + </shape> + </item> + <!-- 鍔ㄧ敾鍥炬爣灏嗗湪浠g爜涓缃负璇ュ浘灞傜殑鍓嶆櫙 --> +</layer-list> \ No newline at end of file diff --git a/app/src/main/res/drawable/walking_worker_animation.xml b/app/src/main/res/drawable/walking_worker_animation.xml new file mode 100644 index 0000000..7a05cf9 --- /dev/null +++ b/app/src/main/res/drawable/walking_worker_animation.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<animation-list xmlns:android="http://schemas.android.com/apk/res/android" + android:oneshot="false"> + <item + android:drawable="@drawable/worker_frame1" + android:duration="500" /> + <item + android:drawable="@drawable/worker_frame2" + android:duration="500" /> +</animation-list> \ No newline at end of file diff --git a/app/src/main/res/drawable/worker_frame1.xml b/app/src/main/res/drawable/worker_frame1.xml new file mode 100644 index 0000000..e0ccfaa --- /dev/null +++ b/app/src/main/res/drawable/worker_frame1.xml @@ -0,0 +1,15 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="48dp" + android:height="48dp" + android:viewportWidth="48" + android:viewportHeight="48"> + <path + android:fillColor="#3D8BFF" + android:pathData="M24,12m-6,0a6,6 0,1 1,12 0a6,6 0,1 1,-12 0"/> + <path + android:fillColor="#3D8BFF" + android:pathData="M32,28h-2v-6c0,-3.31 -2.69,-6 -6,-6s-6,2.69 -6,6v6h-2v-6c0,-4.42 3.58,-8 8,-8s8,3.58 8,8V28z"/> + <path + android:fillColor="#3D8BFF" + android:pathData="M22,30l-4,12h2l4,-10l4,10h2l-4,-12H22z"/> +</vector> \ No newline at end of file diff --git a/app/src/main/res/drawable/worker_frame2.xml b/app/src/main/res/drawable/worker_frame2.xml new file mode 100644 index 0000000..63ee5ff --- /dev/null +++ b/app/src/main/res/drawable/worker_frame2.xml @@ -0,0 +1,15 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="48dp" + android:height="48dp" + android:viewportWidth="48" + android:viewportHeight="48"> + <path + android:fillColor="#3D8BFF" + android:pathData="M24,12m-6,0a6,6 0,1 1,12 0a6,6 0,1 1,-12 0"/> + <path + android:fillColor="#3D8BFF" + android:pathData="M32,28h-2v-6c0,-3.31 -2.69,-6 -6,-6s-6,2.69 -6,6v6h-2v-6c0,-4.42 3.58,-8 8,-8s8,3.58 8,8V28z"/> + <path + android:fillColor="#3D8BFF" + android:pathData="M20,30l-2,12h2l2,-10l6,10h2l-6,-12H20z"/> +</vector> \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_search_result.xml b/app/src/main/res/layout/dialog_search_result.xml new file mode 100644 index 0000000..3711c14 --- /dev/null +++ b/app/src/main/res/layout/dialog_search_result.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="50dp" + android:layout_marginBottom="50dp" + android:layout_marginLeft="20dp" + android:layout_marginRight="20dp" + android:background="@android:color/white" + android:orientation="vertical"> + + <ListView + android:id="@+id/listView" + android:layout_width="match_parent" + android:layout_height="wrap_content" + + android:divider="@android:color/darker_gray" + android:dividerHeight="0.5dp" + android:maxHeight="300dp" /> + +</LinearLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_map.xml b/app/src/main/res/layout/fragment_map.xml index 9a71ebc..dc6e3a4 100644 --- a/app/src/main/res/layout/fragment_map.xml +++ b/app/src/main/res/layout/fragment_map.xml @@ -79,15 +79,31 @@ </RelativeLayout> + + <com.example.expand_button.CustomSearchBar + android:id="@+id/searchButton" + android:layout_width="40dp" + android:layout_height="40dp" + android:layout_alignParentRight="true" + android:layout_below="@+id/inspectRL" + android:layout_marginTop="20dp" + android:layout_marginRight="15dp" + android:background="@drawable/ic_blue_background_down" + android:textColor="@color/white" + android:textSize="18sp" /> + + <TextView android:id="@+id/inspectButton" android:layout_width="wrap_content" - android:layout_height="wrap_content" + android:layout_height="40dp" + android:minWidth="40dp" + android:layout_below="@+id/searchButton" android:layout_alignParentRight="true" - android:layout_marginTop="60dp" + android:layout_marginTop="10dp" android:layout_marginRight="15dp" android:background="@drawable/ic_blue_background" - android:padding="10dp" + android:gravity="center" android:text="宸�" android:textColor="@color/white" android:textSize="18sp" /> @@ -96,13 +112,15 @@ <TextView android:id="@+id/putButton" android:layout_width="wrap_content" - android:layout_height="wrap_content" + android:layout_height="40dp" android:layout_alignParentRight="true" - android:layout_marginTop="120dp" + android:layout_marginTop="10dp" + android:layout_below="@+id/inspectButton" android:layout_marginRight="15dp" android:background="@drawable/ic_green_bg" - android:padding="10dp" android:text="鎶�" + android:minWidth="40dp" + android:gravity="center" android:textColor="@color/white" android:textSize="18sp" /> @@ -111,10 +129,11 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" - android:layout_marginTop="180dp" + android:layout_marginTop="10dp" android:layout_marginRight="15dp" + android:layout_marginLeft="15dp" + android:layout_below="@+id/putButton" android:background="@drawable/ic_green_bg" - android:padding="10dp" android:textColor="@color/white" android:textSize="18sp" app:animDuration="300" diff --git a/app/src/main/res/layout/item_search_result.xml b/app/src/main/res/layout/item_search_result.xml new file mode 100644 index 0000000..6962549 --- /dev/null +++ b/app/src/main/res/layout/item_search_result.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:padding="12dp"> + + <ImageView + android:id="@+id/typeIcon" + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_gravity="center_vertical" /> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="12dp" + android:orientation="vertical"> + + <TextView + android:id="@+id/nameText" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textColor="#333333" + android:textSize="16sp" /> + + <TextView + android:id="@+id/addressText" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="4dp" + android:textColor="#666666" + android:textSize="14sp" /> + </LinearLayout> + +</LinearLayout> \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 8f9e4f6..83c3f71 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -13,6 +13,16 @@ <item name="android:windowSoftInputMode">adjustPan</item> </style> + <!-- 鑷畾涔夊璇濇鏍峰紡 --> + <style name="CustomDialog" parent="Theme.AppCompat.Light.Dialog"> + <item name="android:windowBackground">@android:color/transparent</item> + <item name="android:windowNoTitle">true</item> + <item name="android:windowIsFloating">true</item> + <item name="android:windowContentOverlay">@null</item> + <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item> + <item name="android:backgroundDimEnabled">true</item> + </style> + diff --git a/expand_button/src/main/java/com/example/expand_button/CustomSearchBar.kt b/expand_button/src/main/java/com/example/expand_button/CustomSearchBar.kt new file mode 100644 index 0000000..864c060 --- /dev/null +++ b/expand_button/src/main/java/com/example/expand_button/CustomSearchBar.kt @@ -0,0 +1,178 @@ +package com.example.expand_button + +import android.animation.ValueAnimator +import android.content.Context +import android.graphics.drawable.Drawable +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.View +import android.widget.EditText +import android.widget.ImageView +import android.widget.RelativeLayout +import android.widget.TextView +import androidx.core.content.ContextCompat + +class CustomSearchBar @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : RelativeLayout(context, attrs, defStyleAttr) { + + private var isExpanded = false + private var searchIcon: ImageView + private var searchInput: EditText + private var searchButton: TextView + private var onSearchClickListener: ((String) -> Unit)? = null + private var expandedWidth = 0 + private var animationDuration: Long = 300 + private var initialWidth = 0 + + init { + // 鍒涘缓涓�涓柊鐨凩ayoutInflater锛岄伩鍏嶄娇鐢ㄥ甫鑳屾櫙鐨勫竷灞� + val inflater = LayoutInflater.from(context).cloneInContext(context) + val root = inflater.inflate(R.layout.layout_search_bar, this, false) + // 娓呴櫎甯冨眬鏂囦欢涓殑鑳屾櫙 + root.background = null + // 娣诲姞甯冨眬 + addView(root) + + // 鍒濆鍖栬鍥� + searchIcon = findViewById(R.id.iv_search) + searchInput = findViewById(R.id.et_search) + searchButton = findViewById(R.id.tv_search) + + // 璁剧疆鍒濆鐘舵�� + post { + // 淇濆瓨鍒濆瀹藉害 + initialWidth = width + + // 纭繚鎼滅储鍥炬爣灞呬腑鏄剧ず + (searchIcon.layoutParams as RelativeLayout.LayoutParams).apply { + removeRule(ALIGN_PARENT_START) + addRule(CENTER_IN_PARENT) + setMargins(0, 0, 0, 0) + } + + // 鍒濆鐘舵�侀殣钘忚緭鍏ユ鍜屾悳绱㈡寜閽� + searchInput.visibility = View.GONE + searchButton.visibility = View.GONE + } + + // 璁剧疆鐐瑰嚮鐩戝惉 + searchIcon.setOnClickListener { + if (!isExpanded) { + expand() + } + } + + searchButton.setOnClickListener { + onSearchClickListener?.invoke(searchInput.text.toString()) + collapse() + } + + // 璁剧疆鏁翠釜甯冨眬鐨勭偣鍑荤洃鍚� + setOnClickListener { + if (!isExpanded) { + expand() + } + } + } + + private fun expand() { + if (isExpanded) return + + // 璁$畻灞曞紑鍚庣殑瀹藉害锛堝睆骞曞搴﹀噺鍘诲乏鍙宠竟璺濓級 + if (expandedWidth == 0) { + expandedWidth = context.resources.displayMetrics.widthPixels - + (32 * context.resources.displayMetrics.density).toInt() + } + + // 鍒涘缓瀹藉害鍔ㄧ敾 + ValueAnimator.ofInt(initialWidth, expandedWidth).apply { + duration = animationDuration + addUpdateListener { animator -> + layoutParams = layoutParams.apply { + width = animator.animatedValue as Int + } + } + start() + } + + // 閲嶇疆鎼滅储鍥炬爣浣嶇疆 + (searchIcon.layoutParams as RelativeLayout.LayoutParams).apply { + removeRule(CENTER_VERTICAL) + addRule(ALIGN_PARENT_START) + setMargins( + (8 * context.resources.displayMetrics.density).toInt(), + 0, + 0, + 0 + ) + } + + // 鏄剧ず杈撳叆妗嗗拰鎼滅储鎸夐挳 + searchInput.visibility = View.VISIBLE + searchButton.visibility = View.VISIBLE + searchInput.requestFocus() + + isExpanded = true + } + + private fun collapse() { + if (!isExpanded) return + + // 鍒涘缓瀹藉害鍔ㄧ敾 + ValueAnimator.ofInt(expandedWidth, initialWidth).apply { + duration = animationDuration + addUpdateListener { animator -> + layoutParams = layoutParams.apply { + width = animator.animatedValue as Int + } + } + start() + } + + // 閲嶇疆鎼滅储鍥炬爣鍒颁腑蹇冧綅缃� + (searchIcon.layoutParams as RelativeLayout.LayoutParams).apply { + removeRule(ALIGN_PARENT_START) + addRule(CENTER_IN_PARENT) + setMargins(0, 0, 0, 0) + } + + // 闅愯棌杈撳叆妗嗗拰鎼滅储鎸夐挳 + searchInput.visibility = View.GONE + searchButton.visibility = View.GONE + searchInput.setText("") + + isExpanded = false + } + + /** + * 璁剧疆鎼滅储鎸夐挳鐐瑰嚮鐩戝惉鍣� + * @param listener 鍥炶皟鍑芥暟锛屽弬鏁颁负杈撳叆妗嗕腑鐨勬枃鏈唴瀹� + */ + fun setOnSearchClickListener(listener: (String) -> Unit) { + onSearchClickListener = listener + } + + /** + * 璁剧疆鍔ㄧ敾鏃堕暱 + */ + fun setAnimationDuration(duration: Long) { + animationDuration = duration + } + + /** + * 璁剧疆鎻愮ず鏂囧瓧 + */ + fun setHint(hint: String) { + searchInput.hint = hint + } + + /** + * 鑾峰彇杈撳叆鐨勬枃鏈� + */ + fun getText(): String { + return searchInput.text.toString() + } +} \ No newline at end of file diff --git a/expand_button/src/main/java/com/example/expand_button/ExpandButton.kt b/expand_button/src/main/java/com/example/expand_button/ExpandButton.kt index 3671c76..007713c 100644 --- a/expand_button/src/main/java/com/example/expand_button/ExpandButton.kt +++ b/expand_button/src/main/java/com/example/expand_button/ExpandButton.kt @@ -499,49 +499,20 @@ fun setButtonId(id: String) { this.buttonId = id // 鍔犺浇淇濆瓨鐨勭姸鎬� - loadStates() + } - /** - * 淇濆瓨鎵�鏈夊浘渚嬮」鐨勭姸鎬� - */ - private fun saveStates() { - val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) - val states = legendItems.map { it.isSelected } - prefs.edit().putString("${KEY_LEGEND_STATES}_$buttonId", states.joinToString(",")).apply() - } - - /** - * 鍔犺浇淇濆瓨鐨勭姸鎬� - */ - private fun loadStates() { - val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) - val savedStates = prefs.getString("${KEY_LEGEND_STATES}_$buttonId", null) - - if (savedStates != null && legendItems.isNotEmpty()) { - val states = savedStates.split(",").map { it.toBoolean() } - states.forEachIndexed { index, state -> - if (index < legendItems.size) { - legendItems[index].isSelected = state - } - } - invalidate() - } - } /** * 璁剧疆鍥句緥鍐呭 */ @JvmName("setLegendsList") - fun setLegends(items: List<Triple<Drawable, Drawable, String>>) { - legendItems = items.map { (selectedIcon, unselectedIcon, description) -> + fun setLegends(items: List<Quadruple<Drawable, Drawable, String, Boolean>>) { + legendItems = items.map { (selectedIcon, unselectedIcon, description, isSelected) -> selectedIcon.setBounds(0, 0, iconSize, iconSize) unselectedIcon.setBounds(0, 0, iconSize, iconSize) - LegendItem(selectedIcon, unselectedIcon, description) + LegendItem(selectedIcon, unselectedIcon, description, isSelected) } - - // 鍔犺浇淇濆瓨鐨勭姸鎬� - loadStates() if (!isExpanded) { text = collapsedText @@ -553,8 +524,21 @@ // 娣诲姞涓�涓� Java 鍙嬪ソ鐨勬柟娉� @JvmName("setLegendsArray") - fun setLegends(vararg items: Triple<Drawable, Drawable, String>) { + fun setLegends(vararg items: Quadruple<Drawable, Drawable, String, Boolean>) { setLegends(items.toList()) + } + + // 娣诲姞涓�涓暟鎹被鏉ヨ〃绀哄洓鍏冪粍 + data class Quadruple<A, B, C, D>( + val first: A, + val second: B, + val third: C, + val fourth: D + ) + + // 娣诲姞涓�涓究鎹风殑鎵╁睍鍑芥暟鏉ュ垱寤� Quadruple + fun <A, B, C, D> quadrupleOf(first: A, second: B, third: C, fourth: D): Quadruple<A, B, C, D> { + return Quadruple(first, second, third, fourth) } /** @@ -635,10 +619,7 @@ index, legendItems[index].isSelected ) - - // 淇濆瓨鐘舵�� - saveStates() - + invalidate() } diff --git a/expand_button/src/main/java/com/example/expand_button/ExpandSearchView.kt b/expand_button/src/main/java/com/example/expand_button/ExpandSearchView.kt new file mode 100644 index 0000000..2b269ca --- /dev/null +++ b/expand_button/src/main/java/com/example/expand_button/ExpandSearchView.kt @@ -0,0 +1,449 @@ +package com.example.expand_button + +import android.animation.ValueAnimator +import android.content.Context +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.RectF +import android.graphics.drawable.Drawable +import android.text.InputType +import android.util.AttributeSet +import android.view.Gravity +import android.view.MotionEvent +import android.view.ViewGroup +import androidx.appcompat.widget.AppCompatEditText +import androidx.core.content.ContextCompat +import android.util.TypedValue +import androidx.core.animation.addListener +import android.util.Log + +class ExpandSearchView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : AppCompatEditText(context, attrs, defStyleAttr) { + + // 鎼滅储鍥炬爣 + private val searchIcon: Drawable = ContextCompat.getDrawable( + context, + R.drawable.ic_search + )!!.mutate() + + // 鎼滅储鎸夐挳鏂囧瓧 + private var searchButtonText: String = "鎼滅储" + + // 鎼滅储鎸夐挳鐐瑰嚮鐩戝惉鍣� + private var onSearchClickListener: (() -> Unit)? = null + + // 褰撳墠鏄惁澶勪簬灞曞紑鐘舵�� + private var isExpanded: Boolean = false + // 鍔ㄧ敾鎸佺画鏃堕棿,榛樿300姣 + private var animationDuration: Long = 300 + // 鎼滅储鍥炬爣涓庢枃瀛楃殑闂磋窛,榛樿涓�8dp + private var iconMargin: Float = 8 * context.resources.displayMetrics.density + // 鎼滅储鎸夐挳涓庤緭鍏ユ鐨勯棿璺�,榛樿涓�8dp + private var searchButtonMargin: Float = 8 * context.resources.displayMetrics.density + // 鎻愮ず鏂囧瓧 + private var hintText: String = "璇疯緭鍏ュ彇姘村彛鎴栧垎姘存埧鍚嶇О" + + // 灏哾efaultHeight绉诲埌绫荤殑灞炴�т腑 + private val defaultHeight = (40 * context.resources.displayMetrics.density).toInt() + + // 娣诲姞灞曞紑鏂瑰悜鐨勫睘鎬� + private var expandDirection: Int = EXPAND_LEFT + + companion object { + const val EXPAND_LEFT = 0 + const val EXPAND_RIGHT = 1 + } + + init { + // 璁剧疆鍗曡杈撳叆 + maxLines = 1 + isSingleLine = true + + // 璁剧疆鎼滅储鍥炬爣鐨勫ぇ灏� + val iconSize = (24 * context.resources.displayMetrics.density).toInt() + // 涓嶅湪杩欓噷璁剧疆鍥炬爣杈圭晫锛岃�屾槸鍦╫nDraw涓缃� + + // 璁剧疆鎻愮ず鏂囧瓧 + hint = hintText + + // 璁剧疆鏂囧瓧鍨傜洿灞呬腑 + gravity = Gravity.CENTER_VERTICAL or Gravity.START + + // 璁剧疆鍥哄畾楂樺害 + layoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + defaultHeight + ) + + // 鍒濆鐘舵�佷笅绂佺敤杈撳叆 + inputType = InputType.TYPE_NULL + + // 鍒濆鐘舵�佷笅闅愯棌杈撳叆妗� + if (!isExpanded) { + hint = "" + setText("") + isEnabled = false + + // 纭繚鍒濆瀹藉害涓烘敹璧风姸鎬佺殑瀹藉害 + post { + layoutParams = layoutParams?.apply { + width = defaultHeight // 浣跨敤鐩稿悓鐨勯珮搴︿綔涓哄搴︼紝淇濇寔姝f柟褰� + height = defaultHeight // 淇濇寔鍥哄畾楂樺害 + } + invalidate() // 娣诲姞杩欒纭繚鍥炬爣閲嶇粯 + } + } + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + + // 璁剧疆鍥哄畾楂樺害 + val defaultHeight = (40 * context.resources.displayMetrics.density).toInt() + + if (!isExpanded) { + // 鏀剁缉鐘舵�佷笅鐨勫搴﹁涓烘鏂瑰舰 + setMeasuredDimension(defaultHeight, defaultHeight) + } else { + // 灞曞紑鐘舵�佷笅淇濇寔鎸囧畾鐨勫搴︼紝浣嗛珮搴﹀浐瀹� + setMeasuredDimension(measuredWidth, defaultHeight) + } + } + + override fun onDraw(canvas: Canvas) { + if (!isExpanded) { + // 璁$畻鍥炬爣浣嶇疆骞剁粯鍒� + val centerX = width / 2 + val centerY = height / 2 + val iconSize = (24 * context.resources.displayMetrics.density).toInt() + val iconHalfWidth = iconSize / 2 + val iconHalfHeight = iconSize / 2 + searchIcon.setBounds( + centerX - iconHalfWidth, + centerY - iconHalfHeight, + centerX + iconHalfWidth, + centerY + iconHalfHeight + ) + searchIcon.draw(canvas) + } else { + val iconSize = searchIcon.intrinsicWidth + val centerY = height / 2 + val iconLeftMargin = (5 * context.resources.displayMetrics.density).toInt() + + // 缁樺埗鍥炬爣 + searchIcon.setBounds( + iconLeftMargin, + centerY - iconSize / 2, + iconLeftMargin + iconSize, + centerY + iconSize / 2 + ) + searchIcon.draw(canvas) + + // 缁樺埗鍒嗛殧绾� + paint.apply { + color = 0xFFFFFFFF.toInt() + strokeWidth = (1 * context.resources.displayMetrics.density) + } + val dividerX = iconLeftMargin + iconSize + (iconMargin / 2) + canvas.drawLine( + dividerX, + height * 0.2f, + dividerX, + height * 0.8f, + paint + ) + + // 璁板綍鏂囨湰缁樺埗鍖哄煙鐨勪俊鎭� + val buttonWidth = paint.measureText(searchButtonText) + val textLeft = compoundPaddingLeft.toFloat() // 浣跨敤澶嶅悎鍐呰竟璺� + val textRight = width - compoundPaddingRight.toFloat() // 浣跨敤澶嶅悎鍐呰竟璺� + + // 鍙湪瀹為檯缁樺埗鏂囨湰鏃惰褰曚竴娆″尯鍩熶俊鎭� + if (text!!.isNotEmpty() && !text.toString().endsWith("\n")) { + Log.d("ExpandSearchView", "鏂囨湰缁樺埗鍖哄煙淇℃伅: 鎺т欢灏哄[瀹藉害:$width, 楂樺害:$height], " + + "鏂囨湰鍖哄煙[宸﹁竟鐣�:$textLeft, 鍙宠竟鐣�:$textRight, 鍙敤瀹藉害:${textRight - textLeft}], " + + "鍐呰竟璺漑宸�:$paddingLeft, 鍙�:$paddingRight, 澶嶅悎宸�:$compoundPaddingLeft, 澶嶅悎鍙�:$compoundPaddingRight], " + + "鏂囨湰淇℃伅[褰撳墠鏂囨湰:'${text}', 闀垮害:${text?.length}, 鍏夋爣浣嶇疆:$selectionStart], " + + "缁樺埗鍙傛暟[鍥炬爣澶у皬:$iconSize, 鍥炬爣宸﹁竟璺�:$iconLeftMargin, 鎸夐挳瀹藉害:$buttonWidth, 鎸夐挳杈硅窛:$searchButtonMargin], " + + "浣嶇疆淇℃伅[translationX:$translationX, scrollX:$scrollX, 鏂囨湰鍋忕Щ:${textLeft - compoundPaddingLeft}]") + } + + // 鐢绘枃鏈� + super.onDraw(canvas) + + // 缁樺埗鎸夐挳鏂囨湰 + paint.apply { + color = currentTextColor + textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 16f, context.resources.displayMetrics) + textAlign = Paint.Align.CENTER + } + val buttonText = searchButtonText + val fontMetrics = paint.fontMetrics + val textY = height / 2f + (fontMetrics.bottom - fontMetrics.top) / 2f - fontMetrics.bottom + val buttonX = width - buttonWidth / 2 - searchButtonMargin + + // 璁板綍鎼滅储鎸夐挳浣嶇疆淇℃伅 + Log.d("ExpandSearchView", "鎼滅储鎸夐挳淇℃伅: 浣嶇疆[X:$buttonX, Y:$textY], " + + "鍖哄煙[宸�:${buttonX - buttonWidth/2}, 鍙�:${buttonX + buttonWidth/2}, 涓�:0, 涓�:$height], " + + "灏哄[瀹藉害:$buttonWidth, 楂樺害:$height]") + + canvas.drawText( + buttonText, + buttonX, + textY, + paint + ) + } + } + + override fun onTextChanged( + text: CharSequence?, + start: Int, + lengthBefore: Int, + lengthAfter: Int + ) { + super.onTextChanged(text, start, lengthBefore, lengthAfter) + // 鍙湪灞曞紑鐘舵�佷笖瀹為檯鏈夋柊瀛楃杈撳叆鏃舵墠杈撳嚭鏃ュ織 + if (isExpanded && lengthAfter > lengthBefore) { + val newChar = text?.subSequence(start, start + lengthAfter - lengthBefore) + Log.d("ExpandSearchView", "鏂板瓧绗﹁緭鍏ヤ俊鎭�: 杈撳叆鍐呭[鏂板瓧绗�:'$newChar', 瀹屾暣鏂囨湰:'$text'], " + + "浣嶇疆淇℃伅[杈撳叆浣嶇疆:$start, 鍏夋爣浣嶇疆:$selectionStart], " + + "鏂囨湰鍖哄煙[宸﹀唴杈硅窛:$paddingLeft, 鍙冲唴杈硅窛:$paddingRight, 鍙瀹藉害:${width - paddingLeft - paddingRight}], " + + "婊氬姩鐘舵�乕scrollX:$scrollX, translationX:$translationX]") + } + } + + override fun onTouchEvent(event: MotionEvent): Boolean { + if (event.action == MotionEvent.ACTION_UP) { + if (isExpanded) { + // 灞曞紑鐘舵�佷笅锛屾鏌ユ槸鍚︾偣鍑讳簡鎼滅储鎸夐挳 + val buttonText = searchButtonText + val buttonWidth = paint.measureText(buttonText) + val buttonArea = RectF( + width - buttonWidth - searchButtonMargin * 2, + 0f, + width.toFloat(), + height.toFloat() + ) + + if (buttonArea.contains(event.x, event.y)) { + onSearchClickListener?.invoke() + post { toggleExpand() } + return true + } + } else { + toggleExpand() + return true + } + } + return super.onTouchEvent(event) + } + + override fun performClick(): Boolean { + // 纭繚鍙闂�ф湇鍔℃甯稿伐浣� + super.performClick() + return true + } + + /** + * 鍒囨崲灞曞紑/鏀惰捣鐘舵�� + */ + private fun toggleExpand() { + isExpanded = !isExpanded + + val startWidth = width + val screenWidth = context.resources.displayMetrics.widthPixels + val location = IntArray(2) + getLocationInWindow(location) + + // 鑾峰彇鐖跺竷灞�淇℃伅 + val parent = parent as? ViewGroup + val parentWidth = parent?.width ?: screenWidth + + // 鑾峰彇鎴栬缃竟璺� + var params = layoutParams as? ViewGroup.MarginLayoutParams + if (params == null) { + params = ViewGroup.MarginLayoutParams(layoutParams) + layoutParams = params + } + val margin = params.rightMargin.takeIf { it > 0 } + ?: (45 * context.resources.displayMetrics.density).toInt() // 榛樿45dp鐨勮竟璺� + + // 璁$畻鐩爣瀹藉害锛岃�冭檻涓よ竟鐨勮竟璺� + val endWidth = if (isExpanded) { + val iconSize = searchIcon.intrinsicWidth + val iconMarginPx = (5 * context.resources.displayMetrics.density).toInt() + val buttonWidth = paint.measureText(searchButtonText).toInt() + val hintWidth = paint.measureText(hintText) + val minRequiredWidth = iconMarginPx + iconSize + iconMargin.toInt() + // 宸︿晶鍥炬爣鍖哄煙 + hintWidth.toInt() + // 鎻愮ず鏂囧瓧瀹藉害 + buttonWidth + (searchButtonMargin * 2).toInt() + // 鎸夐挳鍖哄煙 + (16 * context.resources.displayMetrics.density).toInt() // 棰濆杈硅窛 + + maxOf(minRequiredWidth, parentWidth - (margin * 2)) // 鍙栨墍闇�瀹藉害鍜岀埗瀹瑰櫒瀹藉害鐨勮緝澶у�� + } else { + defaultHeight + } + + val initialTranslationX = translationX + ValueAnimator.ofFloat(0f, 1f).apply { + duration = animationDuration + addUpdateListener { animator -> + val fraction = animator.animatedValue as Float + val currentWidth = if (isExpanded) { + startWidth + ((endWidth - startWidth) * fraction).toInt() + } else { + startWidth - ((startWidth - endWidth) * fraction).toInt() + } + + (layoutParams as? ViewGroup.MarginLayoutParams)?.apply { + width = currentWidth + rightMargin = margin + leftMargin = if (isExpanded) margin else 0 // 灞曞紑鏃舵坊鍔犲乏杈硅窛 + }?.also { params -> + layoutParams = params + } + + // 淇浣嶇Щ锛屽綋鎺т欢灞曞紑鏃秚ranslationX璁剧疆涓�0 + if (isExpanded) { + translationX = 0f + } else { + translationX = initialTranslationX * (1 - fraction) + } + + requestLayout() + invalidate() + } + + addListener(onStart = { + if (!isExpanded) { + setText("") + } + }, onEnd = { + if (!isExpanded) { + translationX = 0f + setPadding( + defaultHeight / 4, + defaultHeight / 4, + defaultHeight / 4, + defaultHeight / 4 + ) + hint = "" + isEnabled = false + clearFocus() + inputType = InputType.TYPE_NULL + isFocusable = false + isFocusableInTouchMode = false + + // 鏀惰捣鏃剁Щ闄ゅ乏杈硅窛 + (layoutParams as? ViewGroup.MarginLayoutParams)?.apply { + leftMargin = 0 + }?.also { params -> + layoutParams = params + } + } + if (isExpanded) { + hint = hintText + isEnabled = true + inputType = InputType.TYPE_CLASS_TEXT + isFocusable = true + isFocusableInTouchMode = true + + val iconSize = searchIcon.intrinsicWidth + val iconMarginPx = (5 * context.resources.displayMetrics.density).toInt() + val verticalPadding = (8 * context.resources.displayMetrics.density).toInt() + val buttonWidth = paint.measureText(searchButtonText).toInt() + val hintWidth = paint.measureText(hintText) + + // 璁板綍鎻愮ず鏂囧瓧淇℃伅 + Log.d("ExpandSearchView", "鎻愮ず鏂囧瓧淇℃伅: 鏂囨湰['$hintText'], " + + "瀹藉害淇℃伅[鏂囨湰瀹藉害:$hintWidth, 鍙敤瀹藉害:${width - iconMarginPx - iconSize - iconMargin - buttonWidth - searchButtonMargin * 2}], " + + "鍐呰竟璺漑宸�:${iconMarginPx + iconSize + iconMargin.toInt()}, 鍙�:${buttonWidth + (searchButtonMargin * 2).toInt()}]") + + setPadding( + iconMarginPx + iconSize + iconMargin.toInt(), // 宸﹁竟璺濆寘鍚浘鏍囧拰闂磋窛 + verticalPadding, + buttonWidth + (searchButtonMargin * 2).toInt(), // 鍙宠竟璺濆寘鍚寜閽拰闂磋窛 + verticalPadding + ) + requestFocus() + } + }) + start() + } + } + + + /** + * 璁剧疆鎼滅储鍥炬爣 + */ + fun setSearchIcon(icon: Drawable) { + val size = (24 * context.resources.displayMetrics.density).toInt() + icon.setBounds(0, 0, size, size) + searchIcon.bounds = icon.bounds + invalidate() + } + + /** + * 璁剧疆鎼滅储鍥炬爣涓庢枃瀛楃殑闂磋窛 + */ + fun setIconMargin(margin: Float) { + iconMargin = margin + invalidate() + } + + /** + * 璁剧疆鎻愮ず鏂囧瓧 + */ + fun setHintText(text: String) { + hintText = text + if (isExpanded) { + hint = hintText + } + } + + /** + * 璁剧疆鍔ㄧ敾鏃堕暱 + */ + fun setAnimationDuration(duration: Long) { + animationDuration = duration + } + + /** + * 璁剧疆鎼滅储鎸夐挳涓庤緭鍏ユ鐨勯棿璺� + */ + fun setSearchButtonMargin(margin: Float) { + searchButtonMargin = margin + invalidate() + } + + /** + * 璁剧疆鎼滅储鎸夐挳鐐瑰嚮鐩戝惉鍣� + */ + fun setOnSearchClickListener(listener: () -> Unit) { + onSearchClickListener = listener + } + + /** + * 璁剧疆鎼滅储鎸夐挳鏂囧瓧 + */ + fun setSearchButtonText(text: String) { + searchButtonText = text + if (isExpanded) { + invalidate() + } + } + + /** + * 璁剧疆灞曞紑鏂瑰悜 + */ + fun setExpandDirection(direction: Int) { + require(direction == EXPAND_LEFT || direction == EXPAND_RIGHT) { + "Direction must be either EXPAND_LEFT or EXPAND_RIGHT" + } + expandDirection = direction + } +} diff --git a/expand_button/src/main/res/drawable/bg_search_bar.xml b/expand_button/src/main/res/drawable/bg_search_bar.xml new file mode 100644 index 0000000..da386f2 --- /dev/null +++ b/expand_button/src/main/res/drawable/bg_search_bar.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <solid android:color="#80000000" /> + <corners android:radius="20dp" /> +</shape> \ No newline at end of file diff --git a/expand_button/src/main/res/drawable/ic_search.xml b/expand_button/src/main/res/drawable/ic_search.xml new file mode 100644 index 0000000..870d21c --- /dev/null +++ b/expand_button/src/main/res/drawable/ic_search.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="#FFffffff" + android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/> +</vector> \ No newline at end of file diff --git a/expand_button/src/main/res/layout/layout_search_bar.xml b/expand_button/src/main/res/layout/layout_search_bar.xml new file mode 100644 index 0000000..5655ad9 --- /dev/null +++ b/expand_button/src/main/res/layout/layout_search_bar.xml @@ -0,0 +1,42 @@ +<?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"> + + <ImageView + android:id="@+id/iv_search" + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_centerInParent="true" + android:src="@drawable/ic_search" /> + + <EditText + android:id="@+id/et_search" + android:layout_width="0dp" + android:layout_height="40dp" + android:layout_centerVertical="true" + android:layout_toEndOf="@id/iv_search" + android:layout_toStartOf="@id/tv_search" + android:layout_marginStart="8dp" + android:layout_marginEnd="8dp" + android:background="@null" + android:hint="璇疯緭鍏ュ垎姘存埧鎴栧彇姘村彛鍚嶇О" + android:textColorHint="#80FFFFFF" + android:textColor="#FFFFFF" + android:textSize="14sp" + android:singleLine="true" + android:visibility="gone"/> + + <TextView + android:id="@+id/tv_search" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentEnd="true" + android:layout_centerVertical="true" + android:layout_marginEnd="8dp" + android:text="鎼滅储" + android:textColor="#FFFFFF" + android:textSize="14sp" + android:visibility="gone"/> + +</RelativeLayout> \ No newline at end of file diff --git a/expand_button/src/main/res/values/attrs.xml b/expand_button/src/main/res/values/attrs.xml index aca0c25..b3cbaa3 100644 --- a/expand_button/src/main/res/values/attrs.xml +++ b/expand_button/src/main/res/values/attrs.xml @@ -18,4 +18,17 @@ <!-- 鏂板灞曞紑鍚庣殑瀛椾綋澶у皬灞炴�� --> <attr name="expandedTextSize" format="dimension"/> </declare-styleable> + + <declare-styleable name="CustomSearchBar"> + <!-- 浣跨敤android:background灞炴�� --> + <attr name="android:background"/> + <!-- 鎼滅储鍥炬爣澶у皬 --> + <attr name="searchIconSize" format="dimension"/> + <!-- 鎼滅储鎸夐挳鏂囧瓧 --> + <attr name="searchButtonText" format="string"/> + <!-- 鎻愮ず鏂囧瓧 --> + <attr name="searchHint" format="string"/> + <!-- 鍔ㄧ敾鏃堕暱 --> + <attr name="searchAnimDuration" format="integer"/> + </declare-styleable> </resources> \ No newline at end of file -- Gitblit v1.8.0