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.地图界面上添加滚动功能的控件(部分功能)

---
 expand_button/src/main/java/com/example/expand_button/ExpandButton.kt |  307 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 306 insertions(+), 1 deletions(-)

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

--
Gitblit v1.8.0