18034727885
2026-06-17 1301dd4ee86d9938d630e61f16c12de511b785fa
修复问题
1个文件已添加
8个文件已修改
251 ■■■■■ 已修改文件
app/src/main/java/com/xw/yunwei/ui/user/login/LoginActivity.kt 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/xw/yunwei/ui/widget/ClearEditText.kt 200 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/layout/activity_car_manage.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/layout/activity_find_psw.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/layout/activity_login.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/layout/activity_select_car.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/layout/activity_site_nav.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/layout/fragment_task.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/values/attrs.xml 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/xw/yunwei/ui/user/login/LoginActivity.kt
@@ -160,7 +160,7 @@
                    binding.psw.visibility=View.VISIBLE
                    binding.eye2.visibility=View.VISIBLE
                    binding.vPswLine.visibility=View.VISIBLE
                    binding.forgetPsw.visibility=View.VISIBLE
                    binding.forgetPsw.visibility=View.GONE
                }
            }
        }
app/src/main/java/com/xw/yunwei/ui/widget/ClearEditText.kt
New file
@@ -0,0 +1,200 @@
package com.xw.yunwei.ui.widget
import android.annotation.SuppressLint
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Canvas
import android.graphics.Rect
import android.graphics.RectF
import android.graphics.drawable.Drawable
import android.text.Editable
import android.text.TextWatcher
import android.util.AttributeSet
import android.view.MotionEvent
import androidx.appcompat.widget.AppCompatEditText
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.DrawableCompat
import androidx.core.graphics.withClip
import com.xw.yunwei.R
/**
 * 自定义 EditText,右侧带有可自定义大小和位置的清除按钮(Canvas 绘制)
 */
