Android 自定义EditText

文章目录

  • Android 自定义EditText
    • 概述
    • 源码
      • 可清空内容的EditText
      • 可显示密码的EditText
    • 使用
    • 源码下载

Android 自定义EditText

概述

定义一款可清空内容的 ClearEditText 和可显示密码的 PasswordEditText,支持修改提示图标和大小、背景图片等。

在这里插入图片描述

源码

基类:

open class BaseEditText @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = android.R.attr.editTextStyle
) : androidx.appcompat.widget.AppCompatEditText(context, attrs, defStyleAttr) {

    companion object {
        @JvmStatic
        protected val DEFAULT_DRAWABLE = ColorDrawable(0xFFFFFFFF.toInt())
    }

    init {
        gravity = Gravity.CENTER_VERTICAL
        background = DEFAULT_DRAWABLE
    }

    override fun setLayoutParams(params: ViewGroup.LayoutParams) {
        if (params.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
            params.width = ViewGroup.LayoutParams.MATCH_PARENT
        }
        super.setLayoutParams(params)
    }
}

可清空内容的EditText

定义属性:

<declare-styleable name="ClearEditText">
    <attr name="cet_deleteIcon" format="reference" />
    <attr name="cet_deleteIconSize" format="dimension" />
    <attr name="cet_tipDefaultIcon" format="reference" />
    <attr name="cet_tipSelectedIcon" format="reference" />
    <attr name="cet_tipIconSize" format="dimension" />
    <attr name="cet_defaultBg" format="color|reference" />
    <attr name="cet_selectedBg" format="color|reference" />
</declare-styleable>

定义ClearEditText:

class ClearEditText @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null
) : BaseEditText(context, attrs) {

    private val deleteIconDrawable: Drawable?
    private val tipIconDefaultDrawable: Drawable?
    private val tipIconSelectedDrawable: Drawable?
    private val bgDefaultDrawable: Drawable?
    private val bgSelectedDrawable: Drawable?

    init {
        val a: TypedArray = context.obtainStyledAttributes(attrs, R.styleable.ClearEditText)
        val deleteIconSize =
            a.getDimensionPixelSize(R.styleable.ClearEditText_cet_deleteIconSize, 0)
        deleteIconDrawable = a.getDrawable(R.styleable.ClearEditText_cet_deleteIcon)
        deleteIconDrawable?.let { it ->
            if (deleteIconSize > 0) {
                it.setBounds(0, 0, deleteIconSize, deleteIconSize)
            } else {
                it.setBounds(0, 0, it.intrinsicWidth, it.intrinsicHeight)
            }
        }
        val tipIconSize = a.getDimensionPixelSize(R.styleable.ClearEditText_cet_tipIconSize, 0)
        tipIconDefaultDrawable = a.getDrawable(R.styleable.ClearEditText_cet_tipDefaultIcon)
        tipIconDefaultDrawable?.let { it ->
            if (tipIconSize > 0) {
                it.setBounds(0, 0, tipIconSize, tipIconSize)
            } else {
                it.setBounds(0, 0, it.intrinsicWidth, it.intrinsicHeight)
            }
        }
        tipIconSelectedDrawable = a.getDrawable(R.styleable.ClearEditText_cet_tipSelectedIcon)
        tipIconSelectedDrawable?.let { it ->
            if (tipIconSize > 0) {
                it.setBounds(0, 0, tipIconSize, tipIconSize)
            } else {
                it.setBounds(0, 0, it.intrinsicWidth, it.intrinsicHeight)
            }
        }
        bgDefaultDrawable = a.getDrawable(R.styleable.ClearEditText_cet_defaultBg)
        bgSelectedDrawable = a.getDrawable(R.styleable.ClearEditText_cet_selectedBg)
        a.recycle()

        setup()
    }

    private fun setup() {
        setIconVisible(false, false)
        bgDefaultDrawable?.let {
            background = it
        }
    }

    override fun onTextChanged(
        text: CharSequence,
        start: Int,
        lengthBefore: Int,
        lengthAfter: Int
    ) {
        super.onTextChanged(text, start, lengthBefore, lengthAfter)
        setIconVisible(hasFocus() && text.length > 0, hasFocus())
    }

    override fun onFocusChanged(focused: Boolean, direction: Int, previouslyFocusedRect: Rect?) {
        super.onFocusChanged(focused, direction, previouslyFocusedRect)
        setIconVisible(focused && length() > 0, focused)
    }

    private fun setIconVisible(deleteIconVisible: Boolean, focused: Boolean) {
        setCompoundDrawablesRelative(
            if (focused) tipIconSelectedDrawable else tipIconDefaultDrawable,
            null,
            if (deleteIconVisible) deleteIconDrawable else null,
            null
        )
        if (bgDefaultDrawable != null && bgSelectedDrawable != null) {
            background = if (focused) bgSelectedDrawable else bgDefaultDrawable
        }
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        if (event.action == MotionEvent.ACTION_UP) {
            val drawable = deleteIconDrawable
            if (drawable != null) {
                if (event.x <= width - paddingRight && event.x >= width - paddingRight - drawable.bounds.width()) {
                    text = null
                }
            }
        }
        return super.onTouchEvent(event)
    }
}

