¿Cómo auto-ajustar el tamaño del texto en un TextView multi-línea de acuerdo con la vista de las dimensiones máximas?

He estado buscando una manera de ajustar automáticamente un texto dentro de un textview. A través de mi búsqueda he encontrado muchas soluciones como:

  • FontFitTextView
  • AutoResizeTextView
  • AutoScale / Redimensionar

Pero como muchos otros esto no soluciona mi problema. No funcionan como se esperaba cuando utilizamos un TextView con multilínea.

Básicamente mi objetivo es éste:

Fase 1Fase 2Fase 3Fase 4

Como se puede ver, el texto se redimensiona basándose en el ancho, la altura y también presta atención al salto de línea, creando una vista de texto de varias líneas. También ser capaz de cambiar la tipografía.

Una de mis ideas para resolver esto fue algo como esto:

int size = CONSTANT_MAX_SIZE; TextView tv = (TextView) findViewById(R.id.textview1) while(Math.abs(tv.getMeasuredHeight()) >= TEXTVIEW_MAX_HEIGHT) { size--; tv.setTextSize(size); tv.measure(MeasureSpec.UNSPECIFIED,MeasureSpec.UNSPECIFIED); i++; } 

CONSTANT_MAX_SIZE es una constante que define el tamaño máximo de la fuente (textsize propriety en un TextView)

TEXTVIEW_MAX_HEIGHT es una constante que define el tamaño máximo que puede tener la vista de texto.

Esto se llamaba cada vez que se cambiaba el texto en la vista de texto.

El textview xml era algo como esto:

 <TextView android:id="@+id/textview1" android:layout_width="200dp" android:layout_height="wrap_content" android:singleLine="false" android:inputType="textMultiLine" android:text="" android:textSize="150sp" /> 

Dado que el ancho sería limitado en el XML, sólo la altura de la vista debe ser considerada ya que el ajuste de android crearía automáticamente una multilínea cuando sea necesario.

Aunque esta es una solución potencial no funciona perfectamente (lejos de ello) y no admite el cambio de tamaño hacia abajo (cuando elimina texto).

¿Sugerencias y / o ideas?

Mientras esperaba una posible solución a esta pregunta, he estado experimentando y tratando de entenderlo.

La solución más cercana a este problema se basó en un método de pintura.

Básicamente la pintura tiene un método llamado 'breaktext' que:

public int breakText (texto CharSequence, int start, int end, booleano measureForwards, float maxWidth, float [] measuredWidth)

Añadido en API nivel 1

Mida el texto, deteniéndolo temprano si la anchura medida excede maxWidth. Devuelve el número de caracteres que se midieron y, si la anchura medida no es nula, devuelve el ancho real medido.

Combino eso con la pintura 'getTextBounds' que:

public void getTextBounds (Texto de cadena, int start, int end, Rect limits)

Añadido en API nivel 1

Devuelve en límites (asignado por la persona que llama) el rectángulo más pequeño que encierra todos los caracteres, con un origen implícito en (0,0).

Así que ahora puedo obtener el número de caracteres que se ajustan en un ancho dado y la altura de los caracteres.

Usando un tiempo puedes seguir moviendo los caracteres de eliminación de la cadena que quieres medir y obtener el número de líneas (usando un tiempo (índice <string.length)) y multiplicar por la altura obtenida en getTextBounds.

Además, tendrá que agregar una altura variable para cada dos líneas que representan el espacio entre las líneas (que no se cuenta en getTextBounds).

