studio formularios formulario form ejemplo diseño dinamicos android textview font-size layoutparams

formularios - Ajuste automático de TextView para Android



formularios dinamicos android (15)

A continuación se encuentra TextView avalancha con funcionalidad adicional para la fuente personalizada.

Uso:

<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:foo="http://schemas.android.com/apk/res-auto" android:layout_width="wrap_content" android:layout_height="match_parent" > <de.meinprospekt.androidhd.view.AutoFitText android:layout_width="wrap_content" android:layout_height="10dp" android:text="Small Text" android:textColor="#FFFFFF" android:textSize="100sp" foo:customFont="fonts/Roboto-Light.ttf" /> </FrameLayout>

No olvide agregar: xmlns: foo = "http://schemas.android.com/apk/res-auto". La fuente debe estar en activos firectory

import java.util.ArrayList; import java.util.List; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Paint; import android.graphics.Typeface; import android.os.Build; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; import android.view.View; import android.view.ViewGroup.LayoutParams; import android.view.ViewTreeObserver; import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.widget.TextView; import de.meinprospekt.androidhd.R; import de.meinprospekt.androidhd.adapter.BrochuresHorizontalAdapter; import de.meinprospekt.androidhd.util.LOG; /** * https://stackoverflow.com/a/16174468/2075875 This class builds a new android Widget named AutoFitText which can be used instead of a TextView to * have the text font size in it automatically fit to match the screen width. Credits go largely to Dunni, gjpc, gregm and speedplane from * Stackoverflow, method has been (style-) optimized and rewritten to match android coding standards and our MBC. This version upgrades the original * "AutoFitTextView" to now also be adaptable to height and to accept the different TextView types (Button, TextClock etc.) * * @author pheuschk * @createDate: 18.04.2013 * * combined with: https://stackoverflow.com/a/7197867/2075875 */ @SuppressWarnings("unused") public class AutoFitText extends TextView { private static final String TAG = AutoFitText.class.getSimpleName(); /** Global min and max for text size. Remember: values are in pixels! */ private final int MIN_TEXT_SIZE = 10; private final int MAX_TEXT_SIZE = 400; /** Flag for singleLine */ private boolean mSingleLine = false; /** * A dummy {@link TextView} to test the text size without actually showing anything to the user */ private TextView mTestView; /** * A dummy {@link Paint} to test the text size without actually showing anything to the user */ private Paint mTestPaint; /** * Scaling factor for fonts. It''s a method of calculating independently (!) from the actual density of the screen that is used so users have the * same experience on different devices. We will use DisplayMetrics in the Constructor to get the value of the factor and then calculate SP from * pixel values */ private float mScaledDensityFactor; /** * Defines how close we want to be to the factual size of the Text-field. Lower values mean higher precision but also exponentially higher * computing cost (more loop runs) */ private final float mThreshold = 0.5f; /** * Constructor for call without attributes --> invoke constructor with AttributeSet null * * @param context */ public AutoFitText(Context context) { this(context, null); } public AutoFitText(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } public AutoFitText(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context, attrs); } private void init(Context context, AttributeSet attrs) { //TextViewPlus part https://stackoverflow.com/a/7197867/2075875 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AutoFitText); String customFont = a.getString(R.styleable.AutoFitText_customFont); setCustomFont(context, customFont); a.recycle(); // AutoFitText part mScaledDensityFactor = context.getResources().getDisplayMetrics().scaledDensity; mTestView = new TextView(context); mTestPaint = new Paint(); mTestPaint.set(this.getPaint()); this.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { // make an initial call to onSizeChanged to make sure that refitText is triggered onSizeChanged(AutoFitText.this.getWidth(), AutoFitText.this.getHeight(), 0, 0); // Remove the LayoutListener immediately so we don''t run into an infinite loop //AutoFitText.this.getViewTreeObserver().removeOnGlobalLayoutListener(this); removeOnGlobalLayoutListener(AutoFitText.this, this); } }); } public boolean setCustomFont(Context ctx, String asset) { Typeface tf = null; try { tf = Typeface.createFromAsset(ctx.getAssets(), asset); } catch (Exception e) { LOG.e(TAG, "Could not get typeface: "+e.getMessage()); return false; } setTypeface(tf); return true; } @SuppressLint("NewApi") public static void removeOnGlobalLayoutListener(View v, ViewTreeObserver.OnGlobalLayoutListener listener){ if (Build.VERSION.SDK_INT < 16) { v.getViewTreeObserver().removeGlobalOnLayoutListener(listener); } else { v.getViewTreeObserver().removeOnGlobalLayoutListener(listener); } } /** * Main method of this widget. Resizes the font so the specified text fits in the text box assuming the text box has the specified width. This is * done via a dummy text view that is refit until it matches the real target width and height up to a certain threshold factor * * @param targetFieldWidth The width that the TextView currently has and wants filled * @param targetFieldHeight The width that the TextView currently has and wants filled */ private void refitText(String text, int targetFieldWidth, int targetFieldHeight) { // Variables need to be visible outside the loops for later use. Remember size is in pixels float lowerTextSize = MIN_TEXT_SIZE; float upperTextSize = MAX_TEXT_SIZE; // Force the text to wrap. In principle this is not necessary since the dummy TextView // already does this for us but in rare cases adding this line can prevent flickering this.setMaxWidth(targetFieldWidth); // Padding should not be an issue since we never define it programmatically in this app // but just to to be sure we cut it off here targetFieldWidth = targetFieldWidth - this.getPaddingLeft() - this.getPaddingRight(); targetFieldHeight = targetFieldHeight - this.getPaddingTop() - this.getPaddingBottom(); // Initialize the dummy with some params (that are largely ignored anyway, but this is // mandatory to not get a NullPointerException) mTestView.setLayoutParams(new LayoutParams(targetFieldWidth, targetFieldHeight)); // maxWidth is crucial! Otherwise the text would never line wrap but blow up the width mTestView.setMaxWidth(targetFieldWidth); if (mSingleLine) { // the user requested a single line. This is very easy to do since we primarily need to // respect the width, don''t have to break, don''t have to measure... /*************************** Converging algorithm 1 ***********************************/ for (float testSize; (upperTextSize - lowerTextSize) > mThreshold;) { // Go to the mean value... testSize = (upperTextSize + lowerTextSize) / 2; mTestView.setTextSize(TypedValue.COMPLEX_UNIT_SP, testSize / mScaledDensityFactor); mTestView.setText(text); mTestView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); if (mTestView.getMeasuredWidth() >= targetFieldWidth) { upperTextSize = testSize; // Font is too big, decrease upperSize } else { lowerTextSize = testSize; // Font is too small, increase lowerSize } } /**************************************************************************************/ // In rare cases with very little letters and width > height we have vertical overlap! mTestView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); if (mTestView.getMeasuredHeight() > targetFieldHeight) { upperTextSize = lowerTextSize; lowerTextSize = MIN_TEXT_SIZE; /*************************** Converging algorithm 1.5 *****************************/ for (float testSize; (upperTextSize - lowerTextSize) > mThreshold;) { // Go to the mean value... testSize = (upperTextSize + lowerTextSize) / 2; mTestView.setTextSize(TypedValue.COMPLEX_UNIT_SP, testSize / mScaledDensityFactor); mTestView.setText(text); mTestView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); if (mTestView.getMeasuredHeight() >= targetFieldHeight) { upperTextSize = testSize; // Font is too big, decrease upperSize } else { lowerTextSize = testSize; // Font is too small, increase lowerSize } } /**********************************************************************************/ } } else { /*********************** Converging algorithm 2 ***************************************/ // Upper and lower size converge over time. As soon as they''re close enough the loop // stops // TODO probe the algorithm for cost (ATM possibly O(n^2)) and optimize if possible for (float testSize; (upperTextSize - lowerTextSize) > mThreshold;) { // Go to the mean value... testSize = (upperTextSize + lowerTextSize) / 2; // ... inflate the dummy TextView by setting a scaled textSize and the text... mTestView.setTextSize(TypedValue.COMPLEX_UNIT_SP, testSize / mScaledDensityFactor); mTestView.setText(text); // ... call measure to find the current values that the text WANTS to occupy mTestView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); int tempHeight = mTestView.getMeasuredHeight(); // int tempWidth = mTestView.getMeasuredWidth(); // LOG.debug("Measured: " + tempWidth + "x" + tempHeight); // LOG.debug("TextSize: " + testSize / mScaledDensityFactor); // ... decide whether those values are appropriate. if (tempHeight >= targetFieldHeight) { upperTextSize = testSize; // Font is too big, decrease upperSize } else { lowerTextSize = testSize; // Font is too small, increase lowerSize } } /**************************************************************************************/ // It is possible that a single word is wider than the box. The Android system would // wrap this for us. But if you want to decide fo yourself where exactly to break or to // add a hyphen or something than you''re going to want to implement something like this: mTestPaint.setTextSize(lowerTextSize); List<String> words = new ArrayList<String>(); for (String s : text.split(" ")) { Log.i("tag", "Word: " + s); words.add(s); } for (String word : words) { if (mTestPaint.measureText(word) >= targetFieldWidth) { List<String> pieces = new ArrayList<String>(); // pieces = breakWord(word, mTestPaint.measureText(word), targetFieldWidth); // Add code to handle the pieces here... } } } /** * We are now at most the value of threshold away from the actual size. To rather undershoot than overshoot use the lower value. To match * different screens convert to SP first. See {@link http://developer.android.com/guide/topics/resources/more-resources.html#Dimension} for * more details */ this.setTextSize(TypedValue.COMPLEX_UNIT_SP, lowerTextSize / mScaledDensityFactor); return; } /** * This method receives a call upon a change in text content of the TextView. Unfortunately it is also called - among others - upon text size * change which means that we MUST NEVER CALL {@link #refitText(String)} from this method! Doing so would result in an endless loop that would * ultimately result in a stack overflow and termination of the application * * So for the time being this method does absolutely nothing. If you want to notify the view of a changed text call {@link #setText(CharSequence)} */ @Override protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) { // Super implementation is also intentionally empty so for now we do absolutely nothing here super.onTextChanged(text, start, lengthBefore, lengthAfter); } @Override protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) { if (width != oldWidth && height != oldHeight) { refitText(this.getText().toString(), width, height); } } /** * This method is guaranteed to be called by {@link TextView#setText(CharSequence)} immediately. Therefore we can safely add our modifications * here and then have the parent class resume its work. So if text has changed you should always call {@link TextView#setText(CharSequence)} or * {@link TextView#setText(CharSequence, BufferType)} if you know whether the {@link BufferType} is normal, editable or spannable. Note: the * method will default to {@link BufferType#NORMAL} if you don''t pass an argument. */ @Override public void setText(CharSequence text, BufferType type) { int targetFieldWidth = this.getWidth(); int targetFieldHeight = this.getHeight(); if (targetFieldWidth <= 0 || targetFieldHeight <= 0 || text.equals("")) { // Log.v("tag", "Some values are empty, AutoFitText was not able to construct properly"); } else { refitText(text.toString(), targetFieldWidth, targetFieldHeight); } super.setText(text, type); } /** * TODO add sensibility for {@link #setMaxLines(int)} invocations */ @Override public void setMaxLines(int maxLines) { // TODO Implement support for this. This could be relatively easy. The idea would probably // be to manipulate the targetHeight in the refitText-method and then have the algorithm do // its job business as usual. Nonetheless, remember the height will have to be lowered // dynamically as the font size shrinks so it won''t be a walk in the park still if (maxLines == 1) { this.setSingleLine(true); } else { throw new UnsupportedOperationException("MaxLines != 1 are not implemented in AutoFitText yet, use TextView instead"); } } @Override public void setSingleLine(boolean singleLine) { // save the requested value in an instance variable to be able to decide later mSingleLine = singleLine; super.setSingleLine(singleLine); } }

errores conocidos: no funciona con Android 4.03 - las fuentes son invisibles o muy pequeñas (la avalancha original no funciona también) a continuación se encuentra la solución para ese error: https://stackoverflow.com/a/21851239/2075875

Fondo

Muchas veces necesitamos ajustar automáticamente la fuente de TextView a los límites que se le asignan.

El problema

Lamentablemente, aunque hay muchos hilos y publicaciones (y soluciones sugeridas) que hablan sobre este problema (ejemplo here , here y here ), ninguno de ellos funciona realmente bien.

Por eso, he decidido probar cada uno de ellos hasta que encuentre el verdadero negocio.

Creo que los requisitos de un textView de este tipo deberían ser:

  1. Debe permitir el uso de cualquier fuente, tipo de letra, estilo y conjunto de caracteres.

  2. Debe manejar tanto el ancho como la altura

  3. No hay truncamiento a menos que el texto no pueda ajustarse debido a la limitación que le dimos (ejemplo: texto demasiado largo, tamaño disponible demasiado pequeño). Sin embargo, podríamos solicitar una barra de desplazamiento horizontal / vertical si lo deseamos, solo para esos casos.

  4. Debe permitir multilínea o una sola línea. En caso de multilínea, permitir líneas máx. Y mín.

  5. No debe ser lento en el cálculo. ¿Usando un bucle para encontrar el mejor tamaño? Al menos optimícelo y no incremente su muestreo en 1 cada vez.

  6. En el caso de líneas múltiples, debería permitir cambiar el tamaño o usar más líneas, y / o permitirnos elegir las líneas usando el carácter "/ n".

Lo que he intentado

He intentado tantas muestras (incluidas las de los enlaces sobre las que he escrito) y también he intentado modificarlas para manejar los casos de los que he hablado, pero ninguna realmente funciona.

He realizado un proyecto de muestra que me permite ver visualmente si TextView se ajusta correctamente.

Actualmente, mi proyecto de muestra solo aleatoriza el texto (el alfabeto inglés más los dígitos) y el tamaño de textView, y lo deja en una línea, pero incluso esto no funciona bien en ninguna de las muestras que he probado.

Aquí está el código (también disponible here ):

Archivo res/layout/activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:text="Button" /> <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_above="@+id/button1" android:layout_alignParentLeft="true" android:background="#ffff0000" android:layout_alignParentRight="true" android:id="@+id/container" android:layout_alignParentTop="true" /> </RelativeLayout>

Archivo src/.../MainActivity.java

public class MainActivity extends Activity { private final Random _random =new Random(); private static final String ALLOWED_CHARACTERS ="qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890"; @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final ViewGroup container=(ViewGroup)findViewById(R.id.container); findViewById(R.id.button1).setOnClickListener(new OnClickListener() { @Override public void onClick(final View v) { container.removeAllViews(); final int maxWidth=container.getWidth(); final int maxHeight=container.getHeight(); final FontFitTextView fontFitTextView=new FontFitTextView(MainActivity.this); final int width=_random.nextInt(maxWidth)+1; final int height=_random.nextInt(maxHeight)+1; fontFitTextView.setLayoutParams(new LayoutParams(width,height)); fontFitTextView.setSingleLine(); fontFitTextView.setBackgroundColor(0xff00ff00); final String text=getRandomText(); fontFitTextView.setText(text); container.addView(fontFitTextView); Log.d("DEBUG","width:"+width+" height:"+height+" text:"+text); } }); } private String getRandomText() { final int textLength=_random.nextInt(20)+1; final StringBuilder builder=new StringBuilder(); for(int i=0;i<textLength;++i) builder.append(ALLOWED_CHARACTERS.charAt(_random.nextInt(ALLOWED_CHARACTERS.length()))); return builder.toString(); } }

