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