texto tamaño studio salto linea letra edittext color cambiar caja ajustar android textview textwrapping typeface text-size

android - studio - ¿Cómo ajustar automáticamente el tamaño del texto en un TextView multilínea de acuerdo con las dimensiones máximas de la vista?



salto de linea edittext android (6)

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

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

Básicamente mi objetivo es este:

Como puede ver, el texto cambia de tamaño según 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 poder cambiar el tipo de letra.

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 (propiedad de tamaño de texto en un TextView)

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

Esto fue llamado cada vez que el texto en la vista de texto fue cambiado.

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, solo se debe considerar el alto de la vista, ya que su ajuste en Android creará automáticamente una línea múltiple cuando sea necesario.

Aunque esta es una solución potencial, no está funcionando a la perfección (ni mucho menos) y no admite el cambio de tamaño hacia abajo (cuando se elimina texto).

¿Alguna sugerencia y / o ideas?


El código compilable y fijo: copiar y pegar este, no el aceptado, porque necesitas 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()); }

Necesito usar, gracias.

Importante: necesitas 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)));


Gracias por tu solución eres Superman! He implementado su código en la clase derivada de TextView. El código se convierte en código C # para Android Xamarin. Puede convertir fácilmente a Java si lo necesita (elimine 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" />


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

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, boolean measureForwards, float maxWidth, float [] measureWidth)

Añadido en el nivel API 1

Mida el texto, deteniéndose antes si el ancho medido excede el ancho máximo. Devuelva el número de caracteres que se midieron, y si el Ancho medido no es nulo, regrese el ancho real medido.

Combino eso con la pintura ''getTextBounds'' que:

public void getTextBounds (texto de cadena, inicio int, final int, límites Rect)

Añadido en el nivel API 1

Devuelva dentro de los límites (asignado por el llamante) 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 caben en un ancho dado y la altura de esos caracteres.

Usando un tiempo puede seguir moviendo los caracteres eliminados de la cadena que desea medir y obtener el número de líneas (usando un tiempo (índice <string.length)) y multiplicarlo 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 el getTextBounds).

Como código de ejemplo, la función para saber la altura de un texto de varias líneas 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

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

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

Desafortunadamente, esta fue la única forma (en la medida de lo que sé) que pude lograr el aspecto de las imágenes de arriba.

Espero que esto pueda ser de ayuda para cualquiera que intente superar este obstáculo.


Tiene una solución bastante buena para este problema, pero eché un vistazo a esto desde un punto de vista diferente. Creo que la lógica es más simple y más comprensible, al menos para los nuevos desarrolladores de Android. La idea se basa insertando el carácter ''/ n'' entre los espacios de la cadena grande. Luego, establezco la cadena editada en el textView como el texto que se muestra. Entonces, 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, solo llamas a algo como esto:

myTextView.setText(insertNewLineCharactersAtString(yourDisplayingStr);

Espero que esto ayude a muchos de ustedes a no querer profundizar más con las funciones TextPaint, Rects y math.


Usando share , escribí una clase derivada de TextView que cambia automáticamente el tamaño del texto y soporta múltiples 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); } } }


Una respuesta poco mejorada de phamducgiam. Muestra texto con tamaño ya ajustado, no skretchs después de mostrar. Parece feo en el editor debido al inicio de TextSize 100, pero funciona como debería en la 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; } }