La pregunta

¿Alguien sabe de una solución para este problema común que realmente funciona?

Incluso una solución que tiene muchas menos características de las que he escrito, por ejemplo, una que tiene solo un número constante de líneas de texto y ajusta su fuente según su tamaño, sin embargo, nunca tiene errores extraños y el texto también se pone Grande / pequeño comparado con su espacio disponible.

Proyecto GitHub

Dado que este es un TextView tan importante, he decidido publicar una biblioteca, para que todos puedan usarla fácilmente y contribuir a ella, here .


Ahora hay una solución oficial para este problema. Las vistas automáticas de TextViews introducidas con Android O están disponibles en la biblioteca de soporte 26 y son compatibles con versiones anteriores de Android 4.0.

https://developer.android.com/preview/features/autosizing-textview.html

No estoy seguro de por qué https://.com/a/42940171/47680 que también incluía esta información, fue eliminado por un administrador.


Convierta la vista de texto en una imagen y la escala de la imagen dentro de los límites.

Este es un ejemplo de cómo convertir una vista en una imagen: ¿ Convertir una vista a Bitmap sin mostrarla en Android?

El problema es que su texto no será seleccionable, pero debería hacer el truco. No lo he probado, por lo que no estoy seguro de cómo se vería (debido a la escala).


Gracias a la solución simple de MartinH here , este código también se ocupa de las etiquetas android:drawableLeft , android:drawableRight , android:drawableTop y android:drawableBottom .

