android EditText设置后缀

安卓监听剪切复制粘贴事件实例 本文的剪切复制粘贴事件只针对文本,URI 和 intent 不在讨论范围内。 实例代码实现了2个功能:(1)监听剪切复制粘贴事件;(2)获取剪切复制或粘贴的文本; 阅读详情

有两种实现方案。
方案一:是自己写一个TextWatcher。
方案二:是重写TextView的getOffsetForPosition方法,返回一个计算好的offset。

我在工作时,使用的是方案一。在离职之后,我还是对这个问题耿耿于怀,所以才去看源码,最后想到了方案二。

但实际测试时,发现方案二存在一些问题,而且看了找了好久的源码,都不知道要重写哪个方法来解决,所以只能提供另一个半成品的方案二出来。

先看看两种方案的gif吧。
方案一
在这里插入图片描述
方案二
在这里插入图片描述
从方案一的gif图片可以看到,在输入文本之后,会在文本后面追加suffix text。如果在suffix text的范围内输入会删除,则会将这些操作传递到已输入的文本上。如果已输入的最后一个文本被删除,则会删除掉所有文本。

方案二则相对简单,输入后依然有suffix text,但suffix 的范围是不可以点击的。咋一看,这个方案好像很好,但我在实际测试时发现了一些问题,而且还不知道要怎么解决。

  • 长按EditText会出现全选的dialog,并且点击全选将选择suffix text。这个操作我认为是有问题的。既然suffix text没办法被selection,那全选就不应该将它包含进来
  • 双击suffix text,也会选择suffix text

我找了很久很久的源码,不断的debug,尝试定位到第一个问题和第二个问题调用的源码,并看看能否重写某些方法来改变其逻辑,但最终都无功而返。
在上面我也提到了,我是重写getOffsetForPosition方法实现了这个功能,所以我也尝试在这个方法上动手脚,但最终的效果不是很好。在模拟器上,如果是用了全选,会出现,先全选,再选回suffix text前面的文本。对于用户来说,两个动画同时出现,未免也太滑稽了吧。所以我最终没有采用这种方案。

无论使用哪种方案,在复制时都会将suffix text复制进来,想要解决这个问题也很简单,可以EditText并重写EditText的onTextContextMenuItem方法,并判断id是否为android.R.id.copy。如果是,就重写一下copy的逻辑。至于为什么我知道可以这样做,可以看一下这篇博客。

图也给了,也解释了图片里面发生了什么,下面贴一下代码,注释已经补在代码里面。
方案一

open class SuffixTextWatcher(private val editText: EditText, private val suffixText: String) : TextWatcher {
    private var lastText = ""

    override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
    }

    override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
        if (suffixText.isEmpty()) {
            return
        }
        editText.removeTextChangedListener(this)

        val preText = s.toString()
        val suffixText = suffixText
        // 如果输入的文本等于suffixText,就说明已经清空了输入的文本,那就直接设置为空字符串
        if (preText == suffixText) {
            lastText = ""
            editText.setText(lastText)
        } else {
            // 如果不是以suffixText结尾,则说明suffix已经遭到破坏,或者是还没有suffixText
            if (preText.isNotEmpty() && preText.endsWith(suffixText).not()) {
                // 如果lastText等于空,就说明还没有suffixText
                if (lastText.isEmpty()) {
                    lastText = preText.plus(suffixText)
                    editText.setText(lastText)
                    editText.setSelection(preText.length)
                } else {
                    // 如果执行到该else,就说明suffixText已经被破坏了
                    val suffixTextStartIndex = lastText.length - suffixText.length
                    // 如果两个文本的长度不相等
                    if (lastText.length != preText.length) {
                         // 获取上次已输入的字符串
                        val lastInputText = lastText.substring(0, suffixTextStartIndex)
                        // 如果lastText的长度小于preText的长度,就说明已经suffixText里面输入了字符
                        // 这个if-else就是用于获取实际输入的字符
                        val latestInputText = if (lastText.length < preText.length) {
                            // 获取输入的字符,并拼接到上次已输入的字符串末尾
                            val inputChar = preText.substring(start, start + 1)
                            lastInputText.plus(inputChar)
                        } else {
                            // 如果不大于,那就是小于,因为已经在上面判断了两个不相等
                            // 如果小于,就是将suffixText中的某个字符删除掉
                            
                            // 上次如果只输入了一个字符,本次就将输入的字符设置为空字符串
                            if (lastInputText.length == 1) {
                                ""
                            } else {
                                // 否则就删掉最后一个字符
                                lastInputText.substring(0, lastInputText.length - 1)
                            }
                        }
                        // 如果上面获取到的最新输入文本不为空,就在后面追加suffixText
                        // 否则就将lastText设置为空字符串
                        lastText = if (latestInputText.isNotEmpty()) {
                            latestInputText.plus(suffixText)
                        } else ""
                        editText.setText(lastText)
                        if (lastText.isNotEmpty()) {
                            editText.setSelection(latestInputText.length)
                        }
                    } else {
                        // 这个else一般执行不到,但为了防止出现考虑不到的问题,还是简单处理一下
                        if (start in suffixTextStartIndex until lastText.length) {
                            editText.setText(lastText)
                            editText.setSelection(suffixTextStartIndex)
                        }
                    }
                }
            } else {
                // 执行到该else,就说明suffixText没有遭到破坏
                // 如果用户没有乱来,一般就是执行到这里,但也有例外
                // 从gif图片可以看到,如果在su中间输入s,也是没有问题的,因为此时endWith为true,但这种情况下,selectionIndex是不正确的
                // 此时selectionIndex是在su中间,而不是在ss中间,所以下面的的代码就是处理这个问题
                // 处理的方式也很简单,获取suffixText的长度,并判断start是否在suffixText的范围内
                // 如果是,就将index设置到suffixText前面
                if(preText.isNotEmpty()) {
                    val suffixTextStartIndex = preText.length - suffixText.length
                    if (start >= suffixTextStartIndex) {
                        editText.setSelection(suffixTextStartIndex)
                    }
                }
                lastText = preText
            }
        }
        editText.addTextChangedListener(this)
    }

    override fun afterTextChanged(s: Editable?) {
    }

}

