我有一个带有多个ClickableSpans的TextView.当按下ClickableSpan时,我希望它改变其文本的颜色.
我已经尝试将颜色状态列表设置为TextView的textColorLink属性.这不会产生所需的结果,因为当用户单击TextView上的任何位置时,这会导致所有跨度改变颜色.
有趣的是,使用textColorHighlight更改背景颜色按预期工作:单击跨度仅更改该跨度的背景颜色,并单击TextView中的任何其他位置不执行任何操作.
我也尝试使用与ClickableSpans相同的边界设置ForegroundColorSpans,我在其中传递与上面相同的颜色状态列表作为颜色资源.这也不起作用.跨度始终保持颜色状态列表中默认状态的颜色,并且永远不会进入按下状态.
有谁知道如何做到这一点?
这是我使用的颜色状态列表:
Steven Melio.. 62
我终于找到了一个解决方案来完成我想要的一切.它基于这个答案.
这是我修改过的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); } }
这需要应用于TextView,如下所示:
yourTextView.setMovementMethod(new LinkTouchMovementMethod());
这是修改后的ClickableSpan,它根据LinkTouchMovementMethod设置的按下状态编辑绘制状态:(它还从链接中删除下划线)
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); } }
好方案!为了避免创建无数的`LinkTouchMovementMethod`对象,我会覆盖静态的`LinkMovementMethod.getInstance()`方法,以便随时返回单个实例. (5认同)
zundi.. 22
更简单的解决方案,IMO:
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); } };
这不仅设置了按下状态的颜色,还设置了所有 (3认同)
Maksim Dmitr.. 8
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.
不要忘了路过的时候,从资源获取的颜色normalTextColor
,pressedTextColor
以及pressedBackgroundColor
.
应该在这里传递已解析的颜色而不是资源ID
所有这些解决方案都需要太多工作。
只需android:textColorLink
在您的TextView
选择器中设置即可。然后创建一个无需覆盖updateDrawState(...)的clickableSpan。全部做完。
这里有个简单的例子:
在你strings.xml
有一个这样的声明的字符串:
<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); }
这里是重要的部分..声明一个选择器,如下所示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
的xml中执行以下操作:
<TextView android:id="@+id/mytextview" android:background="@android:color/transparent" android:text="@string/mystring" android:textColorLink="@drawable/myselector" />
现在,您可以在clickableSpan上具有按下状态。
我终于找到了一个解决方案来完成我想要的一切.它基于这个答案.
这是我修改过的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); } }
这需要应用于TextView,如下所示:
yourTextView.setMovementMethod(new LinkTouchMovementMethod());
这是修改后的ClickableSpan,它根据LinkTouchMovementMethod设置的按下状态编辑绘制状态:(它还从链接中删除下划线)
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.
不要忘了路过的时候,从资源获取的颜色normalTextColor
,pressedTextColor
以及pressedBackgroundColor
.
应该在这里传递已解析的颜色而不是资源ID
更简单的解决方案,IMO:
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); } };