我有一个包含多个ClickableSpans的TextView。按下ClickableSpan时,我希望它能改变文本的颜色。
我尝试将颜色状态列表设置为TextView的textColorLink属性。这不会产生预期的结果,因为当用户单击TextView上的任何地方时,这会导致所有跨度改变颜色。
有趣的是,使用textColorHighlight更改背景颜色可以按预期工作:单击某个范围只会更改该范围的背景颜色,并且单击TextView中的任何其他位置都不会执行任何操作。
我也尝试使用与ClickableSpans相同的边界设置ForegroundColorSpans,在那里我传递与上面相同的颜色状态列表作为颜色资源。这也不起作用。跨度始终保持颜色状态列表中默认状态的颜色,并且永远不会进入按下状态。
有谁知道怎么做?
这是我使用的颜色状态列表:
<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>
答案 0 :(得分:60)
我终于找到了一个可以完成我想要的一切的解决方案。它基于this answer。
这是我修改过的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);
}
}
答案 1 :(得分:16)
更简单的解决方案,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);
}
};
答案 2 :(得分:7)
legr3c's answer帮了我很多忙。我想补充一些评论。
备注#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));
修复了错误。
备注#2。
在通过normalTextColor
,pressedTextColor
和pressedBackgroundColor
时,不要忘记从资源中获取颜色。
答案 3 :(得分:4)
所有这些解决方案都是太多的工作。
只需将android:textColorLink
中的TextView
设置为某个选择器即可。然后创建一个clickableSpan,无需覆盖updateDrawState(...)。一切都完成了。
这是一个简单的例子:
在你的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上按下状态。
答案 4 :(得分:2)
尝试此自定义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中测试:
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);
答案 5 :(得分:2)
如果您有许多点击元素(我们需要一个界面),这是我的解决方案: 界面:
public interface IClickSpannableListener{
void onClickSpannText(String text,int starts,int ends);
}
管理活动的班级:
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);
}
}
答案 6 :(得分:0)
上面的Steven M的答案很有效,但我想分享一下: https://github.com/saket/Better-Link-Movement-Method
就可以了。很高兴不必拥有一堆复杂的代码。
答案 7 :(得分:-1)
将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>
尝试一下,说这是你想要的还是别的什么。让我更新。