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