按下时更改单个ClickableSpan的文本颜色,而不会影响同一TextView中的其他ClickableSpans

我有一个TextView与多个ClickableSpans在其中。 当按下ClickableSpan时,我希望它改变文本的颜色。

我曾尝试设置一个颜色状态列表作为TextView的textColorLink属性。 这不会产生所需的结果,因为当用户单击TextView上的任何位置时,这会导致所有跨度更改颜色。

有趣的是,使用textColorHighlight来改变背景颜色的工作原理如下:点击一个跨度只改变该跨度的背景颜色,并在TextView的其他任何地方单击不做任何事情。

我也尝试设置与ClickableSpans具有相同边界的ForegroundColorSpans,其中我将如上所述的相同颜色状态列表作为颜色资源。 这也不pipe用。 跨度始终保持颜色状态列表中的默认状态的颜色,从不进入按下的状态。

有谁知道如何做到这一点?

这是我使用的颜色状态列表:

<selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" android:color="@color/pressed_color"/> <item android:color="@color/normal_color"/> </selector> 

我终于find了一个解决scheme,做我想要的一切。 这是基于这个答案 。

这是我修改后的LinkMovementMethod,它在触摸事件(MotionEvent.ACTION_DOWN)开始时标记一个跨度,并在触摸结束时或当触摸位置移出跨度时取消标记。

 public class LinkTouchMovementMethod extends LinkMovementMethod { private TouchableSpan mPressedSpan; @Override public boolean onTouchEvent(TextView textView, Spannable spannable, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { mPressedSpan = getPressedSpan(textView, spannable, event); if (mPressedSpan != null) { mPressedSpan.setPressed(true); Selection.setSelection(spannable, spannable.getSpanStart(mPressedSpan), spannable.getSpanEnd(mPressedSpan)); } } else if (event.getAction() == MotionEvent.ACTION_MOVE) { TouchableSpan touchedSpan = getPressedSpan(textView, spannable, event); if (mPressedSpan != null && touchedSpan != mPressedSpan) { mPressedSpan.setPressed(false); mPressedSpan = null; Selection.removeSelection(spannable); } } else { if (mPressedSpan != null) { mPressedSpan.setPressed(false); super.onTouchEvent(textView, spannable, event); } mPressedSpan = null; Selection.removeSelection(spannable); } return true; } private TouchableSpan getPressedSpan( TextView textView, Spannable spannable, MotionEvent event) { int x = (int) event.getX() - textView.getTotalPaddingLeft() + textView.getScrollX(); int y = (int) event.getY() - textView.getTotalPaddingTop() + textView.getScrollY(); Layout layout = textView.getLayout(); int position = layout.getOffsetForHorizontal(layout.getLineForVertical(y), x); TouchableSpan[] link = spannable.getSpans(position, position, TouchableSpan.class); TouchableSpan touchedSpan = null; if (link.length > 0 && positionWithinTag(position, spannable, link[0])) { touchedSpan = link[0]; } return touchedSpan; } private boolean positionWithinTag(int position, Spannable spannable, Object tag) { return position >= spannable.getSpanStart(tag) && position <= spannable.getSpanEnd(tag); } } This needs to be applied to the TextView like so: yourTextView.setMovementMethod(new LinkTouchMovementMethod()); And this is the modified ClickableSpan that edits the draw state based on the pressed state set by the LinkTouchMovementMethod: (it also removes the underline from the links) public abstract class TouchableSpan extends ClickableSpan { private boolean mIsPressed; private int mPressedBackgroundColor; private int mNormalTextColor; private int mPressedTextColor; public TouchableSpan(int normalTextColor, int pressedTextColor, int pressedBackgroundColor) { mNormalTextColor = normalTextColor; mPressedTextColor = pressedTextColor; mPressedBackgroundColor = pressedBackgroundColor; } public void setPressed(boolean isSelected) { mIsPressed = isSelected; } @Override public void updateDrawState(TextPaint ds) { super.updateDrawState(ds); ds.setColor(mIsPressed ? mPressedTextColor : mNormalTextColor); ds.bgColor = mIsPressed ? mPressedBackgroundColor : 0xffeeeeee; ds.setUnderlineText(false); } } 