Mi respuesta aquí debería hacerte feliz Auto texto de vista de escalado para que se ajuste dentro de los límites

He modificado su caso de prueba:

@Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final ViewGroup container = (ViewGroup) findViewById(R.id.container); findViewById(R.id.button1).setOnClickListener(new OnClickListener() { @Override public void onClick(final View v) { container.removeAllViews(); final int maxWidth = container.getWidth(); final int maxHeight = container.getHeight(); final AutoResizeTextView fontFitTextView = new AutoResizeTextView(MainActivity.this); final int width = _random.nextInt(maxWidth) + 1; final int height = _random.nextInt(maxHeight) + 1; fontFitTextView.setLayoutParams(new FrameLayout.LayoutParams( width, height)); int maxLines = _random.nextInt(4) + 1; fontFitTextView.setMaxLines(maxLines); fontFitTextView.setTextSize(500);// max size fontFitTextView.enableSizeCache(false); fontFitTextView.setBackgroundColor(0xff00ff00); final String text = getRandomText(); fontFitTextView.setText(text); container.addView(fontFitTextView); Log.d("DEBUG", "width:" + width + " height:" + height + " text:" + text + " maxLines:" + maxLines); } }); }

Estoy publicando el código aquí por solicitud del desarrollador de Android :

Efecto final

Ejemplo de archivo de diseño:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" android:padding="16dp" > <com.vj.widgets.AutoResizeTextView android:layout_width="match_parent" android:layout_height="100dp" android:ellipsize="none" android:maxLines="2" android:text="Auto Resized Text, max 2 lines" android:textSize="100sp" /> <!-- maximum size --> <com.vj.widgets.AutoResizeTextView android:layout_width="match_parent" android:layout_height="100dp" android:ellipsize="none" android:gravity="center" android:maxLines="1" android:text="Auto Resized Text, max 1 line" android:textSize="100sp" /> <!-- maximum size --> <com.vj.widgets.AutoResizeTextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Auto Resized Text" android:textSize="500sp" /> <!-- maximum size --> </LinearLayout>

Y el código de Java:

import android.annotation.TargetApi; import android.content.Context; import android.content.res.Resources; import android.graphics.RectF; import android.os.Build; import android.text.Layout.Alignment; import android.text.StaticLayout; import android.text.TextPaint; import android.util.AttributeSet; import android.util.SparseIntArray; import android.util.TypedValue; import android.widget.TextView; public class AutoResizeTextView extends TextView { private interface SizeTester { /** * * @param suggestedSize * Size of text to be tested * @param availableSpace * available space in which text must fit * @return an integer < 0 if after applying {@code suggestedSize} to * text, it takes less space than {@code availableSpace}, > 0 * otherwise */ public int onTestSize(int suggestedSize, RectF availableSpace); } private RectF mTextRect = new RectF(); private RectF mAvailableSpaceRect; private SparseIntArray mTextCachedSizes; private TextPaint mPaint; private float mMaxTextSize; private float mSpacingMult = 1.0f; private float mSpacingAdd = 0.0f; private float mMinTextSize = 20; private int mWidthLimit; private static final int NO_LINE_LIMIT = -1; private int mMaxLines; private boolean mEnableSizeCache = true; private boolean mInitializedDimens; public AutoResizeTextView(Context context) { super(context); initialize(); } public AutoResizeTextView(Context context, AttributeSet attrs) { super(context, attrs); initialize(); } public AutoResizeTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initialize(); } private void initialize() { mPaint = new TextPaint(getPaint()); mMaxTextSize = getTextSize(); mAvailableSpaceRect = new RectF(); mTextCachedSizes = new SparseIntArray(); if (mMaxLines == 0) { // no value was assigned during construction mMaxLines = NO_LINE_LIMIT; } } @Override public void setTextSize(float size) { mMaxTextSize = size; mTextCachedSizes.clear(); adjustTextSize(); } @Override public void setMaxLines(int maxlines) { super.setMaxLines(maxlines); mMaxLines = maxlines; adjustTextSize(); } public int getMaxLines() { return mMaxLines; } @Override public void setSingleLine() { super.setSingleLine(); mMaxLines = 1; adjustTextSize(); } @Override public void setSingleLine(boolean singleLine) { super.setSingleLine(singleLine); if (singleLine) { mMaxLines = 1; } else { mMaxLines = NO_LINE_LIMIT; } adjustTextSize(); } @Override public void setLines(int lines) { super.setLines(lines); mMaxLines = lines; adjustTextSize(); } @Override public void setTextSize(int unit, float size) { Context c = getContext(); Resources r; if (c == null) r = Resources.getSystem(); else r = c.getResources(); mMaxTextSize = TypedValue.applyDimension(unit, size, r.getDisplayMetrics()); mTextCachedSizes.clear(); adjustTextSize(); } @Override public void setLineSpacing(float add, float mult) { super.setLineSpacing(add, mult); mSpacingMult = mult; mSpacingAdd = add; } /** * Set the lower text size limit and invalidate the view * * @param minTextSize */ public void setMinTextSize(float minTextSize) { mMinTextSize = minTextSize; adjustTextSize(); } private void adjustTextSize() { if (!mInitializedDimens) { return; } int startSize = (int) mMinTextSize; int heightLimit = getMeasuredHeight() - getCompoundPaddingBottom() - getCompoundPaddingTop(); mWidthLimit = getMeasuredWidth() - getCompoundPaddingLeft() - getCompoundPaddingRight(); mAvailableSpaceRect.right = mWidthLimit; mAvailableSpaceRect.bottom = heightLimit; super.setTextSize( TypedValue.COMPLEX_UNIT_PX, efficientTextSizeSearch(startSize, (int) mMaxTextSize, mSizeTester, mAvailableSpaceRect)); } private final SizeTester mSizeTester = new SizeTester() { @TargetApi(Build.VERSION_CODES.JELLY_BEAN) @Override public int onTestSize(int suggestedSize, RectF availableSPace) { mPaint.setTextSize(suggestedSize); String text = getText().toString(); boolean singleline = getMaxLines() == 1; if (singleline) { mTextRect.bottom = mPaint.getFontSpacing(); mTextRect.right = mPaint.measureText(text); } else { StaticLayout layout = new StaticLayout(text, mPaint, mWidthLimit, Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, true); // Return early if we have more lines if (getMaxLines() != NO_LINE_LIMIT && layout.getLineCount() > getMaxLines()) { return 1; } mTextRect.bottom = layout.getHeight(); int maxWidth = -1; for (int i = 0; i < layout.getLineCount(); i++) { if (maxWidth < layout.getLineWidth(i)) { maxWidth = (int) layout.getLineWidth(i); } } mTextRect.right = maxWidth; } mTextRect.offsetTo(0, 0); if (availableSPace.contains(mTextRect)) { // May be too small, don''t worry we will find the best match return -1; } else { // too big return 1; } } }; /** * Enables or disables size caching, enabling it will improve performance * where you are animating a value inside TextView. This stores the font * size against getText().length() Be careful though while enabling it as 0 * takes more space than 1 on some fonts and so on. * * @param enable * Enable font size caching */ public void enableSizeCache(boolean enable) { mEnableSizeCache = enable; mTextCachedSizes.clear(); adjustTextSize(getText().toString()); } private int efficientTextSizeSearch(int start, int end, SizeTester sizeTester, RectF availableSpace) { if (!mEnableSizeCache) { return binarySearch(start, end, sizeTester, availableSpace); } int key = getText().toString().length(); int size = mTextCachedSizes.get(key); if (size != 0) { return size; } size = binarySearch(start, end, sizeTester, availableSpace); mTextCachedSizes.put(key, size); return size; } private static int binarySearch(int start, int end, SizeTester sizeTester, RectF availableSpace) { int lastBest = start; int lo = start; int hi = end - 1; int mid = 0; while (lo <= hi) { mid = (lo + hi) >>> 1; int midValCmp = sizeTester.onTestSize(mid, availableSpace); if (midValCmp < 0) { lastBest = lo; lo = mid + 1; } else if (midValCmp > 0) { hi = mid - 1; lastBest = hi; } else { return mid; } } // Make sure to return the last best. // This is what should always be returned. return lastBest; } @Override protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) { super.onTextChanged(text, start, before, after); adjustTextSize(); } @Override protected void onSizeChanged(int width, int height, int oldwidth, int oldheight) { mInitializedDimens = true; mTextCachedSizes.clear(); super.onSizeChanged(width, height, oldwidth, oldheight); if (width != oldwidth || height != oldheight) { adjustTextSize(); } } }

Advertencia:

Tenga cuidado con este bug resolved en Android 3.1 (Honeycomb) sin embargo.


He modificado un poco la respuesta de M-WaJeEh para tener en cuenta los elementos dibujables compuestos de los lados.

Los métodos getCompoundPaddingXXXX() devuelven el padding of the view + drawable space . Ver por ejemplo: TextView.getCompoundPaddingLeft()

Problema: esto corrige la medición del ancho y el alto del espacio TextView disponible para el texto. Si no tenemos en cuenta el tamaño del dibujo, este se ignora y el texto terminará superponiéndose con el dibujo.

Segmento actualizado adjustTextSize(String) :

private void adjustTextSize(final String text) { if (!mInitialized) { return; } int heightLimit = getMeasuredHeight() - getCompoundPaddingBottom() - getCompoundPaddingTop(); mWidthLimit = getMeasuredWidth() - getCompoundPaddingLeft() - getCompoundPaddingRight(); mAvailableSpaceRect.right = mWidthLimit; mAvailableSpaceRect.bottom = heightLimit; int maxTextSplits = text.split(" ").length; AutoResizeTextView.super.setMaxLines(Math.min(maxTextSplits, mMaxLines)); super.setTextSize( TypedValue.COMPLEX_UNIT_PX, binarySearch((int) mMinTextSize, (int) mMaxTextSize, mSizeTester, mAvailableSpaceRect)); }


Mi requerimiento es

  • Haga clic en la vista de texto escalable
  • Abra una lista de actividades y muestre varios elementos de cadena de longitud.
  • Seleccione un texto de la lista.
  • Vuelve a colocar el texto en la vista de texto escalable en otra actividad.

Remití el enlace: texto de AutoView de escala automática para que se ajuste dentro de los límites (incluidos los comentarios) y también a DialogTitle.java

Descubrí que la solución provista es agradable y simple, pero no cambia dinámicamente el tamaño del cuadro de texto. Funciona muy bien cuando la longitud del texto seleccionado de la vista de lista es mayor en tamaño que la longitud del texto existente en la vista de texto ScalableTextView . Cuando se selecciona el texto que tiene una longitud más pequeña que el texto existente en la vista de texto ScalableTextView , no aumenta el tamaño del texto, mostrando el texto en el tamaño más pequeño.

Modifiqué el archivo NavigationTextView.java para reajustar el tamaño del texto en función de la longitud del texto. Aquí está mi ScalableTextView.java

public class ScalableTextView extends TextView { float defaultTextSize = 0.0f; public ScalableTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); setSingleLine(); setEllipsize(TruncateAt.END); defaultTextSize = getTextSize(); } public ScalableTextView(Context context, AttributeSet attrs) { super(context, attrs); setSingleLine(); setEllipsize(TruncateAt.END); defaultTextSize = getTextSize(); } public ScalableTextView(Context context) { super(context); setSingleLine(); setEllipsize(TruncateAt.END); defaultTextSize = getTextSize(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setTextSize(TypedValue.COMPLEX_UNIT_PX, defaultTextSize); super.onMeasure(widthMeasureSpec, heightMeasureSpec); final Layout layout = getLayout(); if (layout != null) { final int lineCount = layout.getLineCount(); if (lineCount > 0) { int ellipsisCount = layout.getEllipsisCount(lineCount - 1); while (ellipsisCount > 0) { final float textSize = getTextSize(); // textSize is already expressed in pixels setTextSize(TypedValue.COMPLEX_UNIT_PX, (textSize - 1)); super.onMeasure(widthMeasureSpec, heightMeasureSpec); ellipsisCount = layout.getEllipsisCount(lineCount - 1); } } } } }

Feliz codificación ....


Ok, he usado la última semana para volver a escribir mi código de forma masiva para que se ajuste a tu prueba con precisión . Ahora puede copiar este 1: 1 y funcionará de inmediato, incluido setSingleLine() . Por favor, recuerde ajustar MIN_TEXT_SIZE y MAX_TEXT_SIZE si desea valores extremos.

El algoritmo convergente se ve así:

for (float testSize; (upperTextSize - lowerTextSize) > mThreshold;) { // Go to the mean value... testSize = (upperTextSize + lowerTextSize) / 2; // ... inflate the dummy TextView by setting a scaled textSize and the text... mTestView.setTextSize(TypedValue.COMPLEX_UNIT_SP, testSize / mScaledDensityFactor); mTestView.setText(text); // ... call measure to find the current values that the text WANTS to occupy mTestView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); int tempHeight = mTestView.getMeasuredHeight(); // ... decide whether those values are appropriate. if (tempHeight >= targetFieldHeight) { upperTextSize = testSize; // Font is too big, decrease upperSize } else { lowerTextSize = testSize; // Font is too small, increase lowerSize } }

Y toda la clase se puede encontrar aquí.

El resultado es muy flexible ahora. Esto funciona igual declarado en xml así:

<com.example.myProject.AutoFitText android:id="@+id/textView" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="4" android:text="@string/LoremIpsum" />

... así como construido programáticamente como en tu prueba.

Realmente espero que puedas usar esto ahora. Puede llamar a setText(CharSequence text) ahora para usarlo por cierto. La clase se ocupa de excepciones estupendamente raras y debe ser sólida como una roca. Lo único que el algoritmo no soporta aún es:

  • Llamadas a setMaxLines(x) donde x >= 2

¡Pero he agregado comentarios extensos para ayudarte a construir esto si lo deseas!

Tenga en cuenta:

Si solo usas esto normalmente sin limitarlo a una sola línea, entonces es posible que se rompan las palabras como mencionaste antes. Esta es una característica de Android , no es culpa del AutoFitText . Android siempre rompe palabras que son demasiado largas para un TextView y en realidad es una gran comodidad. Si desea intervenir aquí, consulte mis comentarios y el código que comienzan en la línea 203. Ya he escrito una división adecuada y el reconocimiento para usted, todo lo que debería hacer a partir de ahora es dividir las palabras y luego modificarlas como desee. .

En conclusión: debería considerar volver a escribir su prueba para que también admita los caracteres espaciales, así:

final Random _random = new Random(); final String ALLOWED_CHARACTERS = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890"; final int textLength = _random.nextInt(80) + 20; final StringBuilder builder = new StringBuilder(); for (int i = 0; i < textLength; ++i) { if (i % 7 == 0 && i != 0) { builder.append(" "); } builder.append(ALLOWED_CHARACTERS.charAt(_random.nextInt(ALLOWED_CHARACTERS.length()))); } ((AutoFitText) findViewById(R.id.textViewMessage)).setText(builder.toString());

Esto producirá resultados muy hermosos (y más realistas).
Encontrará comentarios para comenzar en este asunto también.

Buena suerte y saludos cordiales


Prueba esto

TextWatcher changeText = new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { tv3.setText(et.getText().toString()); tv3.post(new Runnable() { @Override public void run() { while(tv3.getLineCount() >= 3){ tv3.setTextSize((tv3.getTextSize())-1); } } }); } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} @Override public void afterTextChanged(Editable s) { } };


Voy a explicar cómo funciona este atributo versiones de Android inferiores paso a paso:

1- Importar la biblioteca de soporte de Android 26.xx en su archivo de proyecto gradle. Si no hay una biblioteca de soporte en IDE, se descargarán automáticamente.

dependencies { compile ''com.android.support:support-v4:26.1.0'' compile ''com.android.support:appcompat-v7:26.1.0'' compile ''com.android.support:support-v13:26.1.0'' } allprojects { repositories { jcenter() maven { url "https://maven.google.com" } } }

2- Abra su archivo XML de diseño y refactorice como esta etiqueta su TextView. Este escenario es: cuando incremente el tamaño de fuente en el sistema, ajuste el texto a un ancho disponible, no ajuste de texto.

<android.support.v7.widget.AppCompatTextView android:id="@+id/textViewAutoSize" android:layout_width="match_parent" android:layout_height="25dp" android:ellipsize="none" android:text="Auto size text with compatible lower android versions." android:textSize="12sp" app:autoSizeMaxTextSize="14sp" app:autoSizeMinTextSize="4sp" app:autoSizeStepGranularity="0.5sp" app:autoSizeTextType="uniform" />


Advertencia, error en Android 3 (Honeycomb) y Android 4.0 (Ice Cream Sandwich)

Las versiones de Android: 3.1 - 4.04 tienen un error, que setTextSize () dentro de TextView solo funciona por primera vez (primera invocación).

El error se describe en el Problema 22493: El error de altura de TextView en Android 4.0 y el resolved .

La solución es agregar un carácter de nueva línea al texto asignado a TextView antes de cambiar el tamaño:

final String DOUBLE_BYTE_SPACE = "/u3000"; textView.append(DOUBLE_BYTE_SPACE);

Lo uso en mi código de la siguiente manera:

final String DOUBLE_BYTE_SPACE = "/u3000"; AutoResizeTextView textView = (AutoResizeTextView) view.findViewById(R.id.aTextView); String fixString = ""; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB_MR1 && android.os.Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { fixString = DOUBLE_BYTE_SPACE; } textView.setText(fixString + "The text" + fixString);

Agrego este carácter "/ u3000" a la izquierda y derecha de mi texto, para mantenerlo centrado. Si lo tiene alineado a la izquierda, agregue solo a la derecha. Por supuesto, también se puede incrustar con el widget AutoResizeTextView, pero quería mantener el código fijo fuera.


Desde Android O, es posible redimensionar automáticamente el texto en xml:

https://developer.android.com/preview/features/autosizing-textview.html

<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" app:autoSizeTextType="uniform" app:autoSizeMinTextSize="12sp" app:autoSizeMaxTextSize="100sp" app:autoSizeStepGranularity="2sp" />

Android O le permite indicar a TextView que permita que el tamaño del texto se expanda o se contraiga automáticamente para completar su diseño según las características y los límites de TextView. Esta configuración facilita la optimización del tamaño del texto en diferentes pantallas con contenido dinámico.

La biblioteca de soporte 26.0 Beta proporciona soporte completo para la función TextView de autoajuste en dispositivos que ejecutan versiones de Android anteriores a Android O. La biblioteca brinda soporte a Android 4.0 (nivel API 14) y superior. El paquete android.support.v4.widget contiene la clase TextViewCompat para acceder a las funciones de una manera compatible con versiones anteriores.


Si buscas algo más fácil:

public MyTextView extends TextView{ public void resize(String text, float textViewWidth, float textViewHeight) { Paint p = new Paint(); Rect bounds = new Rect(); p.setTextSize(1); p.getTextBounds(text, 0, text.length(), bounds); float widthDifference = (textViewWidth)/bounds.width(); float heightDifference = (textViewHeight); textSize = Math.min(widthDifference, heightDifference); setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize); }


Solución rápida para el problema descrito por @Malachiasz

He solucionado el problema agregando soporte personalizado para esto en la clase de cambio de tamaño automático:

public void setTextCompat(final CharSequence text) { setTextCompat(text, BufferType.NORMAL); } public void setTextCompat(final CharSequence text, BufferType type) { // Quick fix for Android Honeycomb and Ice Cream Sandwich which sets the text only on the first call if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1 && Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { super.setText(DOUBLE_BYTE_WORDJOINER + text + DOUBLE_BYTE_WORDJOINER, type); } else { super.setText(text, type); } } @Override public CharSequence getText() { String originalText = super.getText().toString(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1 && Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { // We try to remove the word joiners we added using compat method - if none found - this will do nothing. return originalText.replaceAll(DOUBLE_BYTE_WORDJOINER, ""); } else { return originalText; } }

Solo llama en yourView.setTextCompat(newTextValue)lugar deyourView.setText(newTextValue)


Desde junio de 2018, Android admite oficialmente esta función para Android 4.0 (nivel de API 14) y superior.
Con Android 8.0 (nivel API 26) y superior:

setAutoSizeTextTypeUniformWithConfiguration(int autoSizeMinTextSize, int autoSizeMaxTextSize, int autoSizeStepGranularity, int unit);

Versiones de Android anteriores a Android 8.0 (nivel de API 26):

TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(TextView textView, int autoSizeMinTextSize, int autoSizeMaxTextSize, int autoSizeStepGranularity, int unit)

Echa un vistazo a mi answer detallada .


Intenta agregar LayoutParamsy MaxWidthy MaxHeightal TextView. Forzará que el diseño respete el contenedor principal y no el desbordamiento.

textview.setLayoutParams(new LayoutParams(LinearLayout.MATCH_PARENT,LinearLayout.WRAP_CONTENT)); int GeneralApproxWidthOfContainer = 400; int GeneralApproxHeightOfContainer = 600; textview.setMaxWidth(400); textview.setMaxHeight(600);`