Como un código de ejemplo, la función para conocer la altura de un texto de línea múltiple es algo como esto:

 public int getHeightOfMultiLineText(String text,int textSize, int maxWidth) { paint = new TextPaint(); paint.setTextSize(textSize); int index = 0; int linecount = 0; while(index < text.length()) { index += paint.breakText(text,index,text.length,true,maxWidth,null); linecount++; } Rect bounds = new Rect(); paint.getTextBounds("Yy", 0, 2, bounds); // obtain space between lines double lineSpacing = Math.max(0,((lineCount - 1) * bounds.height()*0.25)); return (int)Math.floor(lineSpacing + lineCount * bounds.height()); 

Nota: La variable maxWidth está en píxeles

Entonces tendrá que llamar a este método dentro de un rato para determinar cuál es el tamaño de fuente máximo para esa altura. Un ejemplo de código sería:

 textSize = 100; int maxHeight = 50; while(getHeightOfMultiLineText(text,textSize,maxWidth) > maxHeight) textSize--; 

Desafortunadamente esto era el único (por lo que sé) la manera que podía alcanzar el aspecto de las imágenes arriba.

Espero que esto puede ser de ayuda para cualquier persona tratando de superar este obstáculo.

Utilizando la idea de nunofmendes , escribí una clase derivada de TextView que redimensiona automáticamente el texto y admite varias líneas.

 import android.content.Context; import android.os.Handler; import android.text.Layout; import android.text.TextPaint; import android.text.TextUtils.TruncateAt; import android.util.AttributeSet; import android.util.TypedValue; public class AutoResizeTextView extends TextView { private Handler measureHandler = new Handler(); private Runnable requestLayout = new Runnable() { @Override public void run() { requestLayout(); } }; public AutoResizeTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public AutoResizeTextView(Context context, AttributeSet attrs) { super(context, attrs); } public AutoResizeTextView(Context context) { super(context); } @Override protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); final float maxWidth = getWidth(); final float maxHeight = getHeight(); if (maxWidth < 1.0f || maxHeight < 1.0f) { return; } int index = 0; int lineCount = 0; CharSequence text = getText(); final TextPaint paint = getPaint(); while (index < text.length()) { index += paint.breakText(text, index, text.length(), true, maxWidth, null); lineCount++; } final float height = lineCount * getLineHeight() + (lineCount > 0 ? (lineCount - 1) * paint.getFontSpacing() : 0); if (height > maxHeight) { final float textSize = getTextSize(); setTextSize(TypedValue.COMPLEX_UNIT_PX, (textSize - 1)); measureHandler.post(requestLayout); } } } 

Usted tiene una solución bastante buena en este problema, pero he echado un vistazo a esto desde un punto de vista diferente. Pienso que la lógica es más simple y más comprensible por lo menos para los nuevos desarrolladores androides. La idea se basa insertando el carácter '\ n' entre los espacios de la cadena grande. A continuación, establezco la cadena editada en textView como el texto que se muestra. Así que aquí está el código

 private String insertNewLineCharAtString(String str, int step){ StringBuilder sb = new StringBuilder(); int spaceCounter = 0; String[] strArray = str.split(" "); for(int i = 0; i < strArray.length; i++){ if(spaceCounter == step){ sb.append('\n'); sb.append(strArray[i]); spaceCounter = 0; }else{ sb.append(" "+strArray[i]); } spaceCounter++; } return sb.toString(); } 