方案二

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

    // textWatcher用来补齐后缀
    private val textWatcher = SuffixTextWatcher(this)

    var suffixText = ""
        set(value) {
            val oldSuffix = field
            field = value
            textWatcher.suffixText = value
            updateSuffixText(oldSuffix, value)
        }

    init {
        addTextChangedListener(textWatcher)
        if (suffixText.isNotEmpty()){
            textWatcher.suffixText = suffixText
        }
    }

    private fun updateSuffixText(oldSuffix: String, newSuffix: String) {
        val text = text ?: ""
        if (oldSuffix.isEmpty() || text.isEmpty()) {
            return
        }
        val oldSuffixIndex = text.lastIndexOf(oldSuffix)
        if (oldSuffixIndex != -1) {
            setText(text.substring(0, oldSuffixIndex))
        }
    }

    // 这里就是修改selectionIndext的代码,如果用户的touch行为导致selectionIndex发生变化
    // EditText就会调用这里获取index,所以只需重写该方法即可
    override fun getOffsetForPosition(x: Float, y: Float): Int {
        var superResult = super.getOffsetForPosition(x, y)
        val text = text ?: ""
        val suffixText = suffixText
        if (text.isEmpty() || suffixText.isEmpty()) {
            return superResult
        }
        val textLength = text.length - suffixText.length
        // 如果index在suffixText的范围内,就设置为inputText的最大index
        if (superResult >= textLength) {
            superResult = textLength
        }
        return superResult
    }

    override fun onTextContextMenuItem(id: Int): Boolean {
        // 如果不希望用户复制的内容包含suffix text,就可以重写该方法,并按照我上面提到的代码去做
        return super.onTextContextMenuItem(id)
    }

    private class SuffixTextWatcher(val editText: EditText) : TextWatcher {
        var suffixText: String = ""

        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
        }

        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
            val text = s?.toString() ?: ""
            if (text.isEmpty()) {
                return
            }
            editText.removeTextChangedListener(this)
            // 如果没有suffix text,就在最后追加suffix text
            if (text.endsWith(suffixText).not()) {
                if (text.isNotEmpty()) {
                    editText.setText("$text$suffixText")
                    editText.setSelection(text.length)
                }
            } else {
                if (text == suffixText) {
                    editText.setText("")
                }
            }
            editText.addTextChangedListener(this)
        }

        override fun afterTextChanged(s: Editable?) {
        }
    }
}
Android自定义view之EditText中显示提示文本,在提示文本后面可输入文本示例 看到书上写的关于如何在EditText中显示提示文本,在提示文本后面可输入文本示例?案例,于是按照方案进行实践, 大致效果如图: 解析:在EditText内部显示提示文本(这部分被锁,不可删除和修改,文本在后面输入)一般可以通过android:drawableLeft来实现。首先要做一个带有提示文本的图像(文字当做图像来显示),并通过android:drawableLett属性指定该图像文... 阅读详情

相关推荐

EditText实现输入时自动追加单位,输入证件号部分变星号也可参考