class ClearEditText @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = androidx.appcompat.R.attr.editTextStyle
) : AppCompatEditText(context, attrs, defStyleAttr) {
    // 清除按钮相关
    private var clearIcon: Drawable? = null
    private var iconWidth: Int = 0
    private var iconHeight: Int = 0
    private var iconMarginRight: Int = 0
    private var iconMarginTop: Int = 0
    private var showClearIcon: Boolean = true
    private var iconTint: ColorStateList? = null
    // 图标绘制区域
    private var iconRect = RectF()
    // 图标绘制区域
    private var iconRectBound = Rect()
    private var isIconVisible = false
    // 默认清除图标资源
    private val defaultClearIconRes = android.R.drawable.ic_menu_close_clear_cancel
    init {
        // 解析自定义属性
        val typedArray =
            context.obtainStyledAttributes(attrs, R.styleable.ClearEditText, defStyleAttr, 0)
        val clearIconRes =
            typedArray.getResourceId(R.styleable.ClearEditText_clearIcon, defaultClearIconRes)
        iconWidth = typedArray.getDimensionPixelSize(R.styleable.ClearEditText_iconWidth, iconWidth)
        iconHeight =
            typedArray.getDimensionPixelSize(R.styleable.ClearEditText_iconHeight, iconHeight)
        iconMarginRight =
            typedArray.getDimensionPixelSize(
                R.styleable.ClearEditText_iconMarginRight,
                iconMarginRight
            )
        iconMarginTop =
            typedArray.getDimensionPixelSize(R.styleable.ClearEditText_iconMarginTop, iconMarginTop)
        showClearIcon =
            typedArray.getBoolean(R.styleable.ClearEditText_showClearIcon, showClearIcon)
        iconTint = typedArray.getColorStateList(R.styleable.ClearEditText_iconTint)
        typedArray.recycle()
        // 加载图标
        val drawable = ContextCompat.getDrawable(context, clearIconRes)
        clearIcon = drawable?.let {
            val wrapped = DrawableCompat.wrap(it)
            if (iconTint != null) {
                DrawableCompat.setTintList(wrapped, iconTint)
            } else {
                // 默认使用 hint 文字颜色
                DrawableCompat.setTintList(wrapped, hintTextColors)
            }
            wrapped
        }
        // 添加文本变化监听
        addTextChangedListener(object : TextWatcher {
            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
                updateIconVisibility()
            }
            override fun afterTextChanged(s: Editable?) {}
        })
        // 焦点变化监听
        setOnFocusChangeListener { _, _ ->
            updateIconVisibility()
        }
        // 初始更新可见性
        post { updateIconVisibility() }
    }
    /**
     * 更新图标可见性
     */
    private fun updateIconVisibility() {
        val shouldShow = showClearIcon && hasFocus() && text?.isNotEmpty() == true
        if (isIconVisible != shouldShow) {
            isIconVisible = shouldShow
            invalidate()
        }
    }
    /**
     * 计算图标绘制区域
     */
    private fun calculateIconRect() {
        if (clearIcon == null) return
        val paddingRight = paddingEnd
        val availableHeight = height - paddingTop - paddingBottom
        // 图标右边位置 = 控件右边 - paddingRight - iconMarginRight
        val right = width - paddingRight - iconMarginRight
        val left = right - iconWidth
        // 垂直居中,加上 marginTop 微调
        val centerY = paddingTop + availableHeight / 2f + iconMarginTop
        val top = centerY - iconHeight / 2f
        val bottom = centerY + iconHeight / 2f
        iconRect.set(left.toFloat(), top, right.toFloat(), bottom)
    }
    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        calculateIconRect()
    }
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        // 绘制清除图标
        if (isIconVisible && clearIcon != null) {
            canvas.withClip(iconRect) {
                // 剪切到图标区域(可选,防止绘制超出)
                iconRectBound.apply {
                    left = iconRect.left.toInt()
                    top = iconRect.top.toInt()
                    right = iconRect.right.toInt()
                    bottom = iconRect.bottom.toInt()
                }
                // 设置图标的边界
                clearIcon?.bounds = iconRectBound
                clearIcon?.draw(this)
            }
        }
    }
    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(event: MotionEvent): Boolean {
        if (event.action == MotionEvent.ACTION_UP) {
            val x = event.x
            val y = event.y
            // 检查点击是否在图标区域内
            if (isIconVisible && iconRect.contains(x, y)) {
                text?.clear()
                // 清除后保持焦点
                requestFocus()
                performClick() // 辅助功能
                return true
            }
        }
        return super.onTouchEvent(event)
    }
    /**
     * 设置清除按钮是否可见
     */
    fun setShowClearIcon(show: Boolean) {
        showClearIcon = show
        updateIconVisibility()
    }
    /**
     * 设置清除图标资源
     */
    fun setClearIconRes(@androidx.annotation.DrawableRes resId: Int) {
        val drawable = ContextCompat.getDrawable(context, resId)
        clearIcon = drawable?.let {
            val wrapped = DrawableCompat.wrap(it)
            if (iconTint != null) {
                DrawableCompat.setTintList(wrapped, iconTint)
            } else {
                DrawableCompat.setTintList(wrapped, hintTextColors)
            }
            wrapped
        }
        invalidate()
    }
}
app/src/main/res/layout/activity_car_manage.xml
@@ -60,7 +60,7 @@
                android:paddingHorizontal="@dimen/dp_10"
                app:layout_constraintTop_toTopOf="parent">
                <androidx.appcompat.widget.AppCompatEditText
                <com.xw.yunwei.ui.widget.ClearEditText
                    android:id="@+id/searchEt"
                    android:layout_width="0dp"
                    android:layout_height="@dimen/dp_34"
@@ -70,6 +70,9 @@
                    android:drawableStart="@drawable/svg_search"
                    android:drawablePadding="@dimen/dp_10"
                    android:hint="请输入车牌号"
                    app:iconHeight="@dimen/dp_15"
                    app:iconWidth="@dimen/dp_15"
                    app:clearIcon="@mipmap/ic_close_black2"
                    android:imeOptions="actionSearch"
                    android:paddingHorizontal="@dimen/dp_10"
                    android:singleLine="true"
app/src/main/res/layout/activity_find_psw.xml
@@ -40,7 +40,7 @@
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
        <androidx.appcompat.widget.AppCompatEditText
        <com.xw.yunwei.ui.widget.ClearEditText
            android:id="@+id/phone"
            android:layout_width="match_parent"
            android:layout_height="@dimen/dp_56"
@@ -49,6 +49,10 @@
            android:background="@null"
            android:gravity="center_vertical"
            android:hint="手机号"
            app:iconHeight="@dimen/dp_15"
            app:iconWidth="@dimen/dp_15"
            app:iconMarginRight="@dimen/dp_10"
            app:clearIcon="@mipmap/ic_close_black2"
            android:inputType="phone"
            android:text="@={viewModel.phone}"
            android:textColor="@color/gray_10"
app/src/main/res/layout/activity_login.xml
@@ -58,7 +58,7 @@
            app:tabTextAppearance="@style/TabTextStyle"
            app:tabTextColor="@color/gray_7" />
        <androidx.appcompat.widget.AppCompatEditText
        <com.xw.yunwei.ui.widget.ClearEditText
            android:id="@+id/phone"
            android:layout_width="match_parent"
            android:layout_height="@dimen/dp_56"