可显示密码的EditText

定义属性:

<declare-styleable name="PasswordEditText">
    <attr name="pet_eyeIconSize" format="dimension" />
    <attr name="pet_tipDefaultIcon" format="reference" />
    <attr name="pet_tipSelectedIcon" format="reference" />
    <attr name="pet_tipIconSize" format="dimension" />
    <attr name="pet_defaultBg" format="color|reference" />
    <attr name="pet_selectedBg" format="color|reference" />
</declare-styleable>

定义PasswordEditText:

class PasswordEditText @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null
) : BaseEditText(context, attrs) {

    private val eyeOpenDrawable: Drawable?
    private val eyeCloseDrawable: Drawable?
    private var currentEyeDrawable: Drawable? = null
    private var tipIconDefaultDrawable: Drawable?
    private var tipIconSelectedDrawable: Drawable?
    private var bgDefaultDrawable: Drawable?
    private var bgSelectedDrawable: Drawable?

    init {
        val a: TypedArray = context.obtainStyledAttributes(attrs, R.styleable.PasswordEditText)

        val eyeIconSize = a.getDimensionPixelSize(R.styleable.PasswordEditText_pet_eyeIconSize, 0)
        eyeOpenDrawable = ContextCompat.getDrawable(context, R.drawable.eye_open)
        eyeOpenDrawable?.let {
            if (eyeIconSize > 0) {
                it.setBounds(0, 0, eyeIconSize, eyeIconSize)
            } else {
                it.setBounds(0, 0, it.intrinsicWidth, it.intrinsicHeight)
            }
        }
        eyeCloseDrawable = ContextCompat.getDrawable(context, R.drawable.eye_close)
        eyeCloseDrawable?.let {
            if (eyeIconSize > 0) {
                it.setBounds(0, 0, eyeIconSize, eyeIconSize)
            } else {
                it.setBounds(0, 0, it.intrinsicWidth, it.intrinsicHeight)
            }
        }
        val tipIconSize = a.getDimensionPixelSize(R.styleable.PasswordEditText_pet_tipIconSize, 0)
        tipIconDefaultDrawable = a.getDrawable(R.styleable.PasswordEditText_pet_tipDefaultIcon)
        tipIconDefaultDrawable?.let { it ->
            if (tipIconSize > 0) {
                it.setBounds(0, 0, tipIconSize, tipIconSize)
            } else {
                it.setBounds(0, 0, it.intrinsicWidth, it.intrinsicHeight)
            }
        }
        tipIconSelectedDrawable = a.getDrawable(R.styleable.PasswordEditText_pet_tipSelectedIcon)
        tipIconSelectedDrawable?.let { it ->
            if (tipIconSize > 0) {
                it.setBounds(0, 0, tipIconSize, tipIconSize)
            } else {
                it.setBounds(0, 0, it.intrinsicWidth, it.intrinsicHeight)
            }
        }
        bgDefaultDrawable = a.getDrawable(R.styleable.PasswordEditText_pet_defaultBg)
        bgSelectedDrawable = a.getDrawable(R.styleable.PasswordEditText_pet_selectedBg)
        a.recycle()

        setup()
    }

    private fun setup() {
        setIconVisible(false, false)
        currentEyeDrawable = eyeCloseDrawable
        bgDefaultDrawable?.let {
            background = it
        }
        inputType = InputType.TYPE_TEXT_VARIATION_PASSWORD
        transformationMethod = PasswordTransformationMethod.getInstance()
    }

    override fun onTextChanged(
        text: CharSequence,
        start: Int,
        lengthBefore: Int,
        lengthAfter: Int
    ) {
        super.onTextChanged(text, start, lengthBefore, lengthAfter)
        setIconVisible(hasFocus() && text.length > 0, hasFocus())
    }

    override fun onFocusChanged(focused: Boolean, direction: Int, previouslyFocusedRect: Rect?) {
        super.onFocusChanged(focused, direction, previouslyFocusedRect)
        setIconVisible(focused && length() > 0, focused)
    }

    private fun setIconVisible(pwdIconVisible: Boolean, focused: Boolean) {
        setCompoundDrawablesRelative(
            if (focused) tipIconSelectedDrawable else tipIconDefaultDrawable,
            null,
            if (pwdIconVisible) currentEyeDrawable else null,
            null
        )
        if (bgDefaultDrawable != null && bgSelectedDrawable != null) {
            background = if (focused) bgSelectedDrawable else bgDefaultDrawable
        }
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        if (event.action == MotionEvent.ACTION_UP) {
            val drawable = currentEyeDrawable
            if (drawable != null) {
                if (event.x <= width - paddingRight && event.x >= width - paddingRight - drawable.bounds.width()) {
                    if (drawable == eyeOpenDrawable) {
                        // 密码不可见
                        currentEyeDrawable = eyeCloseDrawable
                        transformationMethod = PasswordTransformationMethod.getInstance()
                        refreshDrawables()
                    } else if (drawable == eyeCloseDrawable) {
                        // 密码可见
                        currentEyeDrawable = eyeOpenDrawable
                        transformationMethod = HideReturnsTransformationMethod.getInstance()
                        refreshDrawables()
                    }
                }
            }
        }
        return super.onTouchEvent(event)
    }

    private fun refreshDrawables() {
        val drawables = compoundDrawablesRelative
        setCompoundDrawablesRelative(drawables[0], drawables[1], currentEyeDrawable, drawables[3])
    }
}

