转载请标明出处 http://blog.csdn.net/xxxiu/article/details/40736057
有个场景: 一段文本,我们想让其中几处成为链接,点击后对应去处理。比如微信的朋友圈中对动态的评论。如图
点人名看资料,点整个弹出软键盘评论。
部分代码:
布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <com.example.androiddemo.commtrls.TextViewSpannableString android:id="@+id/tv" android:background="@drawable/textview_bg_selector" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textSize="20sp" /> </LinearLayout>
Activity引用代码
/** * 该Demo主要解决的应用场景是: 类似微信朋友圈里, A回复B:xxxx * 点击A或者B看个人资料,点击整个弹出评论。其实关键点是,如何区别这两类点击。因为A与B点击可以看为一类。 * 最终处理在TextUnderlineClickSpan的onClick()中,可以从结果追整个流程。 * * 效果查看: * 点击AB看LOG输出,点整个看Toast * @author xiaoXIU * */ public class MainActivity extends Activity { private TextViewSpannableString mTv; private Context mContext; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mContext = this; //测试数据 CarCommend commend = new CarCommend(); commend.setUserId(1); commend.setUserName("小修"); commend.setTargetuserId(2); commend.setTargetuserName("大宝"); commend.setContent("老婆,我爱你。"); mTv = (TextViewSpannableString) findViewById(R.id.tv); mTv.updateUI(commend); mTv.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Toast.makeText(mContext, "打开键盘,回复评论", Toast.LENGTH_SHORT).show(); } }); } }
自定义TextView 该类里有对内容中存在Emoji表情的处理,但是Demo程序没有做的那么复杂,所以想实现的可以打开注释部分自己再修改下。我的项目中是做处理的。
/** * 内容设置,发送消息给链接处理类处理 * @author xiaoXiu * */ public class TextViewSpannableString extends TextView { private Context mContext; private boolean mDontConsumeNonUrlClicks = true; private boolean mLinkHit; public TextViewSpannableString(Context context) { super(context); init(context); } public TextViewSpannableString(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public TextViewSpannableString(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context); } private void init(Context context) { mContext = context; } @Override public boolean hasFocusable() { return false; } @Override public boolean performClick() { if (mDontConsumeNonUrlClicks) { return super.performClick(); } return false; } @Override public boolean onTouchEvent(MotionEvent event) { mLinkHit = false; boolean res = super.onTouchEvent(event); if (!mDontConsumeNonUrlClicks) {// 没有ClickSpan被点击是,处理OnClick事件。 return mLinkHit; } return res; } public void updateUI(CarCommend bean) { setText(""); setTextViewSpan(this, bean.getUserName(), bean.getUserId()); if (bean.getUserId() != bean.getTargetuserId() && bean.getTargetuserId() != 0 && !TextUtils.isEmpty(bean.getTargetuserName())) { append("回复"); setTextViewSpan(this, bean.getTargetuserName(), bean.getTargetuserId()); } append(":"); setContent(bean); } public void updateUIFeedDetails(CarCommend bean) { setText(""); append(""); if (bean.getUserId() != bean.getTargetuserId() && bean.getTargetuserId() != 0 && !TextUtils.isEmpty(bean.getTargetuserName())) { append("回复"); setTextViewSpan(this, bean.getTargetuserName(), bean.getTargetuserId()); append(":"); } setContent(bean); } private void setContent(CarCommend bean) { //这是为了Demo的显示,不带表情 SpannableString sp = new SpannableString(bean.getContent()); Editable editable = getEditableText(); if (editable != null) { editable.insert(length(), sp); } //注释部分解决了带表情的内容处理 /*for (TextEmoji e : bean.getCommendBody()) { if (TextEmoji.TEXT_TYPE.equals(e.getType()) || e.getEmojiId() == -1) { append(e.getContent()); } else { SpannableString sp = getEmojiSpan(e.getEmojiId(), e.getContent()); if (!TextUtils.isEmpty(sp)) { Editable editable = getEditableText(); if (editable != null) { editable.insert(length(), sp); } } } }*/ } private void setTextViewSpan(TextView textView, String text, int tag) { Message message = new Message(); message.what = TextUnderlineClickSpan.FLAG_GO_OTHERS_INFO_ACTIVITY; message.arg1 = tag; SpannableString spStr = new SpannableString(text); ClickableSpan clickSpan = new TextUnderlineClickSpan(mContext, false, message); // 设置超链接 spStr.setSpan(clickSpan, 0, text.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); textView.append(spStr); setMovementMethod(LocalLinkMovementMethod.getInstance()); } 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 TextViewSpannableString) { ((TextViewSpannableString) widget).mDontConsumeNonUrlClicks = false; ((TextViewSpannableString) widget).mLinkHit = true; } // Selection.removeSelection(buffer);// 去除选择背景 return true; } else { if (widget instanceof TextViewSpannableString) { ((TextViewSpannableString) widget).mDontConsumeNonUrlClicks = true; } Selection.removeSelection(buffer); return Touch.onTouchEvent(widget, buffer, event); } } return Touch.onTouchEvent(widget, buffer, event); } } private SpannableString getEmojiSpan(int imgId, String spannableString) { if (TextUtils.isEmpty(spannableString) || imgId == -1) { return null; } ImageSpan imageSpan = new ImageSpan(mContext, FaceConversionUtil.getInstace().getEmojiBitmap(mContext, imgId)); SpannableString spannable = new SpannableString(spannableString); spannable.setSpan(imageSpan, 0, spannableString.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); return spannable; } public void parseUrl(CharSequence text) { if (text instanceof Spannable) { Factory spannableFactory = Spannable.Factory.getInstance(); Spannable sp = spannableFactory.newSpannable(text); int end = text.length(); URLSpan[] urls = sp.getSpans(0, end, URLSpan.class); SpannableStringBuilder style = new SpannableStringBuilder(text); style.clearSpans(); for (URLSpan url : urls) { int spStart = sp.getSpanStart(url); int spEnd = sp.getSpanEnd(url); Message message = new Message(); message.what = TextUnderlineClickSpan.FLAG_URL_CONCLUDE; Bundle bundle = new Bundle(); bundle.putString("title", sp.subSequence(spStart, spEnd).toString()); bundle.putString("content", url.getURL()); message.obj = bundle; ClickableSpan clickSpan = new TextUnderlineClickSpan(mContext, false, message); style.setSpan(clickSpan, spStart, spEnd, Spannable.SPAN_INCLUSIVE_EXCLUSIVE); } setText(style); } else { setText(text); } } }
具体处理控制部分
/** * 用于控制链接执行动作 * 主要方法 onClick() * @author xiaoXiu * */ public class TextUnderlineClickSpan extends ClickableSpan { public static final int FLAG_GO_OTHERS_INFO_ACTIVITY = 1; public static final int FLAG_URL_CONCLUDE = 2; public static final int FLAG_FEED_TO_TOPIC = 3; private Context mContext; private boolean isShowUnderline = false; private int mTextHexColor = 0xff7f93bd; private Message mMessage; public TextUnderlineClickSpan(Context context, boolean showUnderline, Message message) { super(); this.mContext = context; this.mMessage = message; } public void setTextColor(int hexColor) { mTextHexColor = hexColor; } @Override public void onClick(View widget) { if (mMessage == null) { return; } switch (mMessage.what) { case FLAG_GO_OTHERS_INFO_ACTIVITY: //看个人资料 if(mMessage.arg1==1){ Log.e("", "看小修资料"); }else if(mMessage.arg1==2){ Log.e("", "看大宝资料"); } break; case FLAG_URL_CONCLUDE: //链接 Bundle bundle = (Bundle) mMessage.obj; externalControls(bundle); break; case FLAG_FEED_TO_TOPIC: //进入帖子 break; } } private void externalControls(Bundle bundle) { Intent intent = new Intent(); intent.setAction("android.intent.action.VIEW"); Uri uri = Uri.parse(bundle.getString("content").trim()); intent.setData(uri); mContext.startActivity(intent); } @Override public void updateDrawState(TextPaint ds) { ds.setColor(mContext.getResources().getColor(R.drawable.textview_typelink_selector)); ds.setUnderlineText(isShowUnderline); // 去掉下划线 } }
根据上面的方式就解决了该问题,这个Demo唯一的缺陷就是点击人名的时候整个TextView的Selector都生效了。
源码下载地址:SourceCode
因为本人积分就剩了1个了,所以资源分要1分,以后分多了,我会改成免收的,请理解。