EditText 是我们常用的输入框控件,平常我们只是使用它输入文本,这里记录一些它不太常见的操作和一些解决方案。
如果一个页面内定义了EditText,那么有可能我们进入此页面的时候会自动弹起软键盘,(分机型,有的会弹,有的不弹)。如果我们需要弹软键盘,我们制定给 EditText 设置
android:focusable="true"
android:focusableInTouchMode="true"
但是如果我们不想这个页面进去就弹出软键盘,我们可以给根布局或者 EditText 的父布局设置 focusable 。
默认的 EditText 是带下划线和粗光标的,我们可以对它们进行简单的修改
android:background="@null" //去掉了下划线
android:textCursorDrawable="@null" //去掉光标的颜色
自定义光标的颜色和宽度:
使用自定义光标
android:textCursorDrawable="@drawable/edittext_cursor"
我们可以通过监听 EditText 的文本变化的方式来改变文本值,我们还能通过 DigitsKeyListener 的方式监听文本的改变。
我们可以通过监听 EditText 的文本变化,比如我们只想要小数点后面2位数,我们就监听文本变化,点后面的2位数,如果多了就把他删除掉。
public class MoneyTextWatcher implements TextWatcher {
private EditText editText;
private int digits = 2;
public MoneyTextWatcher(EditText et) {
editText = et;
}
public MoneyTextWatcher setDigits(int d) {
digits = d;
return this;
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
//删除“.”后面超过2位后的数据
if (s.toString().contains(".")) {
if (s.length() - 1 - s.toString().indexOf(".") > digits) {
s = s.toString().subSequence(0,
s.toString().indexOf(".") + digits+1);
editText.setText(s);
editText.setSelection(s.length()); //光标移到最后
}
}
//如果"."在起始位置,则起始位置自动补0
if (s.toString().trim().substring(0).equals(".")) {
s = "0" + s;
editText.setText(s);
editText.setSelection(2);
}
//如果起始位置为0,且第二位跟的不是".",则无法后续输入
if (s.toString().startsWith("0")
&& s.toString().trim().length() > 1) {
if (!s.toString().substring(1, 2).equals(".")) {
editText.setText(s.subSequence(0, 1));
editText.setSelection(1);
return;
}
}
}
@Override
public void afterTextChanged(Editable s) {
}
}
使用:
//默认两位小数
mEditText.addTextChangedListener(new MoneyTextWatcher(mEditText1));
//手动设置其他位数,例如3
mEditText.addTextChangedListener(new MoneyTextWatcher(mEditText1).setDigits(3);
public class ETMoneyValueFilter extends DigitsKeyListener {
public ETMoneyValueFilter(int d) {
super(false, true);
digits = d;
}
private int digits = 2; //默认显示二位数的小数点
public ETMoneyValueFilter setDigits(int d) {
digits = d;
return this;
}
@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
CharSequence out = super.filter(source, start, end, dest, dstart, dend);
if (out != null) {
source = out;
start = 0;
end = out.length();
}
int len = end - start;
if (len == 0) {
return source;
}
//以点开始的时候,自动在前面添加0
if (source.toString().equals(".") && dstart == 0) {
return "0.";
}
//如果起始位置为0,且第二位跟的不是".",则无法后续输入
if (!source.toString().equals(".") && dest.toString().equals("0")) {
return "";
}
int dlen = dest.length();
for (int i = 0; i < dstart; i++) {
if (dest.charAt(i) == '.') {
return (dlen - (i + 1) + len > digits) ?
"" :
new SpannableStringBuilder(source, start, end);
}
}
for (int i = start; i < end; ++i) {
if (source.charAt(i) == '.') {
if ((dlen - dend) + (end - (i + 1)) > digits)
return "";
else
break;
}
}
return new SpannableStringBuilder(source, start, end);
}
}
其实是和 TextWatcher 类似的方式,那么使用的时候我们这样使用:
//默认两位小数
mEditText.setFilters(new InputFilter[]{new MoneyValueFilter()});
//手动设置其他位数,例如3
mEditText.setFilters(new InputFilter[]{new MoneyValueFilter().setDigits(3)});
在Kotlin代码中是这样使用:
et_input.filters = arrayOf(ETMoneyValueFilter().setDigits(3))
这样就可以实现小数点后面二位数的控制,还顺便加入了.的判断,自动加0的操作。
当此 EditText 软键盘弹起的时候,右下角的确定变为搜索,我们需要给 EditText 设置一个属性:
android:imeOptions="actionSearch"
然后给软键盘设置一个监听
//点击软键盘搜索按钮
etSearch.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_ENTER) {
// 先隐藏键盘
((InputMethodManager) getSystemService(INPUT_METHOD_SERVICE))
.hideSoftInputFromWindow(TransactionHistorySearchActivity.this.getCurrentFocus()
.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
if (isSearch){
isSearch = false;
if (!TextUtils.isEmpty(etSearch.getText().toString()))
searchHistory(etSearch.getText().toString());
}
}
return false;
}
});
这里使用一个flag来判断,是因为部分机型会回调2次。所以为了统一效果,我们使用拦截判断只调用一次。
当然Search的逻辑如果你使用 Kotlin + DataBinding 来实现,那么就更简单了。
//执行搜索
fun doSearch() {
KeyboardUtils.hideSoftInput(mActivity)
scrollTopRefresh()
}
//搜索的删除
fun searchDel() {
mViewModel.mKeywordLiveData.value = ""
doSearch()
}
主要的 Binding Adapter 方法为 onKeyEnter ,它实现了软键盘的搜索。
下面是自定义BindingAdapter的方法:
var _viewClickFlag = false
var _clickRunnable = Runnable { _viewClickFlag = false }
/**
* Edit的确认按键事件
*/
@BindingAdapter("onKeyEnter")
fun EditText.onKeyEnter(action: () -> Unit) {
setOnKeyListener { _, keyCode, _ ->
if (keyCode == KeyEvent.KEYCODE_ENTER) {
KeyboardUtils.closeSoftKeyboard(this)
if (!_viewClickFlag) {
_viewClickFlag = true
action()
}
removeCallbacks(_clickRunnable)
postDelayed(_clickRunnable, 1000)
}
return@setOnKeyListener false
}
}
和上面Java的实现方式类似,同样的做了防抖的操作。为了部分机型连续调用多次的问题。
效果:
上面说到的焦点,不自动弹出软键盘,如果我想自由的控制焦点与软键盘怎么办?
一个例子来说明,比如我们的需求,点击 EditText 的时候弹出弹框提示用户注意事项,当点击确定或者取消之后再继续输入。分解步骤如下:
代码逻辑如下:
mBankAccountEt.setShowSoftInputOnFocus(false);
mBankAccountEt.setOnFocusChangeListener((v, hasFocus) -> {
if (hasFocus && !isShowedBankAccountNotice) {
showBankAccountNoticePopup();
}
});
private void showBankAccountNoticePopup() {
BasePopupView mPopupView = new XPopup.Builder(mActivity)
.moveUpToKeyboard(false)
.hasShadowBg(true)
.asCustom(new BankNameNoticePopup(mActivity, () -> {
isShowedBankAccountNotice = true;
mBankAccountEt.setShowSoftInputOnFocus(true);
//需要把焦点设置回EditText
mBankAccountEt.setFocusable(true);
mBankAccountEt.setFocusableInTouchMode(true);
mBankAccountEt.requestFocus();
mBankAccountEt.setSelection(mBankAccountEt.getText().toString().length());
KeyboardUtils.showKeyboard(mBankAccountEt);
}));
if (mPopupView != null && !mPopupView.isShow()) {
mPopupView.show();
}
}
弹框就不给大家展示了,非常简单的弹窗,定义使用的弹窗库,逻辑都在完成的回调中。
KeyboardUtils工具类,控制EditText的软键盘展示与隐藏
/*
* 显示键盘
* */
public static void showKeyboard(View view) {
InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
view.requestFocus();
imm.showSoftInput(view, 0);
}
}
/*
* 隐藏键盘
* */
public static void hideKeyboard(View view){
InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.hideSoftInputFromWindow(view.getWindowToken(),0);
}
}
效果:
不知道大家有没有在RV中使用过 EditText ,Item中如果有 EditText 那么在滚出屏幕之后 再拉回来可能刚才输入的文本就消失了,或者换成不是刚才输入的文本了,是因为缓存复用,可能复用了别的Item上面的 EditText 控件。
有几种解决方法如下:
方法一: 强制的停用Recyclerview的复用
helper.setIsRecyclable(false);
但是RV就无法缓存与回收了,如果你的Item数量就是固定的并且不多,那么使用这个方法是最好的。
方法二: 通过监听焦点来添加或移除Edittext的TextChangedListener
@Override
protected void convert(BaseViewHolder helper, EdittextInRecyclerViewOfBean item) {
EditText editText = helper.getView(R.id.et);
editText.setText(item.getNum() + "");
TextWatcher textWatcher = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
//这里处理数据
if (TextUtils.isEmpty(s.toString())) {
item.setNum(0);
} else {
item.setNum(Integer.parseInt(s.toString()));
}
}
};
editText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus){
editText.addTextChangedListener(textWatcher);
}else {
editText.removeTextChangedListener(textWatcher);
}
}
});
}
方法三: 通过view的setTag()方法解决
@Override
protected void convert(BaseViewHolder helper, EdittextInRecyclerViewOfBean item) {
TextWatcher textWatcher = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
if (TextUtils.isEmpty(s.toString())) {
item.setNum(0);
} else {
item.setNum(Integer.parseInt(s.toString()));
}
}
};
EditText editText = helper.getView(R.id.et);
//为了避免TextWatcher在调用settext()时被调用,提前将它移除
if (editText.getTag() instanceof TextWatcher) {
editText.removeTextChangedListener((TextWatcher) editText.getTag());
}
editText.setText(item.getNum() + "");
//重新添加上TextWatcher监听
editText.addTextChangedListener(textWatcher);
//将TextWatcher绑定到EditText
editText.setTag(textWatcher);
}
方法四: 为每个EditText的绑定位置
public class EditTextInRecyclerViewAdapter extends RecyclerView.Adapter {
private List mList = new ArrayList<>();
public void setData(List list) {
this.mList = list;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_edittext, parent, false);
return new ViewHolder(v, new ITextWatcher());
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
ViewHolder viewHolder = (ViewHolder) holder;
viewHolder.mITextWatcher.bindPosition(position);
viewHolder.mEditText.setText(mList.get(position).getNum()+"");
}
@Override
public int getItemCount() {
return mList.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
EditText mEditText;
ITextWatcher mITextWatcher;
private ViewHolder(View v, ITextWatcher watcher) {
super(v);
this.mEditText = v.findViewById(R.id.et);
this.mITextWatcher = watcher;
this.mEditText.addTextChangedListener(watcher);
}
}
class ITextWatcher implements TextWatcher {
private int position;
private void bindPosition(int position) {
this.position = position;
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
if (TextUtils.isEmpty(s.toString())) {
mList.get(position).setNum(0);
} else {
mList.get(position).setNum(Integer.parseInt(s.toString()));
}
}
}
}
方法五: 构造方法中添加TextChanged
class PicViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var ivPic: ImageView = itemView.findViewById(R.id.ivPic)
var etScore: EditText = itemView.findViewById(R.id.etScore)
var tvTitle: TextView = itemView.findViewById(R.id.tvTitle)
var myTextWatcher: MyTextWatcher = MyTextWatcher()
init {
etScore.addTextChangedListener(myTextWatcher)
}
fun updateView(picItem: PicItem) {
myTextWatcher.picItem = picItem
ivPic.setImageResource(picItem.picResId)
tvTitle.text = picItem.title
if (picItem.score == null) {
etScore.hint = "请输入分数"
etScore.setText("")
} else {
etScore.setText(picItem.score)
}
}
}
class MyTextWatcher: TextWatcher {
lateinit var picItem:PicItem
override fun afterTextChanged(s: Editable?) {
picItem?.apply {
score=s?.toString()
}
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
}
方法六: 让产品改需求,不要使用EditText,或者我们干脆使用TextView,然后点击Item弹出输入框的弹框方式来实现。
总结了很多 EditText 不常见的一些问题与解决方案,不知道大家有没有遇到别的问题,或者一些 EditText 的骚操作,都可以评论区留言补充哦!
关于 EditText 的分享就到这里了,后面如果有补充我会继续更新!
最后,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。
1、深入理解Java泛型
2、注解深入浅出
3、并发编程
4、数据传输与序列化
5、Java虚拟机原理
6、高效IO
……
1.Retrofit 2.0源码解析
2.Okhttp3源码解析
3.ButterKnife源码解析
4.MPAndroidChart 源码解析
5.Glide源码解析
6.Leakcanary 源码解析
7.Universal-lmage-Loader源码解析
8.EventBus 3.0源码解析
9.zxing源码分析
10.Picasso源码解析
11.LottieAndroid使用详解及源码解析
12.Fresco 源码分析——图片加载流程
1、Kotlin入门教程
2、Kotlin 实战避坑指南
3、项目实战《Kotlin Jetpack 实战》
从一个膜拜大神的 Demo 开始
Kotlin 写 Gradle 脚本是一种什么体验?
Kotlin 编程的三重境界
Kotlin 高阶函数
Kotlin 泛型
Kotlin 扩展
Kotlin 委托
协程“不为人知”的调试技巧
图解协程:suspend
1.SmartRefreshLayout的使用
2.Android之PullToRefresh控件源码解析
3.Android-PullToRefresh下拉刷新库基本用法
4.LoadSir-高效易用的加载反馈页管理框架
5.Android通用LoadingView加载框架详解
6.MPAndroidChart实现LineChart(折线图)
7.hellocharts-android使用指南
8.SmartTable使用指南
9.开源项目android-uitableview介绍
10.ExcelPanel 使用指南
11.Android开源项目SlidingMenu深切解析
12.MaterialDrawer使用指南
1、NDK 模块开发
2、JNI 模块
3、Native 开发工具
4、Linux 编程
5、底层图片处理
6、音视频开发
7、机器学习
1、Flutter跨平台开发概述
2、Windows中Flutter开发环境搭建
3、编写你的第一个Flutter APP
4、Flutter开发环境搭建和调试
5、Dart语法篇之基础语法(一)
6、Dart语法篇之集合的使用与源码解析(二)
7、Dart语法篇之集合操作符函数与源码分析(三)
…
1、小程序概述及入门
2、小程序UI开发
3、API操作
4、购物商场项目实战……
一、面试合集
二、源码解析合集
三、开源框架合集
欢迎大家一键三连支持,若需要文中资料,直接点击文末CSDN官方认证微信卡片免费领取↓↓↓
作者:newki
链接:https://juejin.cn/post/7115621556700577828