From 3df944d30530be8dc0ea1cbe1ed4afc22eb160a5 Mon Sep 17 00:00:00 2001
From: zuoxiao <470321431@qq.com>
Date: 星期四, 06 二月 2025 11:09:48 +0800
Subject: [PATCH] 1.添加数据更新功能,确保本地没有数据时再获取基础数据 2.地图界面上添加滚动功能的控件(部分功能)
---
app/src/main/java/com/dayu/pipirrapp/fragment/MyFragment.java | 5
app/src/main/java/com/dayu/pipirrapp/net/Constants.java | 4
app/src/main/java/com/dayu/pipirrapp/utils/CommonKeyName.java | 4
app/src/main/java/com/dayu/pipirrapp/dao/DivideDao.java | 4
app/src/main/java/com/dayu/pipirrapp/dao/MarkerDao.java | 5
expand_button/src/main/res/drawable/ic_triangle.xml | 14 +
app/src/main/res/layout/fragment_map.xml | 19 +
app/src/main/java/com/dayu/pipirrapp/dao/CenterPointDao.java | 11
expand_button/src/main/res/values/attrs.xml | 15 +
app/src/main/res/layout/fragment_my.xml | 27 ++
expand_button/src/main/java/com/example/expand_button/ExpandButton.kt | 307 ++++++++++++++++++++++++++++++
app/src/main/java/com/dayu/pipirrapp/fragment/MapFragment.java | 165 ++++++++++++---
app/build.gradle | 3
13 files changed, 536 insertions(+), 47 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 2211ad5..c28c784 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -89,7 +89,8 @@
}
dependencies {
- implementation project(':library')
+ implementation project(':expand_button')
+ implementation(project(':library'))
implementation project(':date_time_picker')
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.8.0'
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 8672cc1..05cb87e 100644
--- a/app/src/main/java/com/dayu/pipirrapp/dao/CenterPointDao.java
+++ b/app/src/main/java/com/dayu/pipirrapp/dao/CenterPointDao.java
@@ -8,7 +8,9 @@
import androidx.room.Update;
import com.dayu.pipirrapp.bean.db.CenterPointBean;
-import com.dayu.pipirrapp.bean.db.TagBean;
+
+import io.reactivex.rxjava3.core.Maybe;
+
/**
* author: zuo
@@ -18,8 +20,11 @@
*/
@Dao
public interface CenterPointDao {
+ @Query("SELECT * FROM CenterPointBean LIMIT 1")
+ Maybe<CenterPointBean> findFirst();
+
@Insert(onConflict = OnConflictStrategy.REPLACE)
- void insert(CenterPointBean adminData);
+ void insert(CenterPointBean centerPointBean);
@Update
void update(CenterPointBean adminData);
@@ -27,6 +32,4 @@
@Delete
void delete(CenterPointBean adminData);
- @Query("select * from CenterPointBean limit 1")
- CenterPointBean findFirst();
}
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 aad77d8..aa9295a 100644
--- a/app/src/main/java/com/dayu/pipirrapp/dao/DivideDao.java
+++ b/app/src/main/java/com/dayu/pipirrapp/dao/DivideDao.java
@@ -14,6 +14,7 @@
import io.reactivex.rxjava3.core.Completable;
import io.reactivex.rxjava3.core.Single;
+import io.reactivex.rxjava3.core.Maybe;
/**
* DivideDao -
@@ -50,4 +51,7 @@
@Query("select * from DivideBean")
Single<List<DivideBean>> findAllToSingle();
+
+ @Query("SELECT * FROM divide")
+ Maybe<List<DivideBean>> getAll(); // 鏀逛负杩斿洖Maybe<List<DivideBean>>
}
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 295e868..0323b6d 100644
--- a/app/src/main/java/com/dayu/pipirrapp/dao/MarkerDao.java
+++ b/app/src/main/java/com/dayu/pipirrapp/dao/MarkerDao.java
@@ -13,6 +13,7 @@
import io.reactivex.rxjava3.core.Completable;
import io.reactivex.rxjava3.core.Single;
+import io.reactivex.rxjava3.core.Maybe;
/**
* author: zuo
@@ -48,4 +49,8 @@
@Query("select * from MarkerBean")
Single<List<MarkerBean>> findAllToSingle();
+
+ @Query("SELECT * FROM MarkerBean")
+ Maybe<List<MarkerBean>> getAll();
+
}
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 39ec637..ddfb4c4 100644
--- a/app/src/main/java/com/dayu/pipirrapp/fragment/MapFragment.java
+++ b/app/src/main/java/com/dayu/pipirrapp/fragment/MapFragment.java
@@ -78,6 +78,7 @@
import java.util.stream.Collectors;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
+import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.schedulers.Schedulers;
/**
@@ -116,6 +117,9 @@
MarkerBean mMarkerBean;
+ // 娣诲姞CompositeDisposable鏉ョ鐞嗘墍鏈夎闃�
+ private CompositeDisposable compositeDisposable = new CompositeDisposable();
+
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
@@ -130,6 +134,13 @@
setRetainInstance(true);
Log.i(TAG, "onCreate");
mInspectionState = SharedPreferencesHelper.getInstance(this.getContext()).get(CommonKeyName.inspectionState, 0);
+
+ // 娣诲姞鍒锋柊鏁版嵁鐨勭洃鍚�
+ LiveEventBus.get(CommonKeyName.refreshData).observe(this, o -> {
+ getCenterPoint();
+ getMarkerData();
+ getDivideList();
+ });
}
@Override
@@ -142,7 +153,6 @@
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
-
binding = FragmentMapBinding.inflate(inflater, container, false);
mapFragmenObserver.setmWebView(binding.webView);
Log.i("MapFragment", "onCreateView");
@@ -151,15 +161,87 @@
MyWebViewInterface myWebViewInterface = new MyWebViewInterface(MapFragment.this);
mWebView.addJavascriptInterface(myWebViewInterface, "Android");
mWebView.loadUrl("file:///android_asset/index.html");
- getCenterPoint();
+
+ // 寮傛鍔犺浇鏈湴鏁版嵁
+ loadLocalData();
+
initView();
initWeb();
- getMarkerData();
- getDivideList();
-
chageInspecState(mInspectionState);
return binding.getRoot();
+ }
+
+ /**
+ * 寮傛鍔犺浇鏈湴鏁版嵁
+ */
+ private void loadLocalData() {
+ // 寮傛鍔犺浇涓績鐐规暟鎹�
+ compositeDisposable.add(
+ DaoSingleton.getAsynchInstance(this.getContext()).centerPointDao().findFirst()
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(result -> {
+ centerPointBean = result;
+ if (centerPointBean == null) {
+ getCenterPoint();
+ } else {
+ jumpCenterPoint();
+ }
+ }, throwable -> {
+ Log.e(TAG, "Load centerPoint error: " + throwable);
+ getCenterPoint();
+ }, () -> {
+ // 褰揗aybe涓虹┖鏃惰皟鐢�
+ getCenterPoint();
+ })
+ );
+
+ // 寮傛鍔犺浇鍙栨按鍙f暟鎹�
+ compositeDisposable.add(
+ DaoSingleton.getAsynchInstance(this.getContext()).markerDao().getAll()
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(markers -> {
+ if (markers == null || markers.isEmpty()) {
+ getMarkerData();
+ } else {
+ for (MarkerBean marker : markers) {
+ markerBeanSet.put(marker.getId(), marker);
+ setMapMarker(marker);
+ }
+ }
+ }, throwable -> {
+ Log.e(TAG, "Load markers error: " + throwable.getMessage());
+ getMarkerData();
+ }, () -> {
+ // 褰揗aybe涓虹┖鏃惰皟鐢�
+ getMarkerData();
+ })
+ );
+
+ // 寮傛鍔犺浇鍒嗘按鎴挎暟鎹�
+ compositeDisposable.add(
+ DaoSingleton.getAsynchInstance(this.getContext()).divideDao().getAll()
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(divides -> {
+ if (divides == null || divides.isEmpty()) {
+ getDivideList();
+ } else {
+ for (DivideBean divide : divides) {
+ divideBeanMap.put(divide.getId(), divide);
+ setMapDivide(divide);
+ }
+ }
+ }, throwable -> {
+ Log.e(TAG, "Load divides error: " + throwable.getMessage());
+ getDivideList();
+ }, () -> {
+ // 褰揗aybe涓虹┖鏃惰皟鐢�
+ getDivideList();
+ })
+ );
}
private void initWeb() {
@@ -232,9 +314,10 @@
* web鍔犺浇瀹屽垵濮嬪寲鏈湴鏁版嵁
*/
public void webFinishInitLocalData() {
- //璺宠浆涓績鐐�
- centerPointBean = DaoSingleton.getInstance(MapFragment.this.getContext()).centerPointDao().findFirst();
- jumpCenterPoint();
+ // 鍙湪鏈湴娌℃湁鏁版嵁鏃惰幏鍙栦腑蹇冪偣
+ if (centerPointBean != null) {
+ jumpCenterPoint();
+ }
//娣诲姞鍥爓ebview娌℃湁鍔犺浇瀹屾垚瀵艰嚧娌℃湁娣诲姞鐨勫湴鍥炬爣娉�
if (!webNoFinishMarkerData.isEmpty()) {
for (MarkerBean bean : webNoFinishMarkerData) {
@@ -285,18 +368,22 @@
})
.collect(Collectors.toList());
- DaoSingleton.getInstance(MapFragment.this.getContext()).markerDao().deleteAll();
- // 浣跨敤 RxJava 寮傛鎻掑叆鏁版嵁
- DaoSingleton.getAsynchInstance(MapFragment.this.getContext()).markerDao().insertAll(markerBeans)
- .subscribeOn(Schedulers.io()) // 鍦� IO 绾跨▼涓婃墽琛�
- .observeOn(AndroidSchedulers.mainThread()) // 鍦ㄤ富绾跨▼涓婅瀵�
- .subscribe(() -> {
- // 鎻掑叆鎴愬姛
- Log.i("mWebView", "鏁版嵁鎻掑叆鎴愬姛");
- }, throwable -> {
- // 鎻掑叆澶辫触
- Log.e("mWebView", "鏁版嵁鎻掑叆澶辫触: " + throwable.getMessage());
- });
+ // 浣跨敤 CompositeDisposable 绠$悊鏁版嵁搴撴彃鍏ユ搷浣�
+ compositeDisposable.add(
+ DaoSingleton.getAsynchInstance(MapFragment.this.getContext()).markerDao().insertAll(markerBeans)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(
+ () -> {
+ // 鎻掑叆鎴愬姛
+ Log.i("mWebView", "鏁版嵁鎻掑叆鎴愬姛");
+ },
+ throwable -> {
+ // 鎻掑叆澶辫触
+ Log.e("mWebView", "鏁版嵁鎻掑叆澶辫触: " + throwable.getMessage());
+ }
+ )
+ );
}
} else {
@@ -629,7 +716,7 @@
}
/**
- * 娣诲姞鍙栨按鍙f爣娉�
+ * 娣诲姞绠$綉鏍囨敞
*/
public void setMapDivide(PipeNetworkBean pipeNetworkBean) {
// if (divide != null) {
@@ -917,27 +1004,30 @@
try {
if (t.isSuccess()) {
if (t.getContent().getObj() != null && !t.getContent().getObj().isEmpty()) {
-
List<DivideBean> divideBeans = new ArrayList<>();
for (DivideResult divideResult : t.getContent().getObj()) {
DivideBean divideBean = getDivideBean(divideResult);
setMapDivide(divideBean);
divideBeans.add(divideBean);
}
- // 浣跨敤 RxJava 寮傛鎻掑叆鏁版嵁
- DaoSingleton.getAsynchInstance(MapFragment.this.getContext()).divideDao().insertAll(divideBeans)
- .subscribeOn(Schedulers.io()) // 鍦� IO 绾跨▼涓婃墽琛�
- .observeOn(AndroidSchedulers.mainThread()) // 鍦ㄤ富绾跨▼涓婅瀵�
- .subscribe(() -> {
- // 鎻掑叆鎴愬姛
- Log.i("mWebView", "鏁版嵁鎻掑叆鎴愬姛");
- }, throwable -> {
- // 鎻掑叆澶辫触
- Log.e("mWebView", "鏁版嵁鎻掑叆澶辫触: " + throwable.getMessage());
- });
+
+ // 浣跨敤 CompositeDisposable 绠$悊鏁版嵁搴撴彃鍏ユ搷浣�
+ compositeDisposable.add(
+ DaoSingleton.getAsynchInstance(MapFragment.this.getContext()).divideDao().insertAll(divideBeans)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(
+ () -> {
+ // 鎻掑叆鎴愬姛
+ Log.i("mWebView", "鏁版嵁鎻掑叆鎴愬姛");
+ },
+ throwable -> {
+ // 鎻掑叆澶辫触
+ Log.e("mWebView", "鏁版嵁鎻掑叆澶辫触: " + throwable.getMessage());
+ }
+ )
+ );
}
-
-
} else {
ToastUtil.showToastLong(MapFragment.this.getContext(), t.getMsg());
}
@@ -946,7 +1036,6 @@
CrashReport.postCatchedException(e);
}
}
-
});
}
@@ -1031,6 +1120,10 @@
@Override
public void onDestroy() {
super.onDestroy();
+ // 娓呯悊鎵�鏈夎闃�
+ if (compositeDisposable != null && !compositeDisposable.isDisposed()) {
+ compositeDisposable.dispose();
+ }
LiveEventBus.get(CommonKeyName.locationData).removeObserver(locationObserver);
if (mWebView != null) {
mWebView.destroy();
diff --git a/app/src/main/java/com/dayu/pipirrapp/fragment/MyFragment.java b/app/src/main/java/com/dayu/pipirrapp/fragment/MyFragment.java
index 085b0f1..4f63ea2 100644
--- a/app/src/main/java/com/dayu/pipirrapp/fragment/MyFragment.java
+++ b/app/src/main/java/com/dayu/pipirrapp/fragment/MyFragment.java
@@ -24,6 +24,7 @@
import com.dayu.pipirrapp.utils.ToastUtil;
import com.dayu.pipirrapp.view.ConfirmDialog;
import com.dayu.pipirrapp.view.TipUtil;
+import com.jeremyliao.liveeventbus.LiveEventBus;
/**
* author: zuo
@@ -94,6 +95,10 @@
Intent intent = new Intent(MyFragment.this.getContext(), IssueListActivity.class);
MyFragment.this.getActivity().startActivity(intent);
});
+ binding.refreshDataTV.setOnClickListener(v->{
+ // 鍙戦�佸埛鏂颁簨浠堕�氱煡MapFragment鍒锋柊鏁版嵁
+ LiveEventBus.get(CommonKeyName.refreshData).post(true);
+ });
}
private void initData() {
diff --git a/app/src/main/java/com/dayu/pipirrapp/net/Constants.java b/app/src/main/java/com/dayu/pipirrapp/net/Constants.java
index b892373..d0ae793 100644
--- a/app/src/main/java/com/dayu/pipirrapp/net/Constants.java
+++ b/app/src/main/java/com/dayu/pipirrapp/net/Constants.java
@@ -7,8 +7,8 @@
*/
public class Constants {
// public static final String BASE_URL = "http://192.168.10.52:8088";
-// public static final String BASE_URL = "https://no253541tf71.vicp.fun";
- public static final String BASE_URL = "http://192.168.40.166:54321";
+ public static final String BASE_URL = "https://no253541tf71.vicp.fun";
+// public static final String BASE_URL = "http://192.168.40.166:54321";
//public static final String BASE_URL = "http://fve2iz.natappfree.cc";
public static final String BASE_UPLOAD_FILE_URL = BASE_URL;
/**
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 6866ddf..1adf529 100644
--- a/app/src/main/java/com/dayu/pipirrapp/utils/CommonKeyName.java
+++ b/app/src/main/java/com/dayu/pipirrapp/utils/CommonKeyName.java
@@ -29,4 +29,8 @@
//鍒涘缓閫氱煡
public final static String CreateNotification="CreateNotification";
+ /**
+ * 鍒锋柊鏁版嵁浜嬩欢
+ */
+ public static final String refreshData = "refreshData";
}
diff --git a/app/src/main/res/layout/fragment_map.xml b/app/src/main/res/layout/fragment_map.xml
index 715b846..e0de4e9 100644
--- a/app/src/main/res/layout/fragment_map.xml
+++ b/app/src/main/res/layout/fragment_map.xml
@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
<!-- <com.github.lzyzsd.jsbridge.BridgeWebView-->
<!-- android:id="@+id/webView"-->
@@ -105,7 +106,21 @@
android:textColor="@color/white"
android:textSize="18sp" />
-
+ <com.example.expand_button.ExpandButton
+ android:id="@+id/expandButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="10dp"
+ android:background="@drawable/ic_green_bg"
+ android:layout_alignParentRight="true"
+ android:layout_marginTop="180dp"
+ android:textSize="18sp"
+ android:layout_marginRight="15dp"
+ android:textColor="@color/white"
+ app:letterSpacing="10dp"
+ app:expandedText="鐐瑰嚮灞曞紑"
+ app:collapsedText="鐐�"
+ app:animDuration="300" />
<RelativeLayout
android:id="@+id/pointRL"
android:layout_width="match_parent"
diff --git a/app/src/main/res/layout/fragment_my.xml b/app/src/main/res/layout/fragment_my.xml
index 636d209..6478c63 100644
--- a/app/src/main/res/layout/fragment_my.xml
+++ b/app/src/main/res/layout/fragment_my.xml
@@ -131,12 +131,37 @@
android:layout_marginRight="15dp"
android:src="@drawable/ic_right" />
</RelativeLayout>
+ <RelativeLayout
+ android:id="@+id/refreshDataRL"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/item_height"
+ android:layout_below="@+id/passwordRL"
+ android:layout_marginTop="1dp">
+ <TextView
+ android:id="@+id/refreshDataTV"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/item_height"
+ android:background="@color/white"
+ android:gravity="center_vertical"
+ android:paddingLeft="30dp"
+ android:text="鏇存柊鏈湴鏁版嵁"
+ android:textColor="@color/black"
+ android:textSize="@dimen/my_item_text_size" />
+
+ <ImageView
+ android:layout_width="25dp"
+ android:layout_height="25dp"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true"
+ android:layout_marginRight="15dp"
+ android:src="@drawable/ic_right" />
+ </RelativeLayout>
<RelativeLayout
android:id="@+id/cleanDataRL"
android:layout_width="match_parent"
android:layout_height="@dimen/item_height"
- android:layout_below="@+id/passwordRL"
+ android:layout_below="@+id/refreshDataRL"
android:layout_marginTop="1dp">
<TextView
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 841ce47..c024329 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
@@ -1,14 +1,319 @@
package com.example.expand_button
-class ExpandButton {
+import android.animation.ValueAnimator
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.drawable.Drawable
+import android.text.Spannable
+import android.text.SpannableStringBuilder
+import android.text.style.ClickableSpan
+import android.util.AttributeSet
+import android.view.Gravity
+import android.view.MotionEvent
+import android.view.View
+import androidx.appcompat.widget.AppCompatTextView
+import androidx.core.content.ContextCompat
+/**
+ * 鍙睍寮�鐨勬寜閽帶浠�
+ * 鍒濆鐘舵�佹樉绀哄崟涓瓧绗︼紝鐐瑰嚮鍚庡睍寮�鏄剧ず瀹屾暣鏂囧瓧
+ * 灞曞紑鍚庣殑姣忎釜瀛楃閮藉彲浠ュ崟鐙偣鍑�
+ */
+class ExpandButton @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0
+) : AppCompatTextView(context, attrs, defStyleAttr) {
+ // 灞曞紑鏃舵樉绀虹殑瀹屾暣鏂囧瓧
+ private var expandedText: String = ""
+ // 鏀惰捣鏃舵樉绀虹殑鍗曚釜瀛楃
+ private var collapsedText: String = ""
+ // 褰撳墠鏄惁澶勪簬灞曞紑鐘舵��
+ private var isExpanded: Boolean = false
+ // 鍔ㄧ敾鎸佺画鏃堕棿锛岄粯璁�300姣
+ private var animationDuration: Long = 300
+ // 瀛楃鐐瑰嚮浜嬩欢鐩戝惉鍣�
+ private var onCharClickListener: ((Char, Int) -> Unit)? = null
+ // 瀛楅棿璺濓紝榛樿涓�2dp
+ private var customLetterSpacing: Float = context.resources.displayMetrics.density * 2
+ // 涓夎褰㈠浘鏍�
+ private val triangleDrawable: Drawable = ContextCompat.getDrawable(
+ context,
+ R.drawable.ic_triangle
+ )!!.mutate()
+ // 鍥炬爣鏃嬭浆瑙掑害
+ private var triangleRotation = 0f
+ // 涓夎褰㈠浘鏍囦笌鏂囧瓧鐨勯棿璺濓紝榛樿涓�8dp
+ private var triangleMargin: Float = 3 * context.resources.displayMetrics.density
+ init {
+ // 璇诲彇鑷畾涔夊睘鎬�
+ context.theme.obtainStyledAttributes(
+ attrs,
+ R.styleable.ExpandButton,
+ defStyleAttr,
+ 0
+ ).apply {
+ try {
+ customLetterSpacing = getDimension(R.styleable.ExpandButton_letterSpacing, customLetterSpacing)
+ expandedText = getString(R.styleable.ExpandButton_expandedText) ?: ""
+ collapsedText = getString(R.styleable.ExpandButton_collapsedText) ?: ""
+ animationDuration = getInteger(R.styleable.ExpandButton_animDuration, 300).toLong()
+ triangleMargin = getDimension(R.styleable.ExpandButton_triangleMargin, triangleMargin)
+ } finally {
+ recycle()
+ }
+ }
+ // 璁剧疆鍒濆鏂囨湰鍜屽搴�
+ if (collapsedText.isNotEmpty()) {
+ text = collapsedText
+ post {
+ // 纭繚鍒濆瀹藉害涓烘敹璧风姸鎬佺殑瀹藉害
+ layoutParams = layoutParams.apply {
+ width = paint.measureText(collapsedText).toInt() + paddingLeft + paddingRight
+ }
+ }
+ }
+ // 璁剧疆鏂囨湰鍙偣鍑伙紝浠呭湪鏀惰捣鐘舵�佹椂鍝嶅簲鐐瑰嚮灞曞紑
+ setOnClickListener {
+ if (!isExpanded) {
+ toggleExpand()
+ }
+ }
+ // 娣诲姞瑙︽懜浜嬩欢澶勭悊
+ setOnTouchListener { _, event ->
+ when (event.action) {
+ MotionEvent.ACTION_DOWN -> {
+ // 妫�鏌ョ偣鍑绘槸鍚﹀湪涓夎褰㈠浘鏍囧尯鍩熷唴
+ if (isClickOnTriangle(event.x)) {
+ toggleExpand()
+ return@setOnTouchListener true
+ }
+ }
+ }
+ false
+ }
+ // 璁剧疆宸﹁竟璺濓紝涓哄浘鏍囩暀鍑虹┖闂�
+ compoundDrawablePadding = triangleMargin.toInt()
+ setPadding(
+ (16 * context.resources.displayMetrics.density + triangleMargin).toInt(), // 宸﹁竟璺濆鍔狅紝涓哄浘鏍囩暀绌洪棿
+ paddingTop,
+ paddingRight,
+ paddingBottom
+ )
+
+ // 璁剧疆鍗曡鏄剧ず锛岄槻姝㈤珮搴﹀彉鍖�
+ maxLines = 1
+ isSingleLine = true
+
+ // 璁剧疆鏂囧瓧鍨傜洿灞呬腑
+ gravity = Gravity.CENTER_VERTICAL
+ }
+
+ override fun onDraw(canvas: Canvas) {
+ // 淇濆瓨鐢诲竷鐘舵��
+ canvas.save()
+
+ // 璁$畻鍥炬爣浣嶇疆
+ val iconSize = triangleDrawable.intrinsicWidth
+ val iconLeft = paddingLeft - iconSize - compoundDrawablePadding
+ val iconTop = (height - iconSize) / 2
+
+ // 璁剧疆鍥炬爣杈圭晫
+ triangleDrawable.setBounds(
+ iconLeft,
+ iconTop,
+ iconLeft + iconSize,
+ iconTop + iconSize
+ )
+
+ // 鏃嬭浆鐢诲竷
+ canvas.rotate(
+ triangleRotation,
+ (iconLeft + iconSize / 2).toFloat(),
+ (iconTop + iconSize / 2).toFloat()
+ )
+
+ // 缁樺埗鍥炬爣
+ triangleDrawable.draw(canvas)
+
+ // 鎭㈠鐢诲竷鐘舵��
+ canvas.restore()
+
+ super.onDraw(canvas)
+ }
+
+ /**
+ * 璁剧疆瀛楅棿璺�
+ * @param spacing 闂磋窛鍊硷紙鍍忕礌锛�
+ */
+ fun setCustomLetterSpacing(spacing: Float) {
+ this.customLetterSpacing = spacing
+ if (isExpanded) {
+ setExpandedClickableText()
+ }
+ }
+
+ /**
+ * 璁剧疆灞曞紑鍜屾敹璧锋椂鏄剧ず鐨勬枃瀛�
+ * @param expanded 灞曞紑鏃舵樉绀虹殑瀹屾暣鏂囧瓧
+ * @param collapsed 鏀惰捣鏃舵樉绀虹殑鍗曚釜瀛楃
+ */
+ fun setExpandText(expanded: String, collapsed: String) {
+ this.expandedText = expanded
+ this.collapsedText = collapsed
+ text = collapsedText
+ }
+
+ /**
+ * 璁剧疆鍗曚釜瀛楃鐐瑰嚮鐩戝惉鍣�
+ * @param listener 鐐瑰嚮鍥炶皟锛屽弬鏁颁负琚偣鍑荤殑瀛楃鍜屼綅缃�
+ * char: 琚偣鍑荤殑瀛楃
+ * position: 瀛楃鍦ㄦ枃鏈腑鐨勪綅缃紙浠�0寮�濮嬶級
+ */
+ fun setOnCharClickListener(listener: (char: Char, position: Int) -> Unit) {
+ this.onCharClickListener = listener
+ }
+
+ /**
+ * 璁剧疆灞曞紑/鏀惰捣鍔ㄧ敾鐨勬寔缁椂闂�
+ * @param duration 鍔ㄧ敾鎸佺画鏃堕棿锛堟绉掞級
+ */
+ fun setAnimationDuration(duration: Long) {
+ this.animationDuration = duration
+ }
+
+ /**
+ * 鍒囨崲灞曞紑/鏀惰捣鐘舵��
+ * 浣跨敤ValueAnimator瀹炵幇瀹藉害鍔ㄧ敾鍜屽浘鏍囨棆杞�
+ */
+ private fun toggleExpand() {
+ isExpanded = !isExpanded
+
+ // 璁$畻鏀惰捣鍜屽睍寮�鐘舵�佺殑瀹藉害
+ val collapsedWidth = paint.measureText(collapsedText).toInt() + paddingLeft + paddingRight
+ val expandedWidth = calculateExpandedWidth()
+
+ // 鍒涘缓瀹藉害鍔ㄧ敾
+ ValueAnimator.ofInt(
+ if (isExpanded) collapsedWidth else expandedWidth,
+ if (isExpanded) expandedWidth else collapsedWidth
+ ).apply {
+ duration = animationDuration
+ addUpdateListener { animator ->
+ layoutParams = layoutParams.apply {
+ width = animator.animatedValue as Int
+ }
+ requestLayout()
+ }
+ start()
+ }
+
+ // 鍒涘缓鍥炬爣鏃嬭浆鍔ㄧ敾
+ ValueAnimator.ofFloat(
+ if (isExpanded) 0f else 180f,
+ if (isExpanded) 180f else 0f
+ ).apply {
+ duration = animationDuration
+ addUpdateListener { animator ->
+ triangleRotation = animator.animatedValue as Float
+ invalidate() // 閲嶇粯浠ユ洿鏂板浘鏍囨棆杞�
+ }
+ start()
+ }
+
+ // 鏇存柊鏂囨湰
+ if (isExpanded) {
+ setExpandedClickableText()
+ } else {
+ text = collapsedText
+ }
+ }
+
+ /**
+ * 璁$畻灞曞紑鍚庣殑鎬诲搴�
+ */
+ private fun calculateExpandedWidth(): Int {
+ val spaceWidth = paint.measureText(" ") * (customLetterSpacing / 10)
+ // 璁$畻鎵�鏈夊瓧绗︾殑鎬诲搴�
+ val textWidth = expandedText.fold(0f) { acc, char ->
+ acc + paint.measureText(char.toString())
+ }
+ // 璁$畻闂磋窛鐨勬�诲搴︼紙瀛楃鏁伴噺鍑�1涓棿璺濓級
+ val spacesWidth = spaceWidth * (expandedText.length - 1)
+ return (textWidth + spacesWidth).toInt() + paddingLeft + paddingRight
+ }
+
+ /**
+ * 璁剧疆灞曞紑鍚庣殑鍙偣鍑绘枃鏈�
+ */
+ private fun setExpandedClickableText() {
+ val builder = SpannableStringBuilder()
+ expandedText.forEachIndexed { index, char ->
+ // 娣诲姞瀛楃
+ builder.append(char)
+
+ // 涓哄瓧绗﹁缃偣鍑讳簨浠�
+ val clickableSpan = object : ClickableSpan() {
+ override fun onClick(view: View) {
+ onCharClickListener?.invoke(char, index)
+ }
+
+ override fun updateDrawState(ds: android.text.TextPaint) {
+ super.updateDrawState(ds)
+ // 绉婚櫎涓嬪垝绾�
+ ds.isUnderlineText = false
+ // 淇濇寔鍘熷鏂囧瓧棰滆壊
+ ds.color = currentTextColor
+ }
+ }
+ builder.setSpan(
+ clickableSpan,
+ builder.length - 1,
+ builder.length,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
+ )
+
+ // 鍙湪闈炴渶鍚庝竴涓瓧绗﹀悗娣诲姞绌烘牸浣滀负闂磋窛
+ if (index < expandedText.length - 1) {
+ builder.append(" ".repeat((customLetterSpacing / 10).toInt()))
+ }
+ }
+
+ text = builder
+ // 鍚敤LinkMovementMethod浠ュ搷搴擟lickableSpan鐨勭偣鍑讳簨浠�
+ movementMethod = android.text.method.LinkMovementMethod.getInstance()
+ }
+
+ /**
+ * 鍒ゆ柇鐐瑰嚮鏄惁鍦ㄤ笁瑙掑舰鍥炬爣鍖哄煙鍐�
+ */
+ private fun isClickOnTriangle(x: Float): Boolean {
+ val iconSize = triangleDrawable.intrinsicWidth
+ val iconLeft = paddingLeft - iconSize - compoundDrawablePadding
+ return x <= paddingLeft && x >= iconLeft
+ }
+
+ /**
+ * 璁剧疆涓夎褰㈠浘鏍囦笌鏂囧瓧鐨勯棿璺�
+ * @param margin 闂磋窛鍊硷紙鍍忕礌锛�
+ */
+ fun setTriangleMargin(margin: Float) {
+ this.triangleMargin = margin
+ compoundDrawablePadding = margin.toInt()
+ setPadding(
+ (16 * context.resources.displayMetrics.density + margin).toInt(),
+ paddingTop,
+ paddingRight,
+ paddingBottom
+ )
+ }
}
\ No newline at end of file
diff --git a/expand_button/src/main/res/drawable/ic_triangle.xml b/expand_button/src/main/res/drawable/ic_triangle.xml
new file mode 100644
index 0000000..86a68e8
--- /dev/null
+++ b/expand_button/src/main/res/drawable/ic_triangle.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="16dp"
+ android:height="16dp"
+ android:viewportWidth="48"
+ android:viewportHeight="48">
+ <path
+ android:pathData="M19,12L31,24L19,36"
+ android:strokeLineJoin="round"
+ android:strokeWidth="4"
+ android:fillColor="#00000000"
+ android:strokeColor="#757575"
+ android:strokeLineCap="round"/>
+</vector>
\ 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
new file mode 100644
index 0000000..1d34c70
--- /dev/null
+++ b/expand_button/src/main/res/values/attrs.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <declare-styleable name="ExpandButton">
+ <!-- 瀛楅棿璺� -->
+ <attr name="letterSpacing" format="dimension"/>
+ <!-- 灞曞紑鏃舵樉绀虹殑鏂囧瓧 -->
+ <attr name="expandedText" format="string"/>
+ <!-- 鏀惰捣鏃舵樉绀虹殑鏂囧瓧 -->
+ <attr name="collapsedText" format="string"/>
+ <!-- 鍔ㄧ敾鏃堕暱 -->
+ <attr name="animDuration" format="integer"/>
+ <!-- 涓夎褰㈠浘鏍囦笌鏂囧瓧鐨勯棿璺� -->
+ <attr name="triangleMargin" format="dimension"/>
+ </declare-styleable>
+</resources>
\ No newline at end of file
--
Gitblit v1.8.0