Android(安卓)TextView ClickSpan与onClick事件冲突问题
16lz
2021-01-26
0 本章内容
- TextView自定义Span实现
- 多Span融合
- 点击事件冲突处理方法
1 TextView自定义Span实现
1.1 产品需求
产品要求实现一个类似于下图的功能
其中红框部分需要实现点击,且左右有Padding。
并且整体的文本需要有点击事件 和 长按事件,蓝字处要实现另一个点击事件
1.2 Android中的Span
这个不再赘述了,Android内本身支持很多类型的Span,例如ClickSpan、ForgroundColorSpan等等。具体实现方式大家可以自行查阅。
2 多Span融合方式
此处使用ClickSpan实现蓝字点击、使用自定义的ReplacementSpan实现UI层样式和Padding。
ClickSpan需要搭配LinkMovementMethod实现点击的具体逻辑。本身系统LinkMovementMethod并不支持去除下划线,并且在点击后会有background变化等问题。此处也许要自定义一个。
自定义Method的具体代码如下
public class MyLinkMovementMethod extends LinkMovementMethod { private static LinkMovementMethod sInstance; private long downTime; public static MovementMethod getInstance() { if (sInstance == null) sInstance = new ReaderQuoterMovementMethod(); return sInstance; } @Override public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) { int action = event.getAction(); if (action == MotionEvent.ACTION_DOWN) { downTime = System.currentTimeMillis(); } if (action == MotionEvent.ACTION_UP) { long interval = System.currentTimeMillis() - downTime; int x = (int) event.getX(); int y = (int) event.getY(); x -= widget.getTotalPaddingLeft(); y -= widget.getTotalPaddingTop(); x += widget.getScrollX(); y += widget.getScrollY(); Layout layout = widget.getLayout(); int line = layout.getLineForVertical(y); int off = layout.getOffsetForHorizontal(line, x); ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class); if (link.length != 0) { if (interval < ViewConfiguration.getLongPressTimeout()) { link[0].onClick(widget); buffer.setSpan(new BackgroundColorSpan(Color.TRANSPARENT), buffer.getSpanStart(link[0]), buffer.getSpanEnd(link[0]), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } } } return true; }}
自定义ReplacementSpan的具体代码如下:
public class QuoterSpan extends ReplacementSpan { private static final int DEFAULT_RIGHT_PADDING = 6; private Context mContext; private Rect mRect; public QuoterSpan(Context context) { mContext = context; mRect = new Rect(); } @Override public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, @Nullable Paint.FontMetricsInt fontMetricsInt) { //获取原本文本的宽度,并在此基础上添加padding宽度 paint.getTextBounds(text.subSequence(0, text.length()).toString(), start, end, mRect); return mRect.width() + Math.round(((TextPaint) paint).density * DEFAULT_LEFT_PADDING * 2); } @Override public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint) { if (mContext == null) { return; } paint.setColor(ThemeSettingsHelper.getInstance().getThemeColor(mContext, R.color.milk_Blue).getDefaultColor()); paint.setAntiAlias(true); //文本开始绘制区域为最初X值加左边padding int textBegin = Math.round(x + ((TextPaint) paint).density * DEFAULT_LEFT_PADDING); canvas.drawText(text.subSequence(start, end).toString(), textCenter, y, paint); }}
3、点击事件的冲突问题:
1、单机:如果textView本身有点击事件,此时又添加了ClickSpan,必须搭配MovenmentMethod才能将点击事件拦截在ClickSpan。这一点在源码中可以找到:
public boolean onTouchEvent(MotionEvent event) { final int action = event.getActionMasked(); if (mEditor != null) { mEditor.onTouchEvent(event); if (mEditor.mSelectionModifierCursorController != null && mEditor.mSelectionModifierCursorController.isDragAcceleratorActive()) { return true; } } // 先交由父类决定是否拦截 final boolean superResult = super.onTouchEvent(event); // ....... // 判断是否由MovementMethod处理 if ((mMovement != null || onCheckIsTextEditor()) && isEnabled() && mText instanceof Spannable && mLayout != null) { boolean handled = false; if (mMovement != null) { handled |= mMovement.onTouchEvent(this, (Spannable) mText, event); } // ....... }
2、长按:如果当前textView设置了longClick事件。而我们的MovementMethod又多在MotionEvent = Up 的时候处理点击逻辑。(放down里可能会误触)。
这就会导致一个问题,当textView的长按被触发后抬起手指,MovementMethod的Up事件也会被触发。
解决:如本文中第一段代码中,在MovementMethod的Down事件时记录一个事件,如果Up和down的间隔时间大于系统内置的长按间隔(500ms默认,各厂商可能会改这个时间,例如小米手机的tap间隔小于系统默认的100ms)。则不做处理。
更多相关文章
- Android(安卓)Studio 中使用github功能
- 进阶必备-Android事件分发机制
- Android(安卓)开发之实时更新 App Widget
- 让android的webview中的按钮,触发事件,也能像原生按钮一样使用
- Android中的双击事件
- Android(安卓)开发事件响应之基于监听的事件响应
- Android(安卓)上从外部应用注入按键事件流程分析
- 从源码剖析PopupWindow 兼容Android(安卓)6.0以上版本点击外部不
- Android(安卓)view触摸反馈原理和源码分析