-
2021-06-12 04:39:26
有没有办法链接ListView中包含的特定TextView?我尝试使用android:autoLink="all",但这不起作用。我得到了一个脱离背景错误。
重要的是要注意:ListView是我在ViewFlipper中的第二个视图。
我也尝试过:
View mItemView = mAdapter.getView(2, null, null);
TextView infoText = (TextView) mItemView.findViewById(R.id.rowText2);
Linkify.addLinks(infoText, Linkify.ALL);
将适配器绑定到ListView并切换View后。没有运气。
这是堆栈跟踪:
06-03 21:19:25.180: ERROR/AndroidRuntime(1214): Uncaught handler: thread main exiting due to uncaught exception
06-03 21:19:25.219: ERROR/AndroidRuntime(1214): android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
06-03 21:19:25.219: ERROR/AndroidRuntime(1214): at android.app.ApplicationContext.startActivity(ApplicationContext.java:550)
06-03 21:19:25.219: ERROR/AndroidRuntime(1214): at android.content.ContextWrapper.startActivity(ContextWrapper.java:248)
06-03 21:19:25.219: ERROR/AndroidRuntime(1214): at android.text.style.URLSpan.onClick(URLSpan.java:62)
06-03 21:19:25.219: ERROR/AndroidRuntime(1214): at android.text.method.LinkMovementMethod.onTouchEvent(LinkMovementMethod.java:216)
06-03 21:19:25.219: ERROR/AndroidRuntime(1214): at android.widget.TextView.onTouchEvent(TextView.java:6560)
06-03 21:19:25.219: ERROR/AndroidRuntime(1214): at android.view.View.dispatchTouchEvent(View.java:3709)
06-03 21:19:25.219: ERROR/AndroidRuntime(1214): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:884)
06-03 21:19:25.219: ERROR/AndroidRuntime(1214): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:884)
06-03 21:19:25.219: ERROR/AndroidRuntime(1214): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:884)
06-03 21:19:25.219: ERROR/AndroidRuntime(1214): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:884)
06-03 21:19:25.219: ERROR/AndroidRuntime(1214): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:884)
06-03 21:19:25.219: ERROR/AndroidRuntime(1214): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:884)
06-03 21:19:25.219: ERROR/AndroidRuntime(1214): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:884)
06-03 21:19:25.219: ERROR/AndroidRuntime(1214): at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1659)
06-03 21:19:25.219: ERROR/AndroidRuntime(1214): at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1107)
06-03 21:19:25.219: ERROR/AndroidRuntime(1214): at android.app.Activity.dispatchTouchEvent(Activity.java:2061)
06-03 21:19:25.219: ERROR/AndroidRuntime(1214): at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1643)
06-03 21:19:25.219: ERROR/AndroidRuntime(1214): at android.view.ViewRoot.handleMessage(ViewRoot.java:1691)
06-03 21:19:25.219: ERROR/AndroidRuntime(1214): at android.os.Handler.dispatchMessage(Handler.java:99)
06-03 21:19:25.219: ERROR/AndroidRuntime(1214): at android.os.Looper.loop(Looper.java:123)
06-03 21:19:25.219: ERROR/AndroidRuntime(1214): at android.app.ActivityThread.main(ActivityThread.java:4363)
06-03 21:19:25.219: ERROR/AndroidRuntime(1214): at java.lang.reflect.Method.invokeNative(Native Method)
06-03 21:19:25.219: ERROR/AndroidRuntime(1214): at java.lang.reflect.Method.invoke(Method.java:521)
06-03 21:19:25.219: ERROR/AndroidRuntime(1214): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
06-03 21:19:25.219: ERROR/AndroidRuntime(1214): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
06-03 21:19:25.219: ERROR/AndroidRuntime(1214): at dalvik.system.NativeStart.main(Native Method)
任何想法?
提前致谢!!!
更多相关内容 -
ckeditor-autolink:CKEditor的超链接自动生成器插件
2021-05-10 10:14:44自动连结 自动链接是CKEditor的插件,用于在键入类似URL的字符串时自动生成...cp -r plugins/autolink your_dir_path/ckeditor/plugins 将此插件添加到您的ckeditor的config.js中,如下所示: config . extraPlugins -
TextView的autoLink属性设置超链接
2018-10-16 15:37:17使用TextView的autoLink属性设置超链接,用工具类方式和自定义View方式两种方式修改超链接显示样式和链接地址的点击事件。 -
autolink-java:Java库,用于从纯文本中提取链接(URL,电子邮件地址); 快速,小巧,智能
2021-05-10 05:04:22自动链接 Java库,用于从纯文本中提取诸如URL和电子邮件地址之类的链接。 明智的做法是知道链接在何处结束(例如使用尾随标点符号)。 介绍 您可能会想:“为此我需要一个库吗?我可以为此编写一个正则表达式!... -
zotero-autolink:zotero-autolink 插件允许 Zotero 用户将他们的链接文件附件存储在独立的网络存储系统上
2021-06-20 17:04:19zotero-autolink zotero-autolink 插件允许 Zotero 用户将他们链接的文件附件存储在独立的网络存储服务上,例如 Dropbox 或私人 ftp 服务器。 使用 zotero-autolink,可以在平板电脑或手机上通过 Zotero 网页界面... -
remark-autolink-headings:插件自动添加链接到标题
2021-07-23 17:49:08npm install remark-autolink-headings 采用 假设我们有以下 markdown 文件example.md : # Lorem ipsum :sleepy_face: ## dolor—sit—amet ### consectetur & adipisicing #### elit ##### elit 我们的... -
Android设置完autoLink属性后自定义跳转到指定界面
2021-06-07 15:51:06在TextView 中设置autoLink 属性可以自动识别WebURL,电话号码,电子邮件地址添加下划线改变字体颜色并实现点击事件,支持自动识别的类型:android:autoLink=“web” 匹配Web URL。android:autoLink=“phone” 匹配...在TextView 中设置autoLink 属性可以自动识别Web
URL,电话号码,电子邮件地址添加下划线改变字体颜色并实现点击事件,支持自动识别的类型:
android:autoLink=“web” 匹配Web URL。
android:autoLink=“phone” 匹配电话号码
android:autoLink=“email” 匹配电子邮件地址
android:autoLink=“map” 匹配地理位置
android:autoLink=“all” 匹配所有可用的模式
android:autoLink=“none” 不匹配任何类型
也可以类似这样的设置 android:autoLink=“web|phone” 表示匹配web URL 和手机号
上面的属性也可以通过java 代码的形式对TextView 设置
setAutoLinkMask(int mask) 进行设置
1
设置的参数分别是:
Linkify.WEB_URLS 匹配Web UR
Linkify.PHONE_NUMBERS 匹配电话号码
Linkify.EMAIL_ADDRESSES 匹配电子邮件地址
Linkify.MAP_ADDRESSES 地理位置匹配
Linkify.ALL 匹配所有可用的模式
设置完antoLink属性,点击TextView中的链接时会跳转的对应的界面,比如点击网页的链接会跳转到系统的默认的浏览器界面,点击手机号会进入拨打电话界面,但是这都是系统默认的,我们可不可以进行拦截处理,跳转到我们指定的界面呢,当然是可以的。下面是我的拦截处理的方法。
继承 MovementMethod ,这里我们先看一下MovementMethod的源码,它的源码比较少
public class LinkMovementMethod extends
ScrollingMovementMethod {
private static final int
CLICK = 1;
private static final int
UP = 2;
private static final int
DOWN = 3;
@Override
public boolean
canSelectArbitrarily() {
return true;
}
@Override
protected boolean
handleMovementKey(TextView widget, Spannable buffer, int
keyCode,
int
movementMetaState, KeyEvent event) {
switch (keyCode) {
case
KeyEvent.KEYCODE_DPAD_CENTER:
case
KeyEvent.KEYCODE_ENTER:
if
(KeyEvent.metaStateHasNoModifiers(movementMetaState)) {
if (event.getAction() == KeyEvent.ACTION_DOWN
&&
event.getRepeatCount() == 0
&& action(CLICK, widget, buffer)) {
return
true;
}
}
break;
}
return super.handleMovementKey(widget, buffer,
keyCode, movementMetaState, event);
}
@Override
protected boolean
up(TextView widget, Spannable buffer) {
if (action(UP, widget, buffer)) {
return
true;
}
return super.up(widget, buffer);
}
@Override
protected boolean
down(TextView widget, Spannable buffer) {
if (action(DOWN, widget, buffer)) {
return
true;
}
return super.down(widget, buffer);
}
@Override
protected boolean
left(TextView widget, Spannable buffer) {
if (action(UP, widget, buffer)) {
return
true;
}
return super.left(widget, buffer);
}
@Override
protected boolean
right(TextView widget, Spannable buffer) {
if (action(DOWN, widget, buffer)) {
return
true;
}
return super.right(widget, buffer);
}
private boolean
action(int what, TextView widget, Spannable buffer) {
Layout layout = widget.getLayout();
int padding = widget.getTotalPaddingTop()
+
widget.getTotalPaddingBottom();
int areaTop = widget.getScrollY();
int areaBot = areaTop + widget.getHeight() -
padding;
int lineTop =
layout.getLineForVertical(areaTop);
int lineBot =
layout.getLineForVertical(areaBot);
int first = layout.getLineStart(lineTop);
int last = layout.getLineEnd(lineBot);
ClickableSpan[] candidates =
buffer.getSpans(first, last, ClickableSpan.class);
int a =
Selection.getSelectionStart(buffer);
int b = Selection.getSelectionEnd(buffer);
int selStart = Math.min(a, b);
int selEnd = Math.max(a, b);
if (selStart < 0) {
if
(buffer.getSpanStart(FROM_BELOW) >= 0) {
selStart = selEnd =
buffer.length();
}
}
if (selStart > last)
selStart =
selEnd = Integer.MAX_VALUE;
if (selEnd < first)
selStart =
selEnd = -1;
switch (what) {
case CLICK:
if
(selStart == selEnd) {
return false;
}
ClickableSpan[] link = buffer.getSpans(selStart, selEnd,
ClickableSpan.class);
if
(link.length != 1)
return false;
link[0].onClick(widget);
break;
case UP:
int
bestStart, bestEnd;
bestStart
= -1;
bestEnd =
-1;
for (int i
= 0; i < candidates.length; i++) {
int end =
buffer.getSpanEnd(candidates[i]);
if (end < selEnd ||
selStart == selEnd) {
if (end > bestEnd) {
bestStart
= buffer.getSpanStart(candidates[i]);
bestEnd =
end;
}
}
}
if
(bestStart >= 0) {
Selection.setSelection(buffer, bestEnd, bestStart);
return true;
}
break;
case DOWN:
bestStart
= Integer.MAX_VALUE;
bestEnd =
Integer.MAX_VALUE;
for (int i
= 0; i < candidates.length; i++) {
int start =
buffer.getSpanStart(candidates[i]);
if (start > selStart ||
selStart == selEnd) {
if (start < bestStart) {
bestStart
= start;
bestEnd =
buffer.getSpanEnd(candidates[i]);
}
}
}
if
(bestEnd < Integer.MAX_VALUE) {
Selection.setSelection(buffer, bestStart, bestEnd);
return true;
}
break;
}
return false;
}
@Override
public boolean
onTouchEvent(TextView widget, Spannable buffer,
MotionEvent event) {
int action = event.getAction();
if (action == MotionEvent.ACTION_UP || action ==
MotionEvent.ACTION_DOWN) {
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[] links = buffer.getSpans(off, off,
ClickableSpan.class);
if
(links.length != 0) {
if (action ==
MotionEvent.ACTION_UP) {
links[0].onClick(widget);
} else if (action ==
MotionEvent.ACTION_DOWN) {
Selection.setSelection(buffer,
buffer.getSpanStart(links[0]),
buffer.getSpanEnd(links[0]));
}
return true;
} else
{
Selection.removeSelection(buffer);
}
}
return super.onTouchEvent(widget, buffer,
event);
}
@Override
public void
initialize(TextView widget, Spannable text) {
Selection.removeSelection(text);
text.removeSpan(FROM_BELOW);
}
@Override
public void
onTakeFocus(TextView view, Spannable text, int dir) {
Selection.removeSelection(text);
if ((dir & View.FOCUS_BACKWARD) != 0)
{
text.setSpan(FROM_BELOW, 0, 0, Spannable.SPAN_POINT_POINT);
} else {
text.removeSpan(FROM_BELOW);
}
}
public static
MovementMethod getInstance() {
if (sInstance == null)
sInstance
= new LinkMovementMethod();
return sInstance;
}
private static
LinkMovementMethod sInstance;
private static Object
FROM_BELOW = new NoCopySpan.Concrete();
}
在上面的这段代码中处理界面跳转的只有这一行
links[0].onClick(widget);我们要实现自己的跳转只要在自定义的LinkMovementMethod的这一行进行处理就行了。
先定义一个替换links[0].onClick(widget);的接口
public interface LinkClickListener {
boolean
onLinkClick(String mURL);
}
定义一个LinkMovementMethod的子类
LinkMovementMethodEx,在onTouchEvent方法中做了判断,在我们应用自己处理的情况,就不在走系统的默认处理,只有我们自己不处理的情况下才走系统的。
public class LinkMovementMethodEx extends LinkMovementMethod
{
private
LinkClickListener listener;
public
LinkMovementMethodEx(LinkClickListener listener) {
this.listener = listener;
}
@Override
public boolean
onTouchEvent(TextView widget, Spannable buffer, MotionEvent event)
{
int action = event.getAction();
if (action == MotionEvent.ACTION_UP || action ==
MotionEvent.ACTION_DOWN) {
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[] links = buffer.getSpans(off, off,
ClickableSpan.class);
if
(links.length != 0) {
if (action ==
MotionEvent.ACTION_UP) {
if (links[0] instanceof URLSpan) {
URLSpan
url = (URLSpan) links[0];
if
(listener != null && listener.onLinkClick(url.getURL()))
{
return true;
} else
{
links[0].onClick(widget);
}
}
} else if (action ==
MotionEvent.ACTION_DOWN) {
Selection.setSelection(buffer,
buffer.getSpanStart(links[0]),
buffer.getSpanEnd(links[0]));
}
return true;
} else
{
Selection.removeSelection(buffer);
}
}
return super.onTouchEvent(widget, buffer,
event);
}
}
在项目中的引用
public class MainActivity extends Activity {
private String TAG =
MainActivity.class.getSimpleName();
private TextView
title;
@Override
protected void
onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
title = findViewById(R.id.tv_content);
title.setText(" 10086 中国移动的 这是百度的
https://www.baidu.com/");
title.setMovementMethod(new
LinkMovementMethodEx(new LinkClickListener() {
@Override
public
boolean onLinkClick(String mURL) {
//在这里执行直接的处理逻辑并将返回值设置为true
Log.i(TAG, "onLinkClick: " +
mURL);
return false;
}
}));
}
}
-
TextView AutoLink, ClikSpan 与长按事件冲突的解决,Android面试真题解析火爆全网
2021-12-15 16:31:05首先调用 Linkify.addLinks 方法解析 autolink 的相关属性 判断是否 mLinksClickable mLinksClickable && !textCanBeSelected() ,若返回 true, 设置 setMovementMethod 我们先来看一下 Linkify 类, ...}
}
}
-
首先调用 Linkify.addLinks 方法解析 autolink 的相关属性
-
判断是否 mLinksClickable mLinksClickable && !textCanBeSelected() ,若返回 true, 设置 setMovementMethod
我们先来看一下 Linkify 类, 里面定义了几个常量, 分别对应 web , email ,phone ,map,他们的值是位上错开的,这样定义的好处是
-
方便组合多种值
-
组合值之后不会丢失状态,即可以获取是否含有某种状态, web, email, phone , map
public class Linkify {
public static final int WEB_URLS = 0x01;
public static final int EMAIL_ADDRESSES = 0x02;
public static final int PHONE_NUMBERS = 0x04;
public static final int MAP_ADDRESSES = 0x08;
}
看一下 linkify 的 addLinks 方法
-
根据 mask 的标志位,进行相应的正则表达式进行匹配,找到 text 里面的相应的 WEB_URLS, EMAIL_ADDRESSES, PHONE_NUMBERS, MAP_ADDRESSES. 并将相应的文本从 text 里面移除,封装成 LinkSpec,并添加到 links 里面
-
遍历 links,设置相应的 URLSpan
pri
《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享
vate static boolean addLinks(@NonNull Spannable text, @LinkifyMask int mask,
@Nullable Context context) {
if (mask == 0) {
return false;
}
URLSpan[] old = text.getSpans(0, text.length(), URLSpan.class);
for (int i = old.length - 1; i >= 0; i–) {
text.removeSpan(old[i]);
}
ArrayList links = new ArrayList();
/ / 根据正则表达式提取 text 里面相应的 WEB_URLS,并且从 text 移除
if ((mask & WEB_URLS) != 0) {
gatherLinks(links, text, Patterns.AUTOLINK_WEB_URL,
new String[] { “http://”, “https://”, “rtsp://” },
sUrlMatchFilter, null);
}
if ((mask & EMAIL_ADDRESSES) != 0) {
gatherLinks(links, text, Patterns.AUTOLINK_EMAIL_ADDRESS,
new String[] { “mailto:” },
null, null);
}
if ((mask & PHONE_NUMBERS) != 0) {
gatherTelLinks(links, text, context);
}
if ((mask & MAP_ADDRESSES) != 0) {
gatherMapLinks(links, text);
}
pruneOverlaps(links);
if (links.size() == 0) {
return false;
}
// 遍历 links,设置相应的 URLSpan
for (LinkSpec link: links) {
applyLink(link.url, link.start, link.end, text);
}
return true;
}
private static final void applyLink(String url, int start, int end, Spannable text) {
URLSpan span = new URLSpan(url);
text.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
接下来我们一起来看一下这个 URLSpan 是何方神圣,它继承了 ClickableSpan(注意下文会用到它),并且重写了 onClick 方法,我们可以看到在 onClick 方法里面,他通过相应的 intent 取启动相应的 activity。因此,我们可以断定 autolink 的自动跳转是在这里处理的。
public class URLSpan extends ClickableSpan implements ParcelableSpan {
private final String mURL;
/**
-
Constructs a {@link URLSpan} from a url string.
-
@param url the url string
*/
public URLSpan(String url) {
mURL = url;
}
/**
- Constructs a {@link URLSpan} from a parcel.
*/
public URLSpan(@NonNull Parcel src) {
mURL = src.readString();
}
@Override
public int getSpanTypeId() {
return getSpanTypeIdInternal();
}
@Override
public void onClick(View widget) {
Uri uri = Uri.parse(getURL());
Context context = widget.getContext();
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
try {
context.startActivity(intent);
} catch (ActivityNotFoundException e) {
Log.w(“URLSpan”, "Actvity was not found for intent, " + intent.toString());
}
}
}
解决了 autolink 属性点击事件在哪里响应了,接下来我们一起看一下 URLSpan 的 onClick 方法是在哪里调用的。
autolink 的 onclick 事件是在哪里被调用的
我们先来复习一下 View 的事件分发机制:
-
dispatchTouchEvent ,这个方法主要是用来分发事件的
-
onInterceptTouchEvent,这个方法主要是用来拦截事件的(需要注意的是ViewGroup才有这个方法,- View没有onInterceptTouchEvent这个方法
-
onTouchEvent 这个方法主要是用来处理事件的
requestDisallowInterceptTouchEvent(true),这个方法能够影响父View是否拦截事件,true 表示父 View 不拦截事件,false 表示父 View 拦截事件
因此我们猜测 URLSpan 的 onClick 事件是在 TextView 的 onTouchEvent 事件里面调用的。下面让我们一起来看一下 TextView 的 onTouchEvent 方法
@Override
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);
/*
-
Don’t handle the release after a long press, because it will move the selection away from
-
whatever the menu action was trying to affect. If the long press should have triggered an
-
insertion action mode, we can now actually show it.
*/
if (mEditor != null && mEditor.mDiscardNextActionUp && action == MotionEvent.ACTION_UP) {
mEditor.mDiscardNextActionUp = false;
if (mEditor.mIsInsertionActionModeStartPending) {
mEditor.startInsertionActionMode();
mEditor.mIsInsertionActionModeStartPending = false;
}
return superResult;
}
final boolean touchIsFinished = (action == MotionEvent.ACTION_UP)
&& (mEditor == null || !mEditor.mIgnoreActionUpEvent) && isFocused();
if ((mMovement != null || onCheckIsTextEditor()) && isEnabled()
&& mText instanceof Spannable && mLayout != null) {
boolean handled = false;
if (mMovement != null) {
handled |= mMovement.onTouchEvent(this, mSpannable, event);
}
final boolean textIsSelectable = isTextSelectable();
if (touchIsFinished && mLinksClickable && mAutoLinkMask != 0 && textIsSelectable) {
// The LinkMovementMethod which should handle taps on links has not been installed
// on non editable text that support text selection.
// We reproduce its behavior here to open links for these.
ClickableSpan[] links = mSpannable.getSpans(getSelectionStart(),
getSelectionEnd(), ClickableSpan.class);
if (links.length > 0) {
links[0].onClick(this);
handled = true;
}
}
if (touchIsFinished && (isTextEditable() || textIsSelectable)) {
// Show the IME, except when selecting in read-only text.
final InputMethodManager imm = InputMethodManager.peekInstance();
viewClicked(imm);
if (isTextEditable() && mEditor.mShowSoftInputOnFocus && imm != null) {
imm.showSoftInput(this, 0);
}
// The above condition ensures that the mEditor is not null
mEditor.onTouchUpEvent(event);
handled = true;
}
if (handled) {
return true;
}
}
return superResult;
}
首先如果 mEditor != null 会将touch事件交给mEditor处理,这个 mEditor 其实是和 EditText 有关系的,没有使用 EditText 这里应该是不会被创建的。
去除 mEditor != null 的相关逻辑之后,剩下的相关代码主要如下:
final boolean touchIsFinished = (action == MotionEvent.ACTION_UP)
&& (mEditor == null || !mEditor.mIgnoreActionUpEvent) && isFocused();
if ((mMovement != null || onCheckIsTextEditor()) && isEnabled()
&& mText instanceof Spannable && mLayout != null) {
boolean handled = false;
if (mMovement != null) {
handled |= mMovement.onTouchEvent(this, mSpannable, event);
}
final boolean textIsSelectable = isTextSelectable();
if (touchIsFinished && mLinksClickable && mAutoLinkMask != 0 && textIsSelectable) {
// The LinkMovementMethod which should handle taps on links has not been installed
// on non editable text that support text selection.
// We reproduce its behavior here to open links for these.
ClickableSpan[] links = mSpannable.getSpans(getSelectionStart(),
getSelectionEnd(), ClickableSpan.class);
if (links.length > 0) {
links[0].onClick(this);
handled = true;
}
}
if (touchIsFinished && (isTextEditable() || textIsSelectable)) {
// Show the IME, except when selecting in read-only text.
final InputMethodManager imm = InputMethodManager.peekInstance();
viewClicked(imm);
if (isTextEditable() && mEditor.mShowSoftInputOnFocus && imm != null) {
imm.showSoftInput(this, 0);
}
// The above condition ensures that the mEditor is not null
mEditor.onTouchUpEvent(event);
handled = true;
}
if (handled) {
return true;
}
}
首先我们先来看一下, mMovement 是否可能为 null,若不为 null,则会调用 handled |= mMovement.onTouchEvent(this, mSpannable, event) 方法。
找啊找,发现在 setText 里面有调用这一段代码,setMovementMethod(LinkMovementMethod.getInstance()); 即 mLinksClickable && !textCanBeSelected() 为 true 的时候给 TextView 设置 MovementMethod。
查看 TextView 的源码我们容易得知 mLinksClickable 的值默认为 true, 而 textCanBeSelected 方法会返回 false,即 mLinksClickable && !textCanBeSelected() 为 true,这个时候会给 TextView 设置 setMovementMethod。 因此在 TextView 的 onTouchEvent 方法中,若 autoLink 等于 true,并且 text 含有 email,phone, webAddress 等的时候,会调用 mMovement.onTouchEvent(this, mSpannable, event) 方法。
if (Linkify.addLinks(s2, mAutoLinkMask)) {
text = s2;
type = (type == BufferType.EDITABLE) ? BufferType.EDITABLE : BufferType.SPANNABLE;
/*
-
We must go ahead and set the text before changing the
-
movement method, because setMovementMethod() may call
-
setText() again to try to upgrade the buffer type.
*/
setTextInternal(text);
// Do not change the movement method for text that support text selection as it
// would prevent an arbitrary cursor displacement.
if (mLinksClickable && !textCanBeSelected()) {
setMovementMethod(LinkMovementMethod.getInstance());
}
}
boolean textCanBeSelected() {
// prepareCursorController() relies on this method.
// If you change this condition, make sure prepareCursorController is called anywhere
// the value of this condition might be changed.
// 默认 mMovement 为 null
if (mMovement == null || !mMovement.canSelectArbitrarily()) return false;
return isTextEditable()
|| (isTextSelectable() && mText instanceof Spannable && isEnabled());
}
ok ,我们一起在来看一下 mMovement 的 onTouchEvent 方法
MovementMethod 是一个借口,实现子类有 ArrowKeyMovementMethod, LinkMovementMethod, ScrollingMovementMethod 。
这里我们先来看一下 LinkMovementMethod 的 onTouchEvent 方法
public boolean onTouchEvent(TextView widget, Spannable buffer,
MotionEvent event) {
int action = event.getAction();
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) {
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[] links = buffer.getSpans(off, off, ClickableSpan.class);
if (links.length != 0) {
ClickableSpan link = links[0];
if (action == MotionEvent.ACTION_UP) {
if (link instanceof TextLinkSpan) {
((TextLinkSpan) link).onClick(
widget, TextLinkSpan.INVOCATION_METHOD_TOUCH);
} else {
link.onClick(widget);
}
}
这里我们重点关注代码 20 - 31 行,可以看到,他会先取出所有的 ClickableSpan,而我们的 URLSpan 正是 ClickableSpan 的子类,接着判断是否是 ACTION_UP 事件,然后调用 onClick 事件。因此,ClickableSpan 的 onClick 方法是在 ACTION_UP 事件中调用的,跟我们的长按事件没半毛钱关系。
重要的事情说三遍
ClickableSpan 的 onClick 方法是在 ACTION_UP 事件中调用的
ClickableSpan 的 onClick 方法是在 ACTION_UP 事件中调用的
ClickableSpan 的 onClick 方法是在 ACTION_UP 事件中调用的
知道了 ClickableSpan 的 onClick 方法是在 ACTION_UP 事件中调用的,下面让我们一起来看一下怎样解决 TextView 中 autolink 与 clickableSpan 与长按事件的冲突。
-
-
rails_autolink:Rails的auto_link函数
2021-04-30 01:52:39rails_autolink 描述: 这是从rails中提取的auto_link方法。 `auto_link`方法已从Rails 3.1版本的Rails中删除。 该宝石旨在弥合移民人群的鸿沟。 特点: 默认情况下,auto_link返回已清理的html_safe字符串。... -
TextView AutoLink, ClikSpan 与长按事件冲突的解决
2022-04-14 21:33:32[](()autolink 的 onclick 事件是在哪里被调用的 我们先来复习一下 View 的事件分发机制: dispatchTouchEvent ,这个方法主要是用来分发事件的 onInterceptTouchEvent,这个方法主要是用来拦截事件的(需要注意的...
if (mAutoLinkMask != 0) {
Spannable s2;
if (type == BufferType.EDITABLE || text instanceof Spannable) {
s2 = (Spannable) text;
} else {
s2 = mSpannableFactory.newSpannable(text);
}
if (Linkify.addLinks(s2, mAutoLinkMask)) {
text = s2;
type = (type == BufferType.EDITABLE) ? BufferType.EDITABLE : BufferType.SPANNABLE;
/*
-
We must go ahead and set the text before changing the
-
movement method, because setMovementMethod() may call
-
setText() again to try to upgrade the buffer type.
*/
setTextInternal(text);
// Do not change the movement method for text that support text selection as it
// would prevent an arbitrary cursor displacement.
if (mLinksClickable && !textCanBeSelected()) {
setMovementMethod(LinkMovementMethod.getInstance());
}
}
}
-
首先调用 Linkify.addLinks 方法解析 autolink 的相关属性
-
判断是否 mLinksClickable mLinksClickable && !textCanBeSelected() ,若返回 true, 设置 setMovementMethod
我们先来看一下 Linkify 类, 里面定义了几个常量, 分别对应 web , email ,phone ,map,他们的值是位上错开的,这样定义的好处是
-
方便组合多种值
-
组合值之后不会丢失状态,即可以获取是否含有某种状态, web, email, phone , map
public class Linkify {
public static final int WEB_URLS = 0x01;
public static final int EMAIL_ADDRESSES = 0x02;
public static final int PHONE_NUMBERS = 0x04;
public static final int MAP_ADDRESSES = 0x08;
}
看一下 linkify 的 addLinks 方法
-
根据 mask 的标志位,进行相应的正则表达式进行匹配,找到 text 里面的相应的 WEB_URLS, EMAIL_ADDRESSES, PHONE_NUMBERS, MAP_ADDRESSES. 并将相应的文本从 text 里面移除,封装成 LinkSpec,并添加到 links 里面
-
遍历 links,设置相应的 URLSpan
private static boolean addLinks(@NonNull Spannable text, @LinkifyMask int mask,
@Nullable Context context) {
if (mask == 0) {
return false;
}
URLSpan[] old = text.getSpans(0, text.length(), URLSpan.class);
for (int i = old.length - 1; i >= 0; i–) {
text.removeSpan(old[i]);
}
ArrayList links = new ArrayList();
/ / 根据正则表达式提取 text 里面相应的 WEB_URLS,并且从 text 移除
if ((mask & WEB_URLS) != 0) {
gatherLinks(links, text, Patterns.AUTOLINK_WEB_URL,
new String[] { “http://”, “https://”, “rtsp://” },
sUrlMatchFilter, null);
}
if ((mask & EMAIL_ADDRESSES) != 0) {
gatherLinks(links, text, Patterns.AUTOLINK_EMAIL_ADDRESS,
new String[] { “mailto:” },
null, null);
}
if ((mask & PHONE_NUMBERS) != 0) {
gatherTelLinks(links, text, context);
}
if ((mask & MAP_ADDRESSES) != 0) {
gatherMapLinks(links, text);
}
pruneOverlaps(links);
if (links.size() == 0) {
return false;
}
// 遍历 links,设置相应的 URLSpan
for (LinkSpec link: links) {
applyLink(link.url, link.start, link.end, text);
}
return true;
}
private static final void applyLink(String url, int start, int end, Spannable text) {
URLSpan span = new URLSpan(url);
text.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
接下来我们一起来看一下这个 URLSpan 是何方神圣,它继承了 ClickableSpan(注意下文会用到它),并且重写了 onClick 方法,我们可以看到在 onClick 方法里面,他通过相应的 intent 取启动相应的 activity。因此,我们可以断定 autolink 的自动跳转是在这里处理的。
public class URLSpan extends ClickableSpan implements ParcelableSpan {
private final String mURL;
/**
-
Constructs a {@link URLSpan} from a url string.
-
@param url the url string
*/
public URLSpan(String url) {
mURL = url;
}
/**
- Constructs a {@link URLSpan} from a parcel.
*/
public URLSpan(@NonNull Parcel src) {
mURL = src.readString();
}
@Override
public int getSpanTypeId() {
return getSpanTypeIdInternal();
}
@Override
public void onClick(View widget) {
Uri uri = Uri.parse(getURL());
Context context = widget.getContext();
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
try {
context.startActivity(intent);
} catch (ActivityNotFoundException e) {
Log.w(“URLSpan”, "Actvity was not found for intent, " + intent.toString());
}
}
}
解决了 autolink 属性点击事件在哪里响应了,接下来我们一起看一下 URLSpan 的 onClick 方法是在哪里调用的。
[](()autolink 的 onclick 事件是在哪里被调用的
我们先来复习一下 View 的事件分发机制:
-
dispatchTouchEvent ,这个方法主要是用来分发事件的
-
onInterceptTouchEvent,这个方法主要是用来拦截事件的(需要注意的是ViewGroup才有这个方法,- View没有onInterceptTouchEvent这个方法
-
onTouchEvent 这个方法主要是用来处理事件的
requestDisallowInterceptTouchEvent(true),这个方法能够影响父View是否拦截事件,true 表示父 View 不拦截事件,false 表示父 View 拦截事件
因此我们猜测 URLS 《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》开源 pan 的 onClick 事件是在 TextView 的 onTouchEvent 事件里面调用的。下面让我们一起来看一下 TextView 的 onTouchEvent 方法
@Override
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);
/*
-
Don’t handle the release after a long press, because it will move the selection away from
-
whatever the menu action was trying to affect. If the long press should have triggered an
-
insertion action mode, we can now actually show it.
*/
if (mEditor != null && mEditor.mDiscardNextActionUp && action == MotionEvent.ACTION_UP) {
mEditor.mDiscardNextActionUp = false;
if (mEditor.mIsInsertionActionModeStartPending) {
mEditor.startInsertionActionMode();
mEditor.mIsInsertionActionModeStartPending = false;
}
return superResult;
}
final boolean touchIsFinished = (action == MotionEvent.ACTION_UP)
&& (mEditor == null || !mEditor.mIgnoreActionUpEvent) && isFocused();
if ((mMovement != null || onCheckIsTextEditor()) && isEnabled()
&& mText instanceof Spannable && mLayout != null) {
boolean handled = false;
if (mMovement != null) {
handled |= mMovement.onTouchEvent(this, mSpannable, event);
}
final boolean textIsSelectable = isTextSelectable();
if (touchIsFinished && mLinksClickable && mAutoLinkMask != 0 && textIsSelectable) {
// The LinkMovementMethod which should handle taps on links has not been installed
// on non editable text that support text selection.
// We reproduce its behavior here to open links for these.
ClickableSpan[] links = mSpannable.getSpans(getSelectionStart(),
getSelectionEnd(), ClickableSpan.class);
if (links.length > 0) {
links[0].onClick(this);
handled = true;
}
}
if (touchI Android开源项目:ali1024.coding.net/public/P7/Android/git sFinished && (isTextEditable() || textIsSelectable)) {
// Show the IME, except when selecting in read-only text.
final InputMethodManager imm = InputMethodManager.peekInstance();
viewClicked(imm);
if (isTextEditable() && mEditor.mShowSoftInputOnFocus && imm != null) {
imm.showSoftInput(this, 0);
}
// The above condition ensures that the mEditor is not null
mEditor.onTouchUpEvent(event);
handled = true;
}
if (handled) {
return true;
}
}
return superResult;
}
首先如果 mEditor != null 会将touch事件交给mEditor处理,这个 mEditor 其实是和 EditText 有关系的,没有使用 EditText 这里应该是不会被创建的。
去除 mEditor != null 的相关逻辑之后,剩下的相关代码主要如下:
final boolean touchIsFinished = (action == MotionEvent.ACTION_UP)
&& (mEditor == null || !mEditor.mIgnoreActionUpEvent) && isFocused();
if ((mMovement != null || onCheckIsTextEditor()) && isEnabled()
&& mText instanceof Spannable && mLayout != null) {
boolean handled = false;
if (mMovement != null) {
handled |= mMovement.onTouchEvent(this, mSpannable, event);
}
结语
- 现在随着短视频,抖音,快手的流行NDK模块开发也显得越发重要,需要这块人才的企业也越来越多,随之学习这块的人也变多了,音视频的开发,往往是比较难的,而这个比较难的技术就是NDK里面的技术。
- 音视频/高清大图片/人工智能/直播/抖音等等这年与用户最紧密,与我们生活最相关的技术一直都在寻找最终的技术落地平台,以前是windows系统,而现在则是移动系统了,移动系统中又是以Android占比绝大部分为前提,所以AndroidNDK技术已经是我们必备技能了。
- 要学习好NDK,其中的关于C/C++,jni,Linux基础都是需要学习的,除此之外,音视频的编解码技术,流媒体协议,ffmpeg这些都是音视频开发必备技能,而且
- OpenCV/OpenGl/这些又是图像处理必备知识,下面这些我都是当年自己搜集的资料和做的一些图,因为当年我就感觉视频这块会是一个大的趋势。所以提前做了一些准备。现在拿出来分享给大家。
vent(this, mSpannable, event);}
结语
- 现在随着短视频,抖音,快手的流行NDK模块开发也显得越发重要,需要这块人才的企业也越来越多,随之学习这块的人也变多了,音视频的开发,往往是比较难的,而这个比较难的技术就是NDK里面的技术。
- 音视频/高清大图片/人工智能/直播/抖音等等这年与用户最紧密,与我们生活最相关的技术一直都在寻找最终的技术落地平台,以前是windows系统,而现在则是移动系统了,移动系统中又是以Android占比绝大部分为前提,所以AndroidNDK技术已经是我们必备技能了。
- 要学习好NDK,其中的关于C/C++,jni,Linux基础都是需要学习的,除此之外,音视频的编解码技术,流媒体协议,ffmpeg这些都是音视频开发必备技能,而且
- OpenCV/OpenGl/这些又是图像处理必备知识,下面这些我都是当年自己搜集的资料和做的一些图,因为当年我就感觉视频这块会是一个大的趋势。所以提前做了一些准备。现在拿出来分享给大家。
[外链图片转存中…(img-oUPRA0fG-1649943193170)]
[外链图片转存中…(img-DqB7tblT-1649943193171)]
-
-
jekyll-autolink_email:自动链接您的Jekyll电子邮件
2021-05-18 18:18:18Jekyll :: Autolink电子邮件 为您的Jekyll网站自动链接电子邮件。 安装 添加到您的Gemfile : gem 'jekyll-autolink_email' 添加到您的_config.yml : gems : - jekyll-autolink_email 用法 在任何html页面或... -
AutoLink.rar
2020-04-06 18:44:19本资源是GitHub上AutoLink,自动玩连连看软件的相关文件。 Git Hub 项目地址:https://github.com/ztmajor/AutoLink 包括了雷电模拟器下载器,单机连连看.apk,训练好的模型(alexnet.pth)和训练好的模型参数 ... -
autolink-开源
2021-05-01 06:51:03perl工具,用于通过本地自定义自动维护多台配置相似的unix机器。 可逐步安装。 基于文件。 -
TextView autoLink="phone" 在8.1机器上无效
2021-07-17 06:17:221.电话号码识别Android SDK中,TextView提供了一个autoLink属性来帮助我们识别各种各样的链接,我们只需要将autoLink属性设置为"phone",就能轻易识别出电话号码。这个属性在8.1之前的机器上是可以正常工作... -
Android TextView autoLink web Bug
2021-06-07 11:41:17I have a TextView like this.android:id="@+id/text_auto_linkify"android:layout_width="match_parent"android:layout_height="wrap_content"android:autoLink="web"android:text="@string/link_text_auto" />... -
去掉TextView中autolink的下划线
2021-06-07 17:15:23我们知道,在布局文件中设置textview的autolink及其类型,这时textivew上会显示link的颜色,并且文字下面会有一条下划线,表示可以点击。而在我们在点击textview时,应用将根据我们所设置的类型跳转到对应的界面。... -
autolink-0.6.0.jar
2022-02-23 22:07:47java运行依赖jar包 -
TextView的autoLink属性设置超链接问题
2018-10-16 15:18:16最近接到类似的这种需求,网上查找资料学习到了TextView的autoLink属性,那autoLink是怎么使用的呢?为什么设置autoLink就可以实现TextView的超链接,底层是怎么实现的呢?TextView显示时自动排版不整齐,怎么解决呢... -
rehype-autolink-headings:插件以添加指向HTML标题的链接
2021-04-30 00:48:12npm install rehype-autolink-headings 用 假设我们有以下文件fragment.html : < h1> Lorem ipsum :sleepy_face: < h2> dolor—sit—amet < h3> consectetur & adipisicing < h4> elit < h5> elit 我们... -
autolink.cr:Crystal的自动链接
2021-02-04 23:21:13autolink.cr:Crystal的自动链接 -
android - autolink窃取ontouch事件,从而反馈触摸列表视图行无效 - 堆栈内存溢出
2021-06-05 02:12:26我有一个listview 。 并且行布局中包含很少的布局。 在行布局中,我有一个TextView ,我想在其上启用自动链接当我启用自动链接时,链接工作正常但...cardview="http://schemas.android.com/apk/res-auto" android:id="@+id/card_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@color/White" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" cardview:cardCornerRadius="5dp" cardview:cardElevation="5dp" cardview:cardBackgroundColor="@color/White" cardview:cardUseCompatPadding="true" > android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" android:descendantFocusability="blocksDescendants"> android:id="@+id/message_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingLeft="10dp" android:paddingRight="10dp" android:paddingTop="10dp" android:layout_marginBottom="10dp" android:text="M" android:gravity="top" android:textAppearance="?android:attr/textAppearanceMedium" android:focusable="false" android:focusableInTouchMode="false" android:autoLink="web" android:background="@color/Blue" /> android:id="@+id/Share" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_below="@+id/message_text" android:focusable="true" android:layout_margin="5dp" android:background="@drawable/share3" android:adjustViewBounds="True" /> android:id="@+id/time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignBaseline="@+id/Share" android:layout_alignBottom="@+id/Share" android:layout_alignParentRight="true" android:layout_margin="5dp" android:text="Timexxxxx" android:layout_gravity="right" android:textAppearance="?android:attr/textAppearanceSmall" android:textColor="@color/Time" /> android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_above="@+id/time" android:layout_alignParentLeft="true" android:paddingBottom="2dp" android:paddingLeft="5dp" android:paddingRight="5dp" android:paddingTop="2dp" android:layout_marginTop="10dp" android:scaleType="fitXY" android:src="@android:drawable/divider_horizontal_bright" /> 有人请帮忙吗... -
Android开发技巧——去掉TextView中autolink的下划线
2021-06-07 17:15:28我们知道,在布局文件中设置textview的autolink及其类型,这时textivew上会显示link的颜色,并且文字下面会有一条下划线,表示可以点击。而在我们在点击textview时,应用将根据我们所设置的类型跳转到对应的界面。... -
AutoLink平台功能特色及文档说明
2021-12-17 18:18:16关键字概要说明 如何使用调度管理 上传和下载RobotFramework用例 微信群 目前AutoLink专用讨论微信群已经建立,为严格控制准入条件的群,加入条件请参见:AutoLink微信群 数据与用例分离的标准项目结构 支持的关键... -
roam-autolink-crx插件
2021-04-03 02:50:46语言:English (United States) 跟踪和自动化漫游研究中链接/页面中的单词 这是漫游的扩展,允许用户指定某些单词以在键入时自动形成链接。 使用方法:... -
AutoLink开源自动化测试集成解决方案.docx
2021-10-26 07:22:06AutoLink开源自动化测试集成解决方案.docx -
Business Contact Manager Autolink Plugin-开源
2021-05-29 02:52:41为 Microsoft 的 Business Contact Manager 自动链接。 作为一项服务安装,该程序会在添加新联系人时为所有用户添加自动链接。 -
flexmark-ext-autolink-0.42.8.jar
2022-02-24 12:21:36java运行依赖jar包