这是本人第一篇技术博客,当然要原创啦。作为第一篇,说点基础又实用的,EditText,大家都用过,最基本的控件了。在开发过程中,有时为了用户体验,会在EditText输入过程中,追加单位,或者输入证件号时部分变星号这类需求。本文就对此进行讨论。如有更好思路,欢迎指教。 实现思路是双层布局,上面透明输入内容,下面TextView显示内容,需要对EditText的输入进行监听。效果图就不展示了,很简

CysionLiu的博客 6864

android 自定义控件之AutoCompleteTextView邮箱后缀自动补全

今天,讲讲昨天看到的一个自定义的控件,可以自动补齐邮箱后缀。 效果很好: 由于原有的AutoCompleteTextView只是按照相同的字符串匹配,所以这里要自定义AutoCompleteTextView,然后复写里面的一些方法 public class EmailAutoCompleteTextView extends AutoCompleteTextView {

谢岩的博客 2022

android实现简单计算器

最近在做一个计算器小demo,中间真是踩了不少坑,所以打算系统的总结一下,也不枉我耗费了这么多心血。计算器要求如下图所示,要求可以进行简单的加减乘除运算,按下clear按钮清空上面的表达式。一、界面设计上面是一个EditText用来表示输入框下面键盘设计是GridView代码如下:&lt;LinearLayout xmlns:android="http://schemas.android.com/...

又考试的laymmy的博客 1595

如何在EditText输入过程中自动添加单位功能

EditText在输入过程中增加固定单位~

qq_38794072的博客 1102

Android-部分可编辑的EditText

有一个需求是这样的,页面上有一个输入框,供用户输入手机号码,如果通讯录里面存在这个号码,会自动把名字追加到号码后面。这个需求变态的地方在于,假如用一个EditText+TextView,那么不好控制二者之间的距离,就算是做了各种适配,但是用户可以设置系统的字体,仍然显示很难看!没办法,之好在一个EditText里面来实现号码部分可编辑,名字是自动追加上的。

从交换机安全配置,看常见局域网攻击

前言 构建零信任网络,自然离不开网络准入(NAC),这就涉及到交换机的一些安全测试,于是有了此文《从交换机安全配置看常见局域网攻击》。 交换机安全配置 如本文标题所说从交换机安全配置看常见的局域网攻击,那么下面提到的各种攻击方式,自然是建立在交换机安全配置之上来说的。我们通过h3c官方提供的《H3C 交换机安全加固手册》和《安全配置指导》,从这两份手册可以看出交换机支持的安全配置非常多,基本已经覆盖了各种我们熟知局域网攻击。下面就挑几个典型的攻击方式进行演示。 arp欺骗 【一>所有资源获取&lt

kali_Ma的博客 1万+

Android

<?xml version="1.0" encoding="utf-8"?> <TableLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http:...

A-lex的博客 134

UE5 Metahuman与Blender手动绑定:ARKit面部动画实战对比

面部动画是角色动画领域的核心技术,其核心原理在于通过驱动面部骨骼或混合形状来模拟真实肌肉运动,从而赋予数字角色生命力。这项技术的价值在于极大地提升了虚拟角色的表现力与沉浸感,广泛应用于游戏开发、影视制作、虚拟现实和实时交互应用。在技术实现路径上,开发者常面临工业化集成方案与高度自定义方案的选择。本文聚焦于使用行业标准的ARKit 52个面部混合形状数据,深度对比了虚幻引擎Metahuman的工业化流程与Blender手动绑定方案在视觉保真度、工作流效率、性能开销和扩展性上的差异,为不同项目规模与技术需求的团

weixin_33901926的博客 449

TextView、EditText 怎样设置后缀为 .otf 原生文件 的文字样式

TextView tv; EditText edt;/** * 获取资源文件 */ Typeface typeFace = Typeface.createFromAsset(context.getAssets(), "fonts/interstate.otf"); tv.setTypeface(typeFace); edt.setTypeface(typeFace);

hellohellowei的博客 1783

Ghost备份还原Win10全攻略:从老电脑迁移到新硬盘,ESP分区处理是关键

本文详细介绍了使用Ghost工具将Win10系统从旧电脑迁移到新硬盘的全过程,特别强调了ESP分区的关键处理方法。通过分步指导备份、分区准备、系统还原和引导修复,帮助用户实现无缝系统迁移,解决常见问题如启动失败和驱动兼容性。

weixin_30823001的博客 672

微信小程序消息推送实战:从模板消息到订阅消息的进阶指南

本文详细解析了微信小程序消息推送的核心机制,重点对比了模板消息与订阅消息的差异与应用场景。模板消息适用于支付成功等强即时性业务回执,而订阅消息则需用户授权,更适合物流提醒等可预期通知。文章提供了从授权、配置到发送的完整实战指南,并分享了常见的避坑技巧与性能优化建议,帮助开发者构建高效、合规的用户触达体系。

weixin_29231027的博客 594

中缀转后缀并分别计算

中缀转后缀并分别计算。参考了几篇大佬的文章最后写出,通过Android studio实现。只实现个位整数+-*/,如有更多需求,请自行修改。

weixin_60996371的博客 719

自动配胶机的PLC控制系统设计

我们根据自动配胶机的控制需求,编写了相应的PLC程序。PID温度控制算法的实现方面,我们采用了经典的PID控制算法,通过不断调整加热器的功率,实现对胶水混合温度的精确控制。在PLC程序中,我们编写了PID控制算法的程序模块,通过实时采集温度传感器的数据,计算温度偏差和偏差变化率,并根据PID控制算法计算出加热器的功率输出值,实现对温度的精确控制。在系统调试阶段,我们对自动配胶机的各个部分进行了详细的测试和调试,包括气路和液路的开关测试、传感器的精度测试、步进电机的位置和速度测试以及PLC程序的逻辑测试等。

lagougongzuoshi的博客 1227

Android——TextView和EditText控件

设置text与drawable(图片)的间隔,与drawableLeft、 drawableRight、drawableTop、drawableBottom一起使用,可设置为负数,单独使用没有效果。附加功能,设置右下角IME动作与编辑框相关的动作,如actionDone右下角将显示一个“完成”,而不设置默认是一个回车符号。设置文本的最大显示行数,与width或者layout_width结合使用,超出部分自动换行,超出行数将不显示。设置文本的类型,用于帮助输入法显示合适的键盘类型。

weixin_45298122的博客 1217

统计滤波器

认知有限,望大家多多包涵,有什么问题也希望能够与大家多交流,共同成长!本文先对统计滤波器做个简单的介绍,具体内容后续再更,其他模块可以参考去我其他文章提示:以下是本篇文章正文内容。

qq_35635374的博客 1644

百度百科 X-Bk-Token 算法还原

百度百科 X-Bk-Token 算法还原

自成背后的博客 1万+

Android——TextView和EditText控件(1)

android:focusable=“true”android:focusableInTouchMode=“true”android:text=“个人博客:https://blog.youkuaiyun.com/qq_40205116\n电话:12345436123”/>android:textSize="20sp"设置字体大小单位sp,设置宽高单位dpandroid:textColor="#ff0000"设置字体颜色 (只能为十六进制)android:singleLine="true"单行显示android:auto

2401_84412738的博客 1176

国科大作业考试资料《人工智能原理与算法》2024新编-第三次作业整理

1. 你的目标是让机器人走出迷宫。机器人面朝北,开始位置在迷宫中间。你可以让机器人转向面朝东、南、西或北。你可以让机器人向前走一段距离,在撞墙之前它会停步。 (1)将问题形式化。状态空间有多大? (2)在迷宫中游走,在两条路或更多路交叉的路口可以转弯。重新形式化这个问题。现在状态空间有多大? (3)从迷宫的任一点出发,我们可以朝四个方向中的任一方向前进直到可以转弯的地方,而且我们只需要这样做。重新对这个问题进行形式化。我们需要记录机器人的方向吗? 2. 传教士和野人问题

MatsumotoChrikk 984

Android——TextView和EditText控件,kotlin脱糖

android:id="@+id/et1" android:layout_width=“match_parent” android:layout_height=“wrap_content” android:inputType=“textPassword” android:maxLength=“6” android:hint="@string/possword" /> android:hint="@string/username"设置底纹(开始要到strings.xml中定义底纹文字,和定义文本一样)

952

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

android_基础_Edittext的setSelection属性

转载自: http://www.mamicode.com/info-detail-1723001.html Edittext的setSelection属性用法 1.如何使edittext点击后全选里面的内容啊? editText.setSelection(0, length); editText.setSelection(0); editText.setSelectAllOnFocus(true); 2.记一次EditText设置默认选中setSelection的一个bug 需求:在重命名的时候,.

写bug的蚊子的博客 3635

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

关于Android的EditText问题总结

1、 在Android应用中,当跳转到某个Activity时,该Activity显示页面的EditText获得焦点,在某些机器中会触发软键盘的自动弹出。有时候为了更好的用户体验,需要控制在用户点击EditText时才弹出软键盘,即需要才弹出。 在androidminifest文件当中,在需要屏蔽输入法的Activity里添加属性: android:windowSoftInputMode

逍遥飞鹤的专栏 3523

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

上一篇: android开发优化代码质量的一些思考
下一篇: 使用kotlin编写html dsl框架
杜壁奇
博客等级 码龄10年 10粉丝 31原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值