使用

<com.example.widgets.custom_edittext.ClearEditText
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginHorizontal="30dp"
    android:layout_marginTop="60dp"
    android:hint="请输入用户名"
    android:padding="10dp"
    android:singleLine="true"
    android:textSize="20sp"
    app:cet_defaultBg="@drawable/shape_border_gray"
    app:cet_deleteIcon="@drawable/ic_delete_x"
    app:cet_deleteIconSize="30dp"
    app:cet_selectedBg="@drawable/shape_border_blue"
    app:cet_tipDefaultIcon="@drawable/ic_user_gray"
    app:cet_tipIconSize="30dp"
    app:cet_tipSelectedIcon="@drawable/ic_user_blue" />

<com.example.widgets.custom_edittext.ClearEditText
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginHorizontal="30dp"
    android:layout_marginTop="30dp"
    android:hint="请输入手机号"
    android:inputType="phone"
    android:padding="10dp"
    android:textSize="20sp"
    app:cet_defaultBg="@drawable/shape_border_gray"
    app:cet_deleteIcon="@drawable/ic_delete_x"
    app:cet_deleteIconSize="30dp"
    app:cet_selectedBg="@drawable/shape_border_blue"
    app:cet_tipDefaultIcon="@drawable/ic_user_gray"
    app:cet_tipIconSize="30dp"
    app:cet_tipSelectedIcon="@drawable/ic_user_blue" />

<com.example.widgets.custom_edittext.PasswordEditText
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginHorizontal="30dp"
    android:layout_marginTop="30dp"
    android:hint="请输入密码"
    android:padding="10dp"
    android:textSize="20sp"
    app:pet_defaultBg="@drawable/shape_border_gray"
    app:pet_selectedBg="@drawable/shape_border_blue"
    app:pet_tipDefaultIcon="@drawable/ic_lock_gray"
    app:pet_tipIconSize="30dp"
    app:pet_tipSelectedIcon="@drawable/ic_lock_blue" />

