ListView: TextView with LinkMovementMethod makes list item unclickable?
There are THREE show-stoppers in this situation. The root reason is that when you call setMovementMethod
or setKeyListener
, TextView
"fixes" it's settings:
setFocusable(true);
setClickable(true);
setLongClickable(true);
The first problem is that when a View is clickable - it always consumes ACTION_UP
event (it returns true in onTouchEvent(MotionEvent event)
).
To fix that you should return true in that method only if the user actually clicks the URL.
But the LinkMovementMethod
doesn't tell us, if the user actually clicked a link. It returns "true" in it's onTouch
if the user clicks the link, but also in many other cases.
So, actually I did a trick here:
public class TextViewFixTouchConsume extends TextView {
boolean dontConsumeNonUrlClicks = true;
boolean linkHit;
public TextViewFixTouchConsume(Context context) {
super(context);
}
public TextViewFixTouchConsume(Context context, AttributeSet attrs) {
super(context, attrs);
}
public TextViewFixTouchConsume(
Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
linkHit = false;
boolean res = super.onTouchEvent(event);
if (dontConsumeNonUrlClicks)
return linkHit;
return res;
}
public void setTextViewHTML(String html)
{
CharSequence sequence = Html.fromHtml(html);
SpannableStringBuilder strBuilder =
new SpannableStringBuilder(sequence);
setText(strBuilder);
}
public static class LocalLinkMovementMethod extends LinkMovementMethod{
static LocalLinkMovementMethod sInstance;
public static LocalLinkMovementMethod getInstance() {
if (sInstance == null)
sInstance = new LocalLinkMovementMethod();
return sInstance;
}
@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[] link = buffer.getSpans(
off, off, ClickableSpan.class);
if (link.length != 0) {
if (action == MotionEvent.ACTION_UP) {
link[0].onClick(widget);
} else if (action == MotionEvent.ACTION_DOWN) {
Selection.setSelection(buffer,
buffer.getSpanStart(link[0]),
buffer.getSpanEnd(link[0]));
}
if (widget instanceof TextViewFixTouchConsume){
((TextViewFixTouchConsume) widget).linkHit = true;
}
return true;
} else {
Selection.removeSelection(buffer);
Touch.onTouchEvent(widget, buffer, event);
return false;
}
}
return Touch.onTouchEvent(widget, buffer, event);
}
}
}
You should call somewhere
textView.setMovementMethod(
TextViewFixTouchConsume.LocalLinkMovementMethod.getInstance()
);
to set this MovementMethod for the textView.
This MovementMethod raises a flag in TextViewFixTouchConsume
if user actually hits link.
(only in ACTION_UP
and ACTION_DOWN
events) and TextViewFixTouchConsume.onTouchEvent
returns true only if user actually hit link.
But that's not all!!!!
The third problem is that ListView
(AbsListView
) calls it's performClick
method (that calls onItemClick
event handler) ONLY if ListView
's item view has no focusables.
So, you need to override
@Override
public boolean hasFocusable() {
return false;
}
in a view that you add to ListView
.
(in my case that is a layout that contains textView)
or you can use setOnClickLIstener
for that view.
The trick is not very good, but it works.
ListView with TextView autoLink not receiving OnItemClickListener
Try adding android:descendantFocusability="blocksDescendants"
to the root view in your row layout. I've done some testing with this and it seems to work.
ListView setOnItemClickListener Does Not Trigger When An Item Has a Link In It
You probably want to take a look at babay's answer on ListView: TextView with LinkMovementMethod makes list item unclickable?
He presents a TextView that issues this problem basically by introducing a different LinkMovementMethod
:
public static class LocalLinkMovementMethod extends LinkMovementMethod{
static LocalLinkMovementMethod sInstance;
public static LocalLinkMovementMethod getInstance() {
if (sInstance == null)
sInstance = new LocalLinkMovementMethod();
return sInstance;
}
@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[] link = buffer.getSpans(off, off, ClickableSpan.class);
if (link.length != 0) {
if (action == MotionEvent.ACTION_UP) {
link[0].onClick(widget);
} else if (action == MotionEvent.ACTION_DOWN) {
Selection.setSelection(buffer,
buffer.getSpanStart(link[0]),
buffer.getSpanEnd(link[0]));
}
if (widget instanceof TextViewFixTouchConsume){
((TextViewFixTouchConsume) widget).linkHit = true;
}
return true;
} else {
Selection.removeSelection(buffer);
Touch.onTouchEvent(widget, buffer, event);
return false;
}
}
return Touch.onTouchEvent(widget, buffer, event);
}
}
Solution NOT TESTED
Based on it, I advice you to fix the file TwitterLinkify.java
(the function that sets the link movement) like this:
private static final void addLinkMovementMethod(TextView t) {
MovementMethod m = t.getMovementMethod();
if ((m == null) || !(m instanceof LocalLinkMovementMethod)) {
if (t.getLinksClickable()) {
t.setMovementMethod(LocalLinkMovementMethod.getInstance());
}
}
}
Thanks
Related Topics
Android Listview Child View Setenabled() and Setclickable() Do Nothing
Firebase Endat() Not Working with Date String
Searchview in Centre and After Focus It Goes to App Bar in Android
Android: Support Multiple Screens
How to Implement Onbackpressed() & Intents in Fragment
How to Make Grid View Scroll Horizontally Not Vertically in Android
What Features Do Progressive Web Apps Have VS. Native Apps and Vice-Versa, on Android
Google Maps API V2 Class Not Found
Android Device Configuration for Aosp
Web App on Android Browser Width Issue
CSS Overflow and Absolute Positioning Issue on Android Browser
Android, How to Read Qr Code in My Application
Sqlitedatabase Close() Function Causing Nullpointerexception When Multiple Threads
Finish an Activity After a Time Period
Textinputlayout Hint Doesn't Float Up After Updating Google Support Library
Flutter: How to Fix "A Renderflex Overflowed by Pixels " Error