Los parámetros son simples. Str es la cadena que vamos a editar y el paso variable define en cuántos espacios el método insertará el carácter '/ n'. Entonces, usted apenas llama algo como esto:

 myTextView.setText(insertNewLineCharactersAtString(yourDisplayingStr); 

Espero que esto ayude a muchos de ustedes a que no quieran profundizar con TextPaint, Rects y funciones matemáticas.

gracias por su solución que son superman! He implementado su código en la clase derivada de TextView. El código se convierte en código C # para Xamarin android. Usted puede convertir fácilmente a Java si usted necesita (quite el primer constructor para Java).

 public class AutoFitTextView : TextView { public AutoFitTextView(System.IntPtr javaReference, Android.Runtime.JniHandleOwnership transfer) : base(javaReference, transfer) { } public AutoFitTextView(Context context) : base(context) { } public AutoFitTextView(Context context, IAttributeSet attrs) : base(context, attrs) { } public void ResizeFontSize() { int textSize = 50; int maxHeight = this.Height; while (GetHeightOfMultiLineText(this.Text, textSize, this.Width) > maxHeight) { textSize--; } float scaleFactor = Context.Resources.DisplayMetrics.ScaledDensity; float additionalFactor = 1.2f; TextSize = ((float)(textSize / (additionalFactor * scaleFactor))); } private int GetHeightOfMultiLineText(string text, int textSize, int maxWidth) { TextPaint paint = new TextPaint(); paint.TextSize = textSize; int index = 0; int lineCount = 0; while (index < text.Length) { index += paint.BreakText(text, index, text.Length, true, maxWidth, null); lineCount++; } Rect bounds = new Rect(); paint.GetTextBounds("Yy", 0, 2, bounds); // obtain space between lines double lineSpacing = Math.Max(0, ((lineCount - 1) * bounds.Height() * 0.25)); return (int)Math.Floor(lineSpacing + lineCount * bounds.Height()); } } 

Aquí está el código AXML

  <touchtest.AutoFitTextView android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/tvLabel2" android:singleLine="false" android:inputType="textMultiLine" android:text="I am here To test this text" /> 

Introduzca aquí la descripción de la imagen Introduzca aquí la descripción de la imagen Introduzca aquí la descripción de la imagen

Una respuesta poco mejorada de phamducgiam. Muestra el texto con el tamaño ya ajustado, no skretchs después de mostrar. Parece feo en el editor debido a iniciar textSize 100, pero funciona como debería en ejecución. Aquí está el código:

 import android.content.Context; import android.text.TextPaint; import android.util.AttributeSet; import android.util.TypedValue; import android.widget.TextView; public class FontFitTextView extends TextView { public FontFitTextView(Context context) { super(context); } public FontFitTextView(Context context, AttributeSet attrs) { super(context, attrs); } public FontFitTextView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); // start decreasing font size from 100 setTextSize(TypedValue.COMPLEX_UNIT_PX, 100); // calculate dimensions. don't forget about padding final float maxWidth = getWidth()-(getPaddingLeft()+getPaddingRight()); final float maxHeight = getHeight()-(getPaddingTop()+getPaddingBottom()); if (maxWidth < 1.0f || maxHeight < 1.0f) { return; } CharSequence text = getText(); int lineCount = getLineCount(maxWidth, text); float height = getHeight(lineCount); // keep decreasing font size until it fits while (height > maxHeight) { final float textSize = getTextSize(); setTextSize(TypedValue.COMPLEX_UNIT_PX, (textSize - 1)); height = getHeight(getLineCount(maxWidth, getText())); } // show fitted textView requestLayout(); } private float getHeight(int lineCount) { return lineCount * getLineHeight() + (lineCount > 0 ? (lineCount - 1) * getPaint().getFontSpacing() : 0); } private int getLineCount(float maxWidth, CharSequence text) { int lineCount = 0; int index = 0; final TextPaint paint = getPaint(); while (index < text.length()) { index += paint.breakText(text, index, text.length(), true, maxWidth, null); lineCount++; } return lineCount; } } 

El compilable, código fijo: copiar y pegar este, no el aceptado, porque es necesario arreglarlo 🙂

 public static int getHeightOfMultiLineText(String text, int textSize, int maxWidth) { TextPaint paint = new TextPaint(); paint.setTextSize(textSize); int index = 0; int lineCount = 0; while (index < text.length()) { index += paint.breakText(text, index, text.length(), true, maxWidth, null); lineCount++; } Rect bounds = new Rect(); paint.getTextBounds("Yy", 0, 2, bounds); // obtain space between lines double lineSpacing = Math.max(0, ((lineCount - 1) * bounds.height() * 0.25)); return (int) Math.floor(lineSpacing + lineCount * bounds.height()); } 

Necesidad de utilizar, gracias.

Importante: Es necesario considerar el factor de escala

  int textSize = 50; int maxHeight = boundsTv.height(); while (getHeightOfMultiLineText(text, textSize, params.width) > maxHeight) { textSize--; } float scaleFactor = mainActivity.getResources().getDisplayMetrics().scaledDensity; float temp = 1.2f; tvText.setTextSize((float) (textSize / (temp*scaleFactor))); 
  • Android: El tipo de letra se cambia cuando se aplica la contraseña. Escriba en EditarTexto
  • Fuentes personalizadas y texto personalizado en Android
  • Cómo cambiar el espaciado de letras en una vista de texto?
  • Android: cómo utilizar la fuente Tamil
  • ¿Cómo hacer cursivo el trabajo de TextView en la vista de diseño gráfico de Eclipse? Incluso cuelga el PC
  • Cómo puedo mostrar el texto de HIndi en la tableta Android
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.