输入框带删除按钮, 此乃标配, 实现起来方法也很多, 网上开源也很多.
但是, 没事就喜欢瞎折腾.
上图说话.
只是在原生的基础上加了扩展. 相对来说入侵非常少, 使用方法和原生的一模一样.无任何阉割.
完整代码:
public class ExEditText extends AppCompatEditText { Rect clearRect = new Rect(); public ExEditText(Context context) { super(context); } public ExEditText(Context context, AttributeSet attrs) { super(context, attrs); } public ExEditText(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); //在不影响默认过滤规则的情况下,添加Emoji表情过滤 if ("emoji".equalsIgnoreCase(String.valueOf(getTag()))) { final InputFilter[] filters = getFilters(); final InputFilter[] newFilters = new InputFilter[filters.length + 1]; System.arraycopy(filters, 0, newFilters, 0, filters.length); newFilters[filters.length] = new EmojiFilter(); setFilters(newFilters); } } @Override protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { super.onFocusChanged(focused, direction, previouslyFocusedRect); //焦点改变,检查是否需要显示删除按钮 checkEdit(focused); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); //用来计算删除按钮所在的矩形区域 clearRect.set(w - getPaddingRight() - Math.min(w, h), getPaddingTop(), w - getPaddingRight(), Math.min(w, h) - getPaddingBottom()); } @Override public boolean onTouchEvent(MotionEvent event) { //检查点击删除按钮区域,并且当前具有焦点 if (event.getAction() == MotionEvent.ACTION_DOWN && isFocused()) { //检查是否在删除按钮矩形区域,按下 if (checkClear(event.getX(), event.getY())) { //如果当前已经是空的, 不处理Touch事件,否则清空文本,达到删除的效果 if (!TextUtils.isEmpty(getText())) { setText(""); return true; } } } return super.onTouchEvent(event); } private void checkEdit(boolean focused) { if (TextUtils.isEmpty(getText()) || !focused) { setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); } else { //这里才是关键, 用的是原生的方法显示删除图标. setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.base_edit_delete, 0); } } //坐标是否在按钮区 private boolean checkClear(float x, float y) { return clearRect.contains(((int) x), (int) y); } @Override protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) { super.onTextChanged(text, start, lengthBefore, lengthAfter); //时刻检查一下 checkEdit(true); } }
Emoji表情过滤:
可能还是有些特殊的Emoji表情,或者字符表情无法过滤, 抱歉;
如果你发现, 请告知, 让我们一起完善一下, 谢谢!
public class EmojiFilter implements InputFilter { Pattern emojiPattern = Pattern.compile("[\ud83c\udc00-\ud83c\udfff]|[\ud83d\udc00-\ud83d\udfff]|[\u2600-\u27ff]", Pattern.UNICODE_CASE | Pattern.CASE_INSENSITIVE); private int mMax = -1;//默认不过滤长度 public EmojiFilter() { } public EmojiFilter(int max) { mMax = max; } /** * 检测是否有emoji表情 * * @param source * @return */ public static boolean containsEmoji(CharSequence source) { int len = source.length(); for (int i = 0; i < len; i++) { char codePoint = source.charAt(i); if (!isEmojiCharacter(codePoint)) { //如果不能匹配,则该字符是Emoji表情 return true; } } return false; } /** * 判断是否是Emoji * * @param codePoint 比较的单个字符 * @return */ private static boolean isEmojiCharacter(char codePoint) { return (codePoint == 0x0) || (codePoint == 0x9) || (codePoint == 0xA) || (codePoint == 0xD) || ((codePoint >= 0x20) && (codePoint <= 0xD7FF)) || ((codePoint >= 0xE000) && (codePoint <= 0xFFFD)) || ((codePoint >= 0x10000) && (codePoint <= 0x10FFFF)); } @Override public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { if (this.emojiPattern.matcher(source).find() || containsEmoji(source)) { return ""; } if (mMax != -1) { int keep = mMax - (dest.length() - (dend - dstart)); if (keep <= 0) { return ""; } else if (keep >= end - start) { return null; // keep original } else { keep += start; if (Character.isHighSurrogate(source.charAt(keep - 1))) { --keep; if (keep == start) { return ""; } } return source.subSequence(start, keep); } } return source; } }
作者:angcyo