[Android开发] 重学Android-EditText的进阶操作

EditText的进阶使用

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 的方式监听文本的改变。

3.1 TextWatcher的方式

我们可以通过监听 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);

3.2 DigitsKeyListener的方式

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的Search操作

当此 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 的时候弹出弹框提示用户注意事项,当点击确定或者取消之后再继续输入。分解步骤如下:

  1. 我们点击EditText不能弹出软键盘
  2. 监听焦点获取之后弹出弹框
  3. 弹框完成之后我们需要手动的给EditText焦点
  4. 获取焦点之后需要设置光标与软键盘

代码逻辑如下:

      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复用的问题

不知道大家有没有在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
……

在这里插入图片描述

二、Android百大框架源码解析

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 源码分析——图片加载流程

在这里插入图片描述

三、Android性能优化实战解析

  • 腾讯Bugly:对字符串匹配算法的一点理解
  • 爱奇艺:安卓APP崩溃捕获方案——xCrash
  • 字节跳动:深入理解Gradle框架之一:Plugin, Extension, buildSrc
  • 百度APP技术:Android H5首屏优化实践
  • 支付宝客户端架构解析:Android 客户端启动速度优化之「垃圾回收」
  • 携程:从智行 Android 项目看组件化架构实践
  • 网易新闻构建优化:如何让你的构建速度“势如闪电”?

在这里插入图片描述

四、高级kotlin强化实战

1、Kotlin入门教程
2、Kotlin 实战避坑指南
3、项目实战《Kotlin Jetpack 实战》

  • 从一个膜拜大神的 Demo 开始

  • Kotlin 写 Gradle 脚本是一种什么体验?

  • Kotlin 编程的三重境界

  • Kotlin 高阶函数

  • Kotlin 泛型

  • Kotlin 扩展

  • Kotlin 委托

  • 协程“不为人知”的调试技巧

  • 图解协程:suspend

在这里插入图片描述

五、Android高级UI开源框架进阶解密

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使用指南
在这里插入图片描述

六、NDK模块开发

1、NDK 模块开发
2、JNI 模块
3、Native 开发工具
4、Linux 编程
5、底层图片处理
6、音视频开发
7、机器学习

在这里插入图片描述

七、Flutter技术进阶

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

你可能感兴趣的:(Android,Android开发,android,android,studio,ide)