Измените цвет текста одного ClickableSpan при нажатии, не затрагивая другие Clickablespan в том же TextView
У меня есть TextView с несколькими ClickableSpans в нем. Когда нажимается ClickableSpan, я хочу, чтобы он изменил цвет своего текста.
Я попытался установить список состояний цвета в качестве атрибута textColorLink TextView. Это не дает желаемого результата, потому что это заставляет все промежутки менять цвет, когда пользователь нажимает в любом месте на TextView.
Интересно, что использование 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>
7 ответов:
Я наконец нашел решение, которое делает все, что я хотел. Он основан наэтом ответе .
Это мой модифицированный 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); } }
Гораздо более простое решение, 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кTextViewс двумя пролетами. Пяди были выделены синим цветом при нажатии на них.myTextView.setHighlightColor(getResources().getColor(android.R.color.transparent));Исправлена ошибка.Замечание №2.
Не забывайте получать цвета из ресурсов при прохождении
normalTextColor,pressedTextColor, иpressedBackgroundColor.Должен передать разрешенный цвет вместо идентификатора ресурса здесь
Все эти решения - слишком большая работа.
Просто установите
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 do это:<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);
Это мое решение, если у вас есть много элементов click (нам нужен интерфейс): Интерфейс:
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); } }
Разместите код 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; } }В приведенном выше коде укажите нужный вам цвет wat .
Измените стиль .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>
Comments