源码下载

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/494711.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

大语言模型(LLM)token解读

1. 什么是token&#xff1f; 人们经常在谈论大模型时候&#xff0c;经常会谈到模型很大&#xff0c;我们也常常会看到一种说法&#xff1a; 参数会让我们了解神经网络的结构有多复杂&#xff0c;而token的大小会让我们知道有多少数据用于训练参数。 什么是token&#xff1f;比…

【C语言】Infiniband驱动init_dev_assign函数

一、注释 一个内核模块的初始化函数&#xff0c;用于分配和初始化某些资源。以下是对代码块的逐行中文注释&#xff1a; // 定义一个初始化设备分配的函数 static void init_dev_assign(void) {int i 1;spin_lock_init(&dev_num_str_lock); // 初始化自旋锁if (mlx4_fil…

量化交易入门(二十三)什么是MTM指标,原理是什么

MTM指标全称是Momentum指标,翻译为动量指标。它用来衡量市场价格在一定时间内上涨或下跌的幅度,属于趋势型指标。其计算公式是: MTM(N) 当前收盘价 - N日前的收盘价 其中N表示统计的周期数,常用参数有6日、12日和24日。 MTM指标的应用要点如下: 判断趋势强弱:MTM数值越大,表…

泛型的进阶

1 通配符 &#xff1f; 我们想调用fun函数帮我们打印&#xff0c;但由于不知道Message具体是什么类型&#xff0c;所以我们可以使用 &#xff1a; &#xff1f;即通配符 当我们将fun函数中改为Message<?>此时就不会报错 2 通配符的上界&#xff1a; <? extends 上…

如何使用 ArcGIS Pro 自动矢量化水系

对于某些要素颜色统一的地图&#xff0c;比如电子地图&#xff0c;可以通过图像识别技术将其自动矢量化&#xff0c;这里为大家介绍一下 ArcGIS Pro 自动矢量化水系的方法&#xff0c;希望能对你有所帮助。 数据来源 教程所使用的数据是从水经微图中下载的电子地图数据&#…

二分练习题——123

123 二分等差数列求和前缀和数组 题目分析 连续一段的和我们想到了前缀和&#xff0c;但是这里的l和r的范围为1e12&#xff0c;明显不能用O(n)的时间复杂度去求前缀和。那么我们开始观察序列的特点&#xff0c;可以按照等差数列对序列进行分块。如上图&#xff0c;在求前10个…

虚拟机Linux(centos)安装python3.8(超详细)

一、Python下载 下载地址&#xff1a;https://www.python.org/downloads/source/ 输入下面网址即可直接下载&#xff1a; python3.8&#xff1a;https://www.python.org/ftp/python/3.8.0/Python-3.8.0.tgz python3.6&#xff1a;https://www.python.org/ftp/python/3.6.5/…

Chrome 插件 tabs API 解析

Chrome.tabs API 解析 使用 chrome.tabs API 与浏览器的标签页系统进行交互&#xff0c;可以使用此 API 在浏览器中创建、修改和重新排列标签页 Tabs API 不仅提供操作和管理标签页的功能&#xff0c;还可以检测标签页的语言、截取屏幕截图&#xff0c;以及与标签页的内容脚本…

Prompt Engineering的4 种方法

此为观看视频 4 Methods of Prompt Engineering 后的笔记。 从通用模型到专用模型&#xff0c;fine tuning&#xff08;微调&#xff09;和prompt engineering&#xff08;提示工程&#xff09;是2种非常重要的方法。本文深入探讨了prompt engineering的4种方法。 首先&#…

MySQL数据库的高级SQL语句与高级操作(2)

目录 一、子查询 1、语法: 2、以下例子均以图中两个表为基础 例子1&#xff1a;查询yun1班级大于85分的学生记录 例子2&#xff1a;将yun2班的学生记录放在一个单独的表中&#xff0c;叫yun2 例子3&#xff1a;教务处误把yun3班叫张丽的学生的成绩搞错了&#xff0c;应该为…

