我想做什么:包含这样的消息的列表:
<用户名>这是用户写的mnessage,它将很好地包装到下一行。完全是这样的。
我有什么:
ListView R.layout.list_item:
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/text_message"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="(Message Text)" />
扩充上述布局的适配器并执行:
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();
}
问题:
点击该项目,不显示祝酒词。只需点击用户名即可显示祝酒词。
我猜,setMovementMethod(LinkMovementMethod.getInstance());
使TextView可以点击。因此,物品本身永远不会被点击了。
如何让商品再次点击?拥有我想要的相同功能。
答案 0 :(得分:57)
在这种情况下有三个显示停止者。根本原因是当您致电setMovementMethod
或setKeyListener
时,TextView
“修复”了它的设置:
setFocusable(true);
setClickable(true);
setLongClickable(true);
第一个问题是,当一个视图可点击时 - 它总是消耗ACTION_UP
事件(它在onTouchEvent(MotionEvent event)
中返回true)。
要解决这个问题,只有当用户实际点击了URL时,才应该在该方法中返回true。
但如果用户实际点击了某个链接,则LinkMovementMethod
不会告诉我们。如果用户点击链接,它会在onTouch
中返回“true”,但在许多其他情况下也是如此。
所以,实际上我在这里做了一个技巧:
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);
}
}
}
你应该在某个地方打电话
textView.setMovementMethod(
TextViewFixTouchConsume.LocalLinkMovementMethod.getInstance()
);
为textView设置此MovementMethod。
如果用户实际点击链接,此MovementMethod会在TextViewFixTouchConsume
中引发一个标记。
(仅在ACTION_UP
和ACTION_DOWN
事件中),TextViewFixTouchConsume.onTouchEvent
仅在用户实际点击链接时才返回true。
但那不是全部!!!!
第三个问题是ListView
(AbsListView
)仅在performClick
的项目视图无焦点时调用它的onItemClick
方法(调用ListView
事件处理程序) 。
所以,你需要覆盖
@Override
public boolean hasFocusable() {
return false;
}
在您添加到ListView
的视图中。
(在我的情况下,这是一个包含textView的布局)
或者您可以将setOnClickLIstener
用于该视图。
诀窍并不是很好,但它确实有效。
答案 1 :(得分:44)
myTextView.setOnTouchListener(new OnTouchListener() {
@Override
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;
}
});
只需将此侦听器分配给列表适配器getView()方法中的TextView。
答案 2 :(得分:5)
以下是使ListView
项和TextView
UrlSpans
可点击的快速解决方法:
private class YourListadapter extends BaseAdapter {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
((ViewGroup)convertView).setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
return convertView
}
}
答案 3 :(得分:3)
问题在于LinkMovementMethod表示将管理触摸事件,独立地触摸是在Spannable或普通文本中。
这应该有用。
public class HtmlTextView extends TextView {
...
@Override
public boolean onTouchEvent(MotionEvent event) {
if (getMovementMethod() == null ) {
boolean result = super.onTouchEvent(event);
return result;
}
MovementMethod m = getMovementMethod();
setMovementMethod(null);
boolean mt = m.onTouchEvent(this, (Spannable) getText(), event);
if (mt && event.getAction() == MotionEvent.ACTION_DOWN) {
event.setAction(MotionEvent.ACTION_UP);
mt = m.onTouchEvent(this, (Spannable) getText(), event);
event.setAction(MotionEvent.ACTION_DOWN);
}
boolean st = super.onTouchEvent(event);
setMovementMethod(m);
setFocusable(false);
return mt || st;
}
...
}
答案 4 :(得分:2)
发生这种情况是因为当我们按下列表项时,它会将新闻事件发送给其所有子项,因此孩子的 setPressed 调用而不是列表项。因此,要单击列表项,您必须将子项的 setPressed 设置为false。为此,您必须自定义 TextView 类并覆盖所需的方法。这是示例代码
public class DontPressWithParentTextView扩展TextView {
public DontPressWithParentTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void setPressed(boolean pressed) {
// If the parent is pressed, do not set to pressed.
if (pressed && ((View) getParent()).isPressed()) {
return;
}
super.setPressed(pressed);
}
}
答案 5 :(得分:1)
我有另一个解决方案使用带有两个不同文本的列表项布局:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="fill_parent" >
<com.me.test.DontPressWithParentTextView
android:id="@+id/text_user"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:text="(Message Text)" />
<TextView
android:id="@+id/text_message"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:text="(Message Text)" />
,适配器代码为:
DontPressWithParentTextView text1 = (DontPressWithParentTextView) convertView.findViewById(R.id.text_user);
TextView text2 = (TextView) convertView.findViewById(R.id.text_message);
text2.setText(message);
SpannableStringBuilder f = new SpannableStringBuilder();
CharSequence username = names1[position];
f.append(username );
f.setSpan(new InternalURLSpan(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getBaseContext(), "Clicked User", Toast.LENGTH_SHORT).show();
}
}), f.length() - username.length(), f.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
f.append(" ");
text1.setText(f);
text1.setMovementMethod(LinkMovementMethod.getInstance());
text1.setFocusable(false);
这将有效..: - )
答案 6 :(得分:0)
为什么使用ListView.setOnItemClickListener()
?您可以通过messageTextView.setOnClickListener()
在适配器中提供相同的内容。
另一种方法 - 为消息设置第二个可点击范围 - 并在那里提供操作。如果你不希望第二部分看起来像一个linke创建
public class InternalClickableSpan extends ClickableSpan {
OnClickListener mListener;
public InternalClickableSpan(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);
ds.setColor(Color.WHITE);// Here you can provide any color - take it from resources or from theme.
}
}
答案 7 :(得分:0)
对于所有有兴趣用EmojisTextView从@babay在第一个答案中所说的那样做的人,我会做出一些改变:
public class EmojiconTextView extends TextView {
private int mEmojiconSize;
private int mTextStart = 0;
private int mTextLength = -1;
boolean dontConsumeNonUrlClicks = true;
boolean linkHit;
public EmojiconTextView(Context context) {
super(context);
init(null);
}
public EmojiconTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public EmojiconTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs);
}
private void init(AttributeSet attrs) {
if (attrs == null) {
mEmojiconSize = (int) getTextSize();
} else {
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.Emojicon);
mEmojiconSize = (int) a.getDimension(R.styleable.Emojicon_emojiconSize, getTextSize());
mTextStart = a.getInteger(R.styleable.Emojicon_emojiconTextStart, 0);
mTextLength = a.getInteger(R.styleable.Emojicon_emojiconTextLength, -1);
a.recycle();
}
setText(getText());
}
@Override
public void setText(CharSequence text, BufferType type) {
SpannableStringBuilder builder = new SpannableStringBuilder(text);
EmojiconHandler.addEmojis(getContext(), builder, mEmojiconSize, mTextStart, mTextLength);
super.setText(builder, type);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
linkHit = false;
boolean res = super.onTouchEvent(event);
if (dontConsumeNonUrlClicks)
return linkHit;
return res;
}
/**
* Set the size of emojicon in pixels.
*/
public void setEmojiconSize(int pixels) {
mEmojiconSize = pixels;
}
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 EmojiconTextView){
((EmojiconTextView) widget).linkHit = true;
}
return true;
} else {
Selection.removeSelection(buffer);
Touch.onTouchEvent(widget, buffer, event);
return false;
}
}
return Touch.onTouchEvent(widget, buffer, event);
}
}
}
之后你会在这里做同样的事情:yourTextView.setMovementMethod(EmojiconTextView.LocalLinkMovementMethod.getInstance());
答案 8 :(得分:0)
在布局中应用此textview。 https://gist.github.com/amilcar-andrade/e4b76840da1dc92febfc
答案 9 :(得分:0)
您必须将此行放在适配器项父视图android:descendantFocusability="blocksDescendants"
答案 10 :(得分:-1)
<com.gongchang.buyer.widget.TextViewFixTouchConsume xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/wx_comment_friendsname"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/comment_bg_with_grey_selector"
android:lineSpacingExtra="1dp"
android:paddingBottom="1dp"
android:paddingLeft="@dimen/spacing_third"
android:paddingRight="@dimen/spacing_third"
android:paddingTop="1dp"
android:textColor="@color/text_black_2d"
android:textSize="@dimen/content_second" />