¿Cómo puedo cambiar el color de una parte de un TextView?

text = text + CepVizyon.getPhoneCode() + "\n\n" + getText(R.string.currentversion) + CepVizyon.getLicenseText(); activationText.setText(text); myTextView.setText(text); 

Quiero cambiar el color de la CepVizyon.getPhoneCode() de CepVizyon.getPhoneCode() . ¿Cómo puedo hacer esto?

Spannable es más flexible:

 String text2 = text + CepVizyon.getPhoneCode() + "\n\n" + getText(R.string.currentversion) + CepVizyon.getLicenseText(); Spannable spannable = new SpannableString(text2); spannable.setSpan(new ForegroundColorSpan(Color.WHITE), text.length(), (text + CepVizyon.getPhoneCode()).length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); myTextView.setText(spannable, TextView.BufferType.SPANNABLE); 
 myTextView.setText(Html.fromHtml(text + "" + CepVizyon.getPhoneCode() + "

" + getText(R.string.currentversion) + CepVizyon.getLicenseText()));

Si tiene texto estático que necesita color, puede agregarlo sin ningún código a través del archivo de cadenas:

 Already have an account? Login 

entonces

  

resultado

enter image description here

no estoy seguro de en qué versiones de API funciona, pero no funciona para api 19 que he probado hasta ahora, por lo que probablemente solo algunas de las versiones de API más recientes admitan esto

editar: como @hairraisin mencionó en los comentarios, intente usar fgcolor lugar de color para el color de la fuente, luego debería funcionar para niveles de API más bajos, pero necesita más pruebas para estar seguro

Con respecto a la respuesta de Maneesh, esto funcionará, pero debes agregar y escapar las comillas para el atributo de color.

 myTextView.setText(Html.fromHtml(text + "" + CepVizyon.getPhoneCode() + "

" + getText(R.string.currentversion) + CepVizyon.getLicenseText()));

¡Es bueno para mi!

  Spannable spannable = new SpannableString("ABC In-Network DEF"); String str = spannable.toString(); iStart = str.indexOf("In-Network"); iEnd = iStart + 10;/*10 characters = in-network. */ SpannableString ssText = new SpannableString(spannable); ClickableSpan clickableSpan = new ClickableSpan() { @Override public void onClick(View widget) { //your code at here. } @Override public void updateDrawState(TextPaint ds) { super.updateDrawState(ds); ds.setUnderlineText(true); ds.setColor(getResources().getColor(R.color.green)); } }; ssText.setSpan(clickableSpan, iStart, iEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); mTextView.setText(ssText); mTextView.setMovementMethod(LinkMovementMethod.getInstance()); mTextView.setHighlightColor(Color.TRANSPARENT); mTextView.setEnabled(true); 

Aquí hay una función de colorize basado en la respuesta de andyboot:

  /** * Colorize a specific substring in a string for TextView. Use it like this: 
 * textView.setText( * Strings.colorized("The some words are black some are the default.","black", Color.BLACK), * TextView.BufferType.SPANNABLE * ); * 

* @param text Text that contains a substring to colorize * @param word The substring to colorize * @param argb The color * @return the Spannable for TextView's consumption */ public static Spannable colorized(final String text, final String word, final int argb) { final Spannable spannable = new SpannableString(text); int substringStart=0; int start; while((start=text.indexOf(word,substringStart))>=0){ spannable.setSpan( new ForegroundColorSpan(argb),start,start+word.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE ); substringStart = start+word.length(); } return spannable; }

No me gusta la idea de hacer esto por código cada vez que quiero colorear partes del texto que he estado haciendo mucho en todas mis aplicaciones (y dado que en algunos casos el texto se está configurando en tiempo de ejecución con diferentes archivos en línea). colores definidos) así que creé mi propio MarkableTextView .

La idea era:

  • Detecta tags XML de una cadena
  • Identificar e identificar el nombre de la etiqueta
  • Extrae y guarda atributos y posición del texto
  • Eliminar la etiqueta y mantener el contenido
  • Itera a través de atributos y aplica estilos

Este es el proceso paso a paso:

Primero necesité una forma de encontrar tags XML en una cadena dada y Regex hizo el truco.

 < ([a-zA-Z]+(?:-[a-zA-Z0-9]+)*)(?:\s+([^>]*))?>([^>][^< ]*) 

Para que lo anterior coincida con una etiqueta XML, debe tener los siguientes criterios:

  • Nombre de etiqueta válido como pero no <1>
  • Etiqueta de cierre que tiene un nombre coincidente como pero no
  • Cualquier contenido, ya que no hay necesidad de estilo “nada”

Ahora para los atributos vamos a usar este …

 ([a-zA-Z]+)\s*=\s*(['"])\s*([^'"]+?)\s*\2 

Tiene el mismo concepto y, en general, no necesito ir muy lejos para ambos, ya que el comstackdor se encargará del rest si algo se sale del formato.

Ahora necesitamos una clase que pueda contener los datos extraídos:

 public class MarkableSheet { private String attributes; private String content; private int outset; private int ending; private int offset; private int contentLength; public MarkableSheet(String attributes, String content, int outset, int ending, int offset, int contentLength) { this.attributes = attributes; this.content = content; this.outset = outset; this.ending = ending; this.offset = offset; this.contentLength = contentLength; } public String getAttributes() { return attributes; } public String getContent() { return content; } public int getOutset() { return outset; } public int getContentLength() { return contentLength; } public int getEnding() { return ending; } public int getOffset() { return offset; } } 

Antes que nada, vamos a agregar este genial iterador que he estado utilizando durante mucho tiempo para recorrer las coincidencias (no puedo recordar al autor) :

 public static Iterable matches(final Pattern p, final CharSequence input) { return new Iterable() { public Iterator iterator() { return new Iterator() { // Use a matcher internally. final Matcher matcher = p.matcher(input); // Keep a match around that supports any interleaving of hasNext/next calls. MatchResult pending; public boolean hasNext() { // Lazily fill pending, and avoid calling find() multiple times if the // clients call hasNext() repeatedly before sampling via next(). if (pending == null && matcher.find()) { pending = matcher.toMatchResult(); } return pending != null; } public MatchResult next() { // Fill pending if necessary (as when clients call next() without // checking hasNext()), throw if not possible. if (!hasNext()) { throw new NoSuchElementException(); } // Consume pending so next call to hasNext() does a find(). MatchResult next = pending; pending = null; return next; } /** Required to satisfy the interface, but unsupported. */ public void remove() { throw new UnsupportedOperationException(); } }; } }; } 

MarkableTextView:

 public class MarkableTextView extends AppCompatTextView { public MarkableTextView(Context context) { super(context); } public MarkableTextView(Context context, AttributeSet attrs) { super(context, attrs); } public MarkableTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public void setText(CharSequence text, BufferType type) { // Intercept and process text text = prepareText(text.toString()); super.setText(text, type); } public Spannable Markable; private Spannable prepareText(String text) { String parcel = text; Multimap markableSheets = ArrayListMultimap.create(); // Used to correct content position after tossing tags int totalOffset = 0; // Iterate through text for (MatchResult match : matches(Markable.Patterns.XML, parcel)) { // Get tag name String tag = match.group(1); // Match with a defined tag name "case-sensitive" if (!tag.equals(Markable.Tags.MARKABLE)) { // Break if no match break; } // Extract data String attributes = match.group(2); String content = match.group(3); int outset = match.start(0); int ending = match.end(0); int offset = totalOffset; // offset=0 since no preceded changes happened int contentLength = match.group(3).length(); // Calculate offset for the next element totalOffset = (ending - outset) - contentLength; // Add to markable sheets MarkableSheet sheet = new MarkableSheet(attributes, content, outset, ending, offset, contentLength); markableSheets.put(tag, sheet); // Toss the tag and keep content Matcher reMatcher = Markable.Patterns.XML.matcher(parcel); parcel = reMatcher.replaceFirst(content); } // Initialize spannable with the modified text Markable = new SpannableString(parcel); // Iterate through markable sheets for (MarkableSheet sheet : markableSheets.values()) { // Iterate through attributes for (MatchResult match : matches(Markable.Patterns.ATTRIBUTES, sheet.getAttributes())) { String attribute = match.group(1); String value = match.group(3); // Apply styles stylate(attribute, value, sheet.getOutset(), sheet.getOffset(), sheet.getContentLength()); } } return Markable; } 

Finalmente, el estilo, así que aquí hay un moldeador muy simple que hice para esta respuesta:

 public void stylate(String attribute, String value, int outset, int offset, int length) { // Correct position outset -= offset; length += outset; if (attribute.equals(Markable.Tags.TEXT_STYLE)) { if (value.contains(Markable.Tags.BOLD) && value.contains(Markable.Tags.ITALIC)) { Markable.setSpan( new StyleSpan(Typeface.BOLD_ITALIC), outset, length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } else if (value.contains(Markable.Tags.BOLD)) { Markable.setSpan( new StyleSpan(Typeface.BOLD), outset, length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } else if (value.contains(Markable.Tags.ITALIC)) { Markable.setSpan( new StyleSpan(Typeface.ITALIC), outset, length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } if (value.contains(Markable.Tags.UNDERLINE)) { Markable.setSpan( new UnderlineSpan(), outset, length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } } if (attribute.equals(Markable.Tags.TEXT_COLOR)) { if (value.equals(Markable.Tags.ATTENTION)) { Markable.setSpan( new ForegroundColorSpan(ContextCompat.getColor( getContext(), R.color.colorAttention)), outset, length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } else if (value.equals(Markable.Tags.INTERACTION)) { Markable.setSpan( new ForegroundColorSpan(ContextCompat.getColor( getContext(), R.color.colorInteraction)), outset, length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } } } 

Y así es como se Markable clase Markable contiene las definiciones:

 public class Markable { public static class Patterns { public static final Pattern XML = Pattern.compile("< ([a-zA-Z]+(?:-[a-zA-Z0-9]+)*)(?:\\s+([^>]*))?>([^>][^< ]*)"); public static final Pattern ATTRIBUTES = Pattern.compile("(\\S+)\\s*=\\s*(['\"])\\s*(.+?)\\s*\\2"); } public static class Tags { public static final String MARKABLE = "markable"; public static final String TEXT_STYLE = "textStyle"; public static final String BOLD = "bold"; public static final String ITALIC = "italic"; public static final String UNDERLINE = "underline"; public static final String TEXT_COLOR = "textColor"; public static final String ATTENTION = "attention"; public static final String INTERACTION = "interaction"; } } 

Todo lo que necesitamos ahora es hacer referencia a una cadena y, básicamente, debería verse así:

  < ![CDATA[Hello world!]]>  

Asegúrese de envolver las tags con una CDATA Section y escapar " con \ .

Hice esto como una solución modular para procesar partes del texto de diferentes maneras sin la necesidad de rellenar código innecesario.

Lo hice como dijo Andy boot, pero también tuve un lapso de setSpans , y no funcionó porque el orden en que se llamaba a setSpans . Así que primero debe llamar al spannable.setSpan(clickableSpanand... luego el spannable.setSpan(new ForegroundColorSpan... para obtener el color en el TextView

Una forma es dividir myTextView en pocos TextViews separados, uno de los cuales sería solo para el código del teléfono. Entonces controlar el color de este TextView específico es bastante directo.