Machine Learning机器学习之向量机(Support Vector Machine,SVM)

目录 前言 算法提出背景&#xff1a; 核心思想&#xff1a; 原理&#xff1a; 应用领域&#xff1a; 一、支持向量机分类&#xff08;主要变体&#xff09; 二、构建常见的支持向量机模型 基于Python 中的 Scikit-learn 库构建线性支持向量机&#xff08;SVM&#xff09; 三、向…

Matplotlib数据可视化实战-2绘制折线图(2)

2.11营业额可视化 已知某学校附近一个烧烤店2022年每个月的营业额如下图所示。编写程序绘制折线图对该烧烤店全年营业额进行可视化&#xff0c;使用红色点画线连接每个月的数据&#xff0c;并在每个月的数据处使用三角形进行标记。 烧烤店营业额 月份123456789101112营业额/万…

Python调用Python并传参

常规 在Python中调用另一个Python脚本可以通过多种方式实现&#xff0c;例如使用subprocess模块或者直接导入模块。以下是两种常见的方法&#xff1a; 使用subprocess模块&#xff1a; 调用 import subprocess # 调用另一个Python脚本&#xff0c;例如script.py subprocess.r…

ES5和ES6的深拷贝问题

深拷贝我们知道是引用值的一个问题&#xff0c;因为在拷贝的时候&#xff0c;拷贝的是在内存中同一个引用。所以当其中的一个应用值发生改变的时候&#xff0c;其他的同一个引用值也会发生变化。那么针对于这种情况&#xff0c;我们需要进行深度拷贝&#xff0c;这样就可以做到…

centos安装jdk的坑

文章目录 一、安装jdk二、查找jdk的目录三、配置JAVA_HOME 一、安装jdk 我们一般用yum search java | grep jdk查询可以安装的jdk 但是一定要注意如下图&#xff0c;必须知道jdk和jre的区别 yum install java-1.8.0-openjdk-devel.x86_64二、查找jdk的目录 用如下命令 sudo…

云电脑安全性怎么样?企业如何选择安全的云电脑

云电脑在保障企业数字资产安全方面&#xff0c;采取了一系列严谨而全面的措施。随着企业对于数字化转型的深入推进&#xff0c;数字资产的安全问题日益凸显&#xff0c;而云电脑作为一种新兴的办公模式&#xff0c;正是为解决这一问题而生。云电脑安全吗&#xff1f;可以放心使…

Mybatis-获取参数值的两种方式

1. ${ } 和 #{ } MyBatis获取参数值的两种方式&#xff1a;${ } 和 #{ } 对于初学者来说&#xff0c;理解MyBatis中获取参数值的两种方式——#{}和${}&#xff0c;关键在于明白它们如何影响SQL语句的构建以及为何在安全性、灵活性上有显著差异。下面我将用简单易懂的语言来解…

Flutter开发之下标

Flutter开发之下标 在iOS开发中使用下标就很方便&#xff0c;本文主要是记录一下Flutter中系统自带的下标&#xff0c;还可以通过对应的方法编写自己的下标。 在Objective-C中的下标 关键字Subscript。 NSArray - (ObjectType)objectAtIndexedSubscript:(NSUInteger)idx A…

Abaqus周期性边界代表体单元Random Sphere RVE 3D (Mesh)插件

插件介绍 Random Sphere RVE 3D (Mesh) - AbyssFish 插件可在Abaqus生成三维具备周期性边界条件(Periodic Boundary Conditions, PBC)的随机球体骨料及骨料-水泥界面过渡区(Interfacial Transition Zone, ITZ)模型。即采用周期性代表性体积单元法(Periodic Representative Vol…

vscode上编辑vba

安装xvba插件更换vscode的工作目录启动扩展服务器在config.json中添加目标工作簿的名称加载excel文件&#xff08;必须带宏的xlsm&#xff09;这个扩展就会自动提取出Excel文件中的代码Export VBA&#xff08;编辑完成的VBA代码保存到 Excel文件 &#xff09;再打开excel文件可…