legr3c的回答帮了我很多。 我想补充几点意见。

备注#1。

 TextView myTextView = (TextView) findViewById(R.id.my_textview); myTextView.setMovementMethod(new LinkTouchMovementMethod()); myTextView.setHighlightColor(getResources().getColor(android.R.color.transparent)); SpannableString mySpannable = new SpannableString(text); mySpannable.setSpan(new TouchableSpan(), 0, 7, 0); mySpannable.setSpan(new TouchableSpan(), 15, 18, 0); myTextView.setText(mySpannable, BufferType.SPANNABLE); 

我将LinkTouchMovementMethod应用于两个跨度的TextView 。 单击时用蓝色突出显示跨度。 myTextView.setHighlightColor(getResources().getColor(android.R.color.transparent)); 修复了这个bug。

备注#2。

当传递normalTextColorpressedTextColorpressedBackgroundColor时,不要忘记从资源中获取颜色。

应该在这里通过parsing的颜色,而不是资源ID

IMO更简单的解决scheme:

 final int colorForThisClickableSpan = Color.RED; //Set your own conditional logic here. final ClickableSpan link = new ClickableSpan() { @Override public void onClick(final View view) { //Do something here! } @Override public void updateDrawState(TextPaint ds) { super.updateDrawState(ds); ds.setColor(colorForThisClickableSpan); } }; 

试试这个自定义的ClickableSpan:

 class MyClickableSpan extends ClickableSpan { private String action; private int fg; private int bg; private boolean selected; public MyClickableSpan(String action, int fg, int bg) { this.action = action; this.fg = fg; this.bg = bg; } @Override public void onClick(View widget) { Log.d(TAG, "onClick " + action); } @Override public void updateDrawState(TextPaint ds) { ds.linkColor = selected? fg : 0xffeeeeee; super.updateDrawState(ds); } } 

