ListView:使用LinkMovementMethod的TextView使列表项无法点击?
ListView:使用LinkMovementMethod的TextView使列表项无法点击?
我的目标是创建一个包含如下消息的列表:
这是用户写的消息,它会很好地换行。就像这样。
我有以下内容:
ListView R.layout.list_item:
适配器会填充上述布局并执行以下操作:
SpannableStringBuilder f = new SpannableStringBuilder(check.getContent()); f.append(username); f.setSpan(new InternalURLSpan(new OnClickListener() { @Override public void onClick(View v) { Toast.makeText(context, "Clicked User", Toast.LENGTH_SHORT).show(); } }), f.length() - username.length(), f.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); f.append(" " + message); messageTextView.setText(f); messageTextView.setMovementMethod(LinkMovementMethod.getInstance()); meesageTextView.setFocusable(false);
InternalURLSpan类:
public class InternalURLSpan extends ClickableSpan { OnClickListener mListener; public InternalURLSpan(OnClickListener listener) { mListener = listener; } @Override public void onClick(View widget) { mListener.onClick(widget); } @Override public void updateDrawState(TextPaint ds) { super.updateDrawState(ds); ds.setUnderlineText(false); } }
在活动的onCreate(...)
方法中:
listView.setOnItemClickListener(ProgramChecksActivity.this);
以及上述的实现:
@Override public void onItemClick(AdapterView> adapterView, View view, int position, long id) { Toast.makeText(context, "Clicked Item", Toast.LENGTH_SHORT).show(); }
问题:
点击列表项时不会显示toast。只有点击用户名才会显示toast。
我猜测setMovementMethod(LinkMovementMethod.getInstance());
使TextView可点击。所以,列表项本身不再被点击。
如何使列表项再次可点击,并具有相同的功能?
ListView中的TextView使用LinkMovementMethod后,列表项不可点击的原因可能是因为LinkMovementMethod会覆盖ListView的点击事件,导致无法触发列表项的点击事件。解决方法是使用自定义的OnTouchListener,在触摸事件中模拟LinkMovementMethod的点击功能,以实现点击链接和点击列表项的双重功能。
具体的解决方法如下:
1. 在适配器的getView()方法中为TextView设置自定义的OnTouchListener。
2. 在OnTouchListener的onTouch方法中,获取点击事件的坐标,并根据坐标计算出点击位置在TextView中的行数和偏移量。
3. 使用getSpans方法获取点击位置处的ClickableSpan对象数组。
4. 如果获取到的ClickableSpan数组不为空,说明点击位置处有可点击的链接,根据点击事件的类型执行ClickableSpan的onClick方法,并返回true表示已处理点击事件。
5. 如果获取到的ClickableSpan数组为空,说明点击位置处没有可点击的链接,返回false表示未处理点击事件。
代码如下:
myTextView.setOnTouchListener(new OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { boolean ret = false; CharSequence text = ((TextView) v).getText(); Spannable stext = Spannable.Factory.getInstance().newSpannable(text); TextView widget = (TextView) v; 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 = stext.getSpans(off, off, ClickableSpan.class); if (link.length != 0) { if (action == MotionEvent.ACTION_UP) { link[0].onClick(widget); } ret = true; } } return ret; } });
需要注意的是,这种解决方法可能会导致在ClickableSpan外部点击区域也被认为是点击了ClickableSpan,因此可以在点击事件中先判断点击位置是否在ClickableSpan的范围内,再决定是否处理点击事件。
此外,可能会出现警告信息"'onTouch should call View#performClick when a click is detected'",可以忽略该警告或在onTouch方法中添加View的performClick方法以消除警告。
总结起来,解决ListView中TextView使用LinkMovementMethod导致列表项不可点击的问题的方法是使用自定义的OnTouchListener来模拟LinkMovementMethod的点击功能,并在触摸事件中判断点击位置和处理点击事件。
这篇文章将讨论一个关于ListView中的TextView使用LinkMovementMethod导致列表项不可点击的问题,以及解决方法。
问题的原因是,当TextView中使用LinkMovementMethod时,它会捕捉到触摸事件,并处理链接的点击事件。这会导致ListView无法捕捉到触摸事件,从而使列表项无法点击。
解决方法是修改ListView的适配器,通过设置ViewGroup的descendantFocusability属性为FOCUS_BLOCK_DESCENDANTS,来阻止TextView捕捉到触摸事件。这样,列表项就可以正常点击了。
以下是解决方法的示例代码:
private class YourListadapter extends BaseAdapter { public View getView(int position, View convertView, ViewGroup parent) { ((ViewGroup)convertView).setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); return convertView; } }
上述代码将ListView的适配器中的getView方法进行了修改,在获取视图的时候,通过设置convertView的descendantFocusability属性为FOCUS_BLOCK_DESCENDANTS来阻止TextView捕捉到触摸事件。
此外,还可以通过在XML布局文件中设置descendantFocusability属性来解决该问题。在ListView的布局中,设置android:descendantFocusability="blocksDescendants"即可。
通过以上方法,可以解决TextView使用LinkMovementMethod导致列表项不可点击的问题。
问题的原因是当调用setMovementMethod或setKeyListener时,TextView会自动设置为可点击、可长按、可聚焦。第一个问题是当一个View可点击时,它会消耗ACTION_UP事件,即在onTouchEvent方法中返回true。为了解决这个问题,可以在onTouchEvent方法中只在用户实际点击链接时返回true。但是LinkMovementMethod无法告诉我们用户是否实际点击了链接,它在onTouch方法中无论用户点击链接还是其他情况都返回true。为了解决这个问题,可以通过在TextViewFixTouchConsume类中设置一个标志位来判断用户是否实际点击了链接,并在onTouchEvent方法中返回这个标志位。
第三个问题是ListView在performClick方法中只有在ListView的item view没有可聚焦的子元素时才调用onItemClick事件处理程序。为了解决这个问题,需要在添加到ListView中的view中重写hasFocusable方法,将其返回false,或者可以为该view设置setOnClickListener方法。
所以,解决该问题的方法是:创建一个TextViewFixTouchConsume类继承自TextView,在该类中重写onTouchEvent方法和hasFocusable方法,同时创建一个LocalLinkMovementMethod类继承自LinkMovementMethod,并在其中重写onTouchEvent方法。然后,在需要使用这个功能的TextView上调用setMovementMethod方法并传入LocalLinkMovementMethod.getInstance()。
但是需要注意的是,这个解决方法也会带来另一个问题,即长按操作会在点击列表项后立即触发,而不是需要按住一段时间才触发。
此外,还有其他一些问题需要注意,比如在某些设备上可能会抛出异常,解决方法是使用clear版本的Android 5.0,还有可能会出现调用setFocusable(false)无效的情况,因为TextView会自动将其设置为可聚焦。