Modificare il colore del testo di un singolo ClickableSpan quando premuto, senza influenzare le altre ClickableSpans nella stessa TextView

Ho una TextView con più ClickableSpans in esso. Quando un ClickableSpan viene premuto, la voglio cambiare il colore del testo.

Ho provato l’impostazione di un colore nell’elenco stato come textColorLink attributo della TextView. Questo non dà il risultato desiderato, perché questo provoca tutti campate per cambiare colore quando l’utente fa clic ovunque sulla TextView.

È interessante notare che, utilizzando textColorHighlight per modificare il colore di sfondo funziona come previsto: Cliccando su un arco cambia solo il colore di sfondo che si estendono e facendo clic su qualsiasi altra parte del TextView non fa nulla.

Ho anche provato impostazione ForegroundColorSpans, con gli stessi confini come ClickableSpans dove passare lo stesso colore nell’elenco stato come sopra come il colore delle risorse. Questo non funziona. Le campate tenere sempre il colore di default di stato nel colore della lista di stato e non entrare mai lo stato premuto.

Qualcuno sa come fare?

Questo è il colore dello stato la lista che ho usato:

<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>
Sarà necessario utilizzare Spannable oggetto qui per il testo, c’è un esempio. API: developer.android.com/reference/android/text/Spannable.html stackoverflow.com/questions/3282940/….
Sto facendo tutto su un Spannable. E collegato solo esempio viene illustrato come impostare il colore di un arco, che non è qui il problema. Voglio che il colore di un arco di cambiare, quando viene premuto.
afaik ClickableSpan non supporta direttamente
Beh, è un peccato quindi. Sembra una cosa semplice da fare, provenienti da CSS. Io probabilmente basta usare textColorHighlight in questo caso.
per fortuna mi sono sbagliato, ancora di più, è in grado di cambiare non solo il colore del collegamento (updateDrawState), ma anche il colore di sfondo (SpanWatcher)

OriginaleL’autore Steven Meliopoulos | 2013-12-31

7 Replies
  1. 60

    Ho finalmente trovato la soluzione che fa tutto quello che volevo. Esso è basato su questa risposta.

    Questo è il mio modificato LinkMovementMethod che segna un arco di pressione, all’inizio di un evento touch (MotionEvent.ACTION_DOWN) e unmarks quando il tocco termina o quando il tocco posizione si sposta al di fuori dell’intervallo di misura.

    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);
            }
        }

    Questo deve essere applicato per la TextView in questo modo:

        yourTextView.setMovementMethod(new LinkTouchMovementMethod());

    E questa è la modifica ClickableSpan che modifica il sorteggio stato basato sullo stato premuto set da LinkTouchMovementMethod: (rimuove anche la sottolineatura dei link).

    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);
        }
    }
    bello, una cosa: l’impostazione di ds.bgColor a 0xffffffff completamente nasconde il default selecrion (utile quando si utilizza dpad frecce per spostarsi su un link)
    Grazie per il suggerimento. Ho cambiato 0xffeeeeee come nel tuo esempio ora. Perché non fare una differenza? Non che sia completamente trasparente?
    bene, sono copletly opaco dal 0xff******dovrebbe essere qualcosa di simile a 0xaa******
    Ottima soluzione! Per evitare la creazione di innumerevoli LinkTouchMovementMethod oggetti, mi piacerebbe ignorare la statica LinkMovementMethod.getInstance() metodo per restituire una singola istanza in qualsiasi momento.
    Ho seguito questo esempio e ha funzionato molto bene. Tuttavia, il feedback tattili colorati zona fuori centro sul testo. Sembra essere inferiore verticalmente rispetto al centro del testo. Quando ho scelto di mostrare il layout limiti nelle impostazioni di sviluppo, non c’è dialogo intorno a ciascuna campata. Qualcuno sa come questo può essere alterato?

    OriginaleL’autore Steven Meliopoulos

  2. 16

    Soluzione molto più semplice, 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);
        }
    };
    Questo non solo consente di impostare il colore per stato premuto, ma tutti

    OriginaleL’autore zundi

  3. 8

    legr3c risposta mi ha aiutato molto. E vorrei aggiungere un paio di osservazioni.

    Commento #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);

    Ho applicato LinkTouchMovementMethod per un TextView con due campate. Le campate sono state evidenziate con il colore blu quando cliccato su di loro.
    myTextView.setHighlightColor(getResources().getColor(android.R.color.transparent));
    risolto il bug.

    Commento #2.

    Non dimenticare di ottenere i colori da risorse quando si passa normalTextColor, pressedTextColor, e pressedBackgroundColor.

    Dovrebbe passare risolto colore al posto di un id di risorsa qui

    OriginaleL’autore Maksim Dmitriev

  4. 5

    Tutte queste soluzioni sono troppo lavoro.

    Basta impostare android:textColorLink nel TextView per alcuni di selezione. Quindi creare un clickableSpan senza la necessità di eseguire l’override updateDrawState(…). Tutto fatto.

    qui un esempio veloce:

    Nel strings.xml hanno dichiarato stringa come questa:

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

    poi nella vostra attività:

    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);
    }

    e heres le parti importanti ..dichiarare un selettore come questa chiamata è 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>

    E nel TextView in xml fare questo:

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

    Ora si può avere uno stato premuto sul tuo clickableSpan.

    selector colori funzionano bene solo se abbiamo solo 1 link, se ci sono 2 link, link si evidenzia stesso tempo, quando abbiamo premuto

    OriginaleL’autore j2emanue

  5. 2

    provare questa usanza 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);
        }
    }

    e questo 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) {
        }
    }

    test in 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);
    Grazie per la risposta. Ci ho provato, ma non è andata esattamente come mi aspettavo. In particolare, i collegamenti non tornare al loro stato originale, quando sono stati rilasciati o quando il tocco posizione spostato fuori il link, così alla fine sono andato con la versione che ho postato sopra.

    OriginaleL’autore pskink

  6. 2

    Questa è la mia soluzione, se hai molti clic sugli elementi (abbiamo bisogno di un’interfaccia):
    Interfaccia:

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

    La classe che gestisce l’evento:

    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);
      }
    }

    In classe principale:

    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);
      }
    }

    OriginaleL’autore Carlos Gómez

  7. -1

    Posto il codice java come di seguito :

    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;   
    } 
    }

    Nel codice di cui sopra specificare wat colore che si desidera .

    Cambiare lo stile .xml come si desidera.

    <?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>

    Provare e dire ciò che si desidera o qualcos’altro . aggiornamento mi tizio.

    Questo non può cambiare il colore di tutta la TextView?
    sì certo , y ?
    L’obiettivo è quello di cambiare solo il colore del ClickableSpan che viene premuto. Il resto della TextView dovrebbe rimanere invariato.

    OriginaleL’autore kathir

Lascia un commento