Cómo escalar / cambiar el tamaño del texto para adaptarse a una TextView ?

? RyanM @ | Original: StackOverFlow

Estoy tratando de crear un método para cambiar el tamaño de texto de varias líneas en una TextView que quepa dentro de los límites (tanto las dimensiones X e Y) de la TextView .

En la actualidad, tengo algo, pero lo único que hace es cambiar el tamaño del texto de tal manera que sólo la letra primero / carácter del texto llena las dimensiones del TextView ( es decir, sólo la primera letra es visible, y es enorme ) . Lo necesito para encajar todas las líneas del texto dentro de los límites de la TextView .

Esto es lo que tengo hasta ahora :

public static void autoScaleTextViewTextToHeight(TextView tv)
{
    final float initSize = tv.getTextSize();
    //get the width of the view's back image (unscaled).... 
    float minViewHeight;
    if(tv.getBackground()!=null)
    {
      minViewHeight = tv.getBackground().getIntrinsicHeight();
    }
    else
    {
      minViewHeight = 10f;//some min.
    }
    final float maxViewHeight = tv.getHeight() - (tv.getPaddingBottom()+tv.getPaddingTop())-12;// -12 just to be sure
    final String s = tv.getText().toString();

    //System.out.println(""+tv.getPaddingTop()+"/"+tv.getPaddingBottom());

    if(minViewHeight >0 && maxViewHeight >2)
    {
      Rect currentBounds = new Rect();
      tv.getPaint().getTextBounds(s, 0, s.length(), currentBounds);
      //System.out.println(""+initSize);
      //System.out.println(""+maxViewHeight);
      //System.out.println(""+(currentBounds.height()));

      float resultingSize = 1;
      while(currentBounds.height() < maxViewHeight)
      {
        resultingSize ++;
        tv.setTextSize(resultingSize);

        tv.getPaint().getTextBounds(s, 0, s.length(), currentBounds);
        //System.out.println(""+(currentBounds.height()+tv.getPaddingBottom()+tv.getPaddingTop()));
        //System.out.println("Resulting: "+resultingSize);
      }
      if(currentBounds.height()>=maxViewHeight)
      {
        //just to be sure, reduce the value
        tv.setTextSize(resultingSize-1);
      }
    }
}

Creo que el problema está en el uso de tv.getPaint().getTextBounds(...) . Siempre devuelve números pequeños para los extremos de texto ... pequeño en relación con los valores y tv.getWidth() tv.getHeight() ... incluso si el tamaño del texto es mucho más grande que la anchura o la altura de la TextView .

Top 5 Respuesta

1snctln @

tal vez intente configurar http://developer.android.com/reference/android/widget/TextView.html#setHorizontallyScrolling%28boolean%29 true antes de tomar mediciones de texto para que el TextView no trata de la disposición de su texto en varias líneas

2RyanM @

Tuve la oportunidad de responder a mi propia pregunta con el siguiente código ( ver más abajo), pero mi solución era muy específica para la aplicación. Por ejemplo, esto probablemente sólo va a quedar bien y / o trabajar para una TextView dimensionada para aprox . 1/2 de la pantalla ( también con un alto margen de 40px y 20px secundarios márgenes ... hay margen inferior ) .

El uso de este enfoque, sin embargo, usted puede crear su propia aplicación similar. El método estático, básicamente, sólo se ve en el número de caracteres y determina un factor de escala a aplicar para el tamaño del texto de la Vista de Texto, y luego aumenta gradualmente el tamaño del texto hasta que la altura total ( una altura estimada - utilizando el ancho del texto, el texto altura y la anchura de la TextView ) es justo por debajo de la de la TextView . Los parámetros necesarios para determinar el factor de escala ( es decir, el if / else if ) se establecen en conjeturas y comprobación . Es probable que tenga que jugar con los números para hacer que funcione para su aplicación en particular .

Esta no es la solución más elegante, aunque era fácil de código y funciona para mí. ¿Alguien tiene un mejor enfoque ?

public static void autoScaleTextViewTextToHeight(final TextView tv, String s)
    {       
        float currentWidth=tv.getPaint().measureText(s);
        int scalingFactor = 0;
        final int characters = s.length();
        //scale based on # of characters in the string
        if(characters<5)
        {
            scalingFactor = 1;
        }
        else if(characters>=5 && characters<10)
        {
            scalingFactor = 2;
        }
        else if(characters>=10 && characters<15)
        {
            scalingFactor = 3;
        }
        else if(characters>=15 && characters<20)
        {
            scalingFactor = 3;
        }
        else if(characters>=20 && characters<25)
        {
            scalingFactor = 3;
        }
        else if(characters>=25 && characters<30)
        {
            scalingFactor = 3;
        }
        else if(characters>=30 && characters<35)
        {
            scalingFactor = 3;
        }
        else if(characters>=35 && characters<40)
        {
            scalingFactor = 3;
        }
        else if(characters>=40 && characters<45)
        {
            scalingFactor = 3;
        }
        else if(characters>=45 && characters<50)
        {
            scalingFactor = 3;
        }
        else if(characters>=50 && characters<55)
        {
            scalingFactor = 3;
        }
        else if(characters>=55 && characters<60)
        {
            scalingFactor = 3;
        }
        else if(characters>=60 && characters<65)
        {
            scalingFactor = 3;
        }
        else if(characters>=65 && characters<70)
        {
            scalingFactor = 3;
        }
        else if(characters>=70 && characters<75)
        {
            scalingFactor = 3;
        }
        else if(characters>=75)
        {
            scalingFactor = 5;
        }

        //System.out.println(((int)Math.ceil(currentWidth)/tv.getWidth()+scalingFactor));
        //the +scalingFactor is important... increase this if nec. later
        while((((int)Math.ceil(currentWidth)/tv.getWidth()+scalingFactor)*tv.getTextSize())<tv.getHeight())
        {
            tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, tv.getTextSize()+0.25f);
            currentWidth=tv.getPaint().measureText(s);
            //System.out.println(((int)Math.ceil(currentWidth)/tv.getWidth()+scalingFactor));
        }

        tv.setText(s);
    }

Thanks.

3Octavian Damiean @

Si el único requisito es tener el texto automáticamente dividida y continuar en la siguiente línea y la altura no es importante, entonces sólo hay así.

<TextView
    android:layout_height="wrap_content"
    android:layout_width="wrap_content"
    android:maxEms="integer"
    android:width="integer"/>

Esto tendrá su envoltura TextView a su contenido en función verticalmente en su valor maxEms .

4vsm @

Compruebe si mi solución le ayuda a:

http://stackoverflow.com/questions/5033012/auto-scale-textview-text-to-fit-within-bounds/5280436#5280436

5Chase @

Tuve el mismo problema y escribí una clase que parece funcionar para mí. Básicamente, he usado un diseño estático para dibujar el texto en un lienzo separada y vuelva a medir hasta que encuentre un tamaño de fuente que se ajuste . Usted puede ver la clase publicado en el tema a continuación . Espero que ayude .

http://stackoverflow.com/questions/5033012/auto-scale-textview-text-to-fit-within-bounds/5535672#5535672