和这个SpanWatcher:

 class Watcher implements SpanWatcher { private TextView tv; private MyClickableSpan selectedSpan = null; public Watcher(TextView tv) { this.tv = tv; } private void changeColor(Spannable text, Object what, int start, int end) { // Log.d(TAG, "changeFgColor " + what); if (what == Selection.SELECTION_END) { MyClickableSpan[] spans = text.getSpans(start, end, MyClickableSpan.class); if (spans != null) { tv.setHighlightColor(spans[0].bg); if (selectedSpan != null) { selectedSpan.selected = false; } selectedSpan = spans[0]; selectedSpan.selected = true; } } } @Override public void onSpanAdded(Spannable text, Object what, int start, int end) { changeColor(text, what, start, end); } @Override public void onSpanChanged(Spannable text, Object what, int ostart, int oend, int nstart, int nend) { changeColor(text, what, nstart, nend); } @Override public void onSpanRemoved(Spannable text, Object what, int start, int end) { } } 

在onCreate中testing它:

  TextView tv = new TextView(this); tv.setTextSize(40); tv.setMovementMethod(LinkMovementMethod.getInstance()); SpannableStringBuilder b = new SpannableStringBuilder(); b.setSpan(new Watcher(tv), 0, 0, Spanned.SPAN_INCLUSIVE_INCLUSIVE); b.append("this is "); int start = b.length(); MyClickableSpan link = new MyClickableSpan("link0 action", 0xffff0000, 0x88ff0000); b.append("link 0"); b.setSpan(link, start, b.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); b.append("\nthis is "); start = b.length(); b.append("link 1"); link = new MyClickableSpan("link1 action", 0xff00ff00, 0x8800ff00); b.setSpan(link, start, b.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); b.append("\nthis is "); start = b.length(); b.append("link 2"); link = new MyClickableSpan("link2 action", 0xff0000ff, 0x880000ff); b.setSpan(link, start, b.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); tv.setText(b); setContentView(tv); 

这是我的解决scheme,如果你有很多点击元素(我们需要一个接口):接口:

 public interface IClickSpannableListener{ void onClickSpannText(String text,int starts,int ends); } 

pipe理事件的class级:

 public class SpecialClickableSpan extends ClickableSpan{ private IClickSpannableListener listener; private String text; private int starts, ends; public SpecialClickableSpan(String text,IClickSpannableListener who,int starts, int ends){ super(); this.text = text; this.starts=starts; this.ends=ends; listener = who; } @Override public void onClick(View widget) { listener.onClickSpannText(text,starts,ends); } } 

主要课程:

 class Main extends Activity implements IClickSpannableListener{ //Global SpannableString _spannableString; Object _backGroundColorSpan=new BackgroundColorSpan(Color.BLUE); private void setTextViewSpannable(){ _spannableString= new SpannableString("You can click «here» or click «in this position»"); _spannableString.setSpan(new SpecialClickableSpan("here",this,15,18),15,19, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); _spannableString.setSpan(new SpecialClickableSpan("in this position",this,70,86),70,86, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); TextView tv = (TextView)findViewBy(R.id.textView1); tv.setMovementMethod(LinkMovementMethod.getInstance()); tv.setText(spannableString); } @Override public void onClickSpannText(String text, int inicio, int fin) { System.out.println("click on "+ text); _spannableString.removeSpan(_backGroundColorSpan); _spannableString.setSpan(_backGroundColorSpan, inicio, fin, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); ((TextView)findViewById(R.id.textView1)).setText(_spannableString); } } 

所有这些解决scheme都是太多的工作。

只需在您的TextView中将android:textColorLink设置为某个select器即可。 然后创build一个clickableSpan,不需要重写updateDrawState(…)。 全做完了。

这里有个简单的例子:

在你的strings.xml有这样一个声明的string:

 <string name="mystring">This is my message%1$s these words are highlighted%2$s and awesome. </string> 

那么在你的活动中:

 private void createMySpan(){ final String token = "#"; String myString = getString(R.string.mystring,token,token); int start = myString.toString().indexOf(token); //we do -1 since we are about to remove the tokens afterwards so it shifts int finish = myString.toString().indexOf(token, start+1)-1; myString = myString.replaceAll(token, ""); //create your spannable final SpannableString spannable = new SpannableString(myString); final ClickableSpan clickableSpan = new ClickableSpan() { @Override public void onClick(final View view) { doSomethingOnClick(); } }; spannable.setSpan(clickableSpan, start, finish, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); mTextView.setMovementMethod(LinkMovementMethod.getInstance()); mTextView.setText(spannable); } 

和inheritance人的重要组成部分..declare这样的select器调用它myselector.xml

 <?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" android:color="@color/gold"/> <item android:color="@color/pink"/> </selector> 

最后在你的TextView中做到这一点:

  <TextView android:id="@+id/mytextview" android:background="@android:color/transparent" android:text="@string/mystring" android:textColorLink="@drawable/myselector" /> 

现在,您可以在clickableSpan上按下状态。

放置java代码如下:

 package com.synamegames.orbs; import android.view.MotionEvent; import android.view.View; import android.widget.TextView; public class CustomTouchListener implements View.OnTouchListener { public boolean onTouch(View view, MotionEvent motionEvent) { switch(motionEvent.getAction()){ case MotionEvent.ACTION_DOWN: ((TextView) view).setTextColor(0x4F4F4F); break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: ((TextView) view).setTextColor(0xCDCDCD); break; } return false; } } 

在上面的代码中指定你想要的颜色。

根据需要更改样式.xml。

 <?xml version="1.0" encoding="utf-8"?> <resources> <style name="MenuFont"> <item name="android:textSize">20sp</item> <item name="android:textColor">#CDCDCD</item> <item name="android:textStyle">normal</item> <item name="android:clickable">true</item> <item name="android:layout_weight">1</item> <item name="android:gravity">left|center</item> <item name="android:paddingLeft">35dp</item> <item name="android:layout_width">175dp</item> <item name="android:layout_height">fill_parent</item> </style> 

试试看,说是你想要的还是别的什么。 更新我哥们。