广州网站建设oem,jquery电子商务网站模板,自建网站如何备案,wordpress 勾子有两种实现方案。 方案一#xff1a;是自己写一个TextWatcher。 方案二#xff1a;是重写TextView的getOffsetForPosition方法#xff0c;返回一个计算好的offset。
我在工作时#xff0c;使用的是方案一。在离职之后#xff0c;我还是对这个问题耿耿于怀#xff0c;所以…有两种实现方案。 方案一是自己写一个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已经遭到破坏或者是还没有suffixTextif (preText.isNotEmpty() preText.endsWith(suffixText).not()) {// 如果lastText等于空就说明还没有suffixTextif (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.lengthif (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 fieldfield valuetextWatcher.suffixText valueupdateSuffixText(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 suffixTextif (text.isEmpty() || suffixText.isEmpty()) {return superResult}val textLength text.length - suffixText.length// 如果index在suffixText的范围内就设置为inputText的最大indexif (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 textif (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?) {}}
}