@@ -67,6 +67,10 @@
            android:background="@null"
            android:gravity="center_vertical"
            android:hint="手机号"
            app:iconHeight="@dimen/dp_15"
            app:iconWidth="@dimen/dp_15"
            app:iconMarginRight="@dimen/dp_10"
            app:clearIcon="@mipmap/ic_close_black2"
            android:inputType="phone"
            android:text="@={viewModel.phone}"
            android:textColor="@color/gray_10"
@@ -157,6 +161,7 @@
            android:layout_marginTop="@dimen/dp_12"
            android:onClick="@{viewModel.onClick}"
            android:text="忘记密码"
            android:visibility="gone"
            android:textColor="@color/gray_6"
            android:textSize="@dimen/sp_12"
            app:layout_constraintEnd_toEndOf="@+id/psw"
app/src/main/res/layout/activity_select_car.xml
@@ -59,7 +59,7 @@
                app:layout_constraintTop_toBottomOf="@+id/rlTitleBar"
                tools:ignore="MissingConstraints">
                <androidx.appcompat.widget.AppCompatEditText
                <com.xw.yunwei.ui.widget.ClearEditText
                    android:id="@+id/searchEt"
                    android:layout_width="0dp"
                    android:layout_height="@dimen/dp_34"
@@ -69,6 +69,9 @@
                    android:drawablePadding="@dimen/dp_10"
                    android:hint="请输入车牌号"
                    android:maxLength="8"
                    app:iconHeight="@dimen/dp_15"
                    app:iconWidth="@dimen/dp_15"
                    app:clearIcon="@mipmap/ic_close_black2"
                    android:imeOptions="actionSearch"
                    android:paddingHorizontal="@dimen/dp_10"
                    android:singleLine="true"
app/src/main/res/layout/activity_site_nav.xml
@@ -79,7 +79,7 @@
                android:paddingHorizontal="@dimen/dp_10"
                app:layout_constraintTop_toTopOf="parent">
                <androidx.appcompat.widget.AppCompatEditText
                <com.xw.yunwei.ui.widget.ClearEditText
                    android:id="@+id/searchEt"
                    android:layout_width="0dp"
                    android:layout_height="@dimen/dp_34"
@@ -89,6 +89,9 @@
                    android:drawableStart="@drawable/svg_search"
                    android:drawablePadding="@dimen/dp_10"
                    android:hint="请输入站点名称"
                    app:iconHeight="@dimen/dp_15"
                    app:iconWidth="@dimen/dp_15"
                    app:clearIcon="@mipmap/ic_close_black2"
                    android:imeOptions="actionSearch"
                    android:paddingHorizontal="@dimen/dp_10"
                    android:singleLine="true"
app/src/main/res/layout/fragment_task.xml
@@ -73,11 +73,14 @@
            app:layout_constraintTop_toBottomOf="@+id/rlTitleBar"
            tools:ignore="MissingConstraints">
            <androidx.appcompat.widget.AppCompatEditText
            <com.xw.yunwei.ui.widget.ClearEditText
                android:id="@+id/searchEt"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                app:iconHeight="@dimen/dp_15"
                app:iconWidth="@dimen/dp_15"
                app:clearIcon="@mipmap/ic_close_black2"
                android:background="@drawable/shape_white_bg_4"
                android:drawableStart="@mipmap/ic_search_task"
                android:drawablePadding="@dimen/dp_6"
app/src/main/res/values/attrs.xml
@@ -40,4 +40,20 @@
        <attr name="spacingY" format="dimension" />
        <attr name="isStaggered" format="boolean" />
    </declare-styleable>
    <declare-styleable name="ClearEditText">
        <!-- 清除按钮图标 -->
        <attr name="clearIcon" format="reference" />
        <!-- 图标宽度 -->
        <attr name="iconWidth" format="dimension" />
        <!-- 图标高度 -->
        <attr name="iconHeight" format="dimension" />
        <!-- 图标距离右边的距离 -->
        <attr name="iconMarginRight" format="dimension" />
        <!-- 图标距离顶部的偏移(用于垂直居中微调) -->
        <attr name="iconMarginTop" format="dimension" />
        <!-- 是否显示清除按钮(默认true) -->
        <attr name="showClearIcon" format="boolean" />
        <!-- 图标着色(可选) -->
        <attr name="iconTint" format="color|reference" />
    </declare-styleable>
</resources>