複数のClickableSpansを含むTextViewがあります。 ClickableSpanが押されたときに、テキストの色を変更する必要があります。
TextViewのtextColorLink属性としてカラー状態リストを設定しようとしました。これにより、ユーザーがTextViewでanywhereをクリックしたときにallスパンの色が変わるため、望ましい結果が得られません。
興味深いことに、textColorHighlightを使用して背景色を変更すると、期待どおりに機能します。スパンをクリックしても、そのスパンの背景色のみが変更され、TextViewの他の場所をクリックしても何も起こりません。
また、ForegroundColorSpansをClickableSpansと同じ境界に設定してみました。ここでは、色リソースとして上記と同じ色状態リストを渡します。これも機能しません。スパンは常に色状態リストのデフォルト状態の色を保持し、押された状態には決して入りません。
誰もこれを行う方法を知っていますか?
これは私が使用した色状態リストです。
<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>
私は最終的に、私が望んでいたすべてを行うソリューションを見つけました。 この答え に基づいています。
これは、修正された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());
そして、これはLinkTouchMovementMethodによって設定された押された状態に基づいて描画状態を編集する修正されたClickableSpanです:(リンクから下線も削除します)
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);
}
}
よりシンプルなソリューション、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);
}
};
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
を2つのスパンを持つTextView
に適用しました。スパンをクリックすると、スパンが青で強調表示されました。 myTextView.setHighlightColor(getResources().getColor(Android.R.color.transparent));
はバグを修正しました。
備考2。
normalTextColor
、pressedTextColor
、およびpressedBackgroundColor
を渡すときに、リソースから色を取得することを忘れないでください。
これらの解決策はすべて、大変な作業です。
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>
最後に、xmlのTextView
で次のようにします。
<TextView
Android:id="@+id/mytextview"
Android:background="@Android:color/transparent"
Android:text="@string/mystring"
Android:textColorLink="@drawable/myselector" />
これで、clickableSpanで押された状態を持つことができます。
このカスタム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);
これは、多くのクリック要素を取得した場合の私のソリューションです(インターフェイスが必要です):インターフェイス:
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);
}
}