转载请标明出处 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分,以后分多了,我会改成免收的,请理解。