2010-04-07 17 views
27

Estoy tratando de crear un método para cambiar el tamaño del texto de varias líneas en un TextView de manera que quepa dentro de los límites (las dimensiones X e Y) del TextView.¿Cómo escalar/cambiar el tamaño del texto para que se ajuste a un TextView?

En este momento, tengo algo, pero todo lo que hace es cambiar el tamaño del texto de manera que solo la primera letra/carácter del texto llene las dimensiones del TextView (es decir, solo la primera letra es visible y es enorme). Necesito que se ajuste a todas las líneas del texto dentro de los límites de 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 límites de texto ... pequeños en relación con los valores tv.getWidth() y tv.getHeight() ... incluso si el tamaño del texto es muy superior al ancho o alto del TextView.

+0

Posible duplicado de: http://stackoverflow.com/a/7875656/435605 –

+0

posible duplicado de [Texto Auto Escala Vista de Texto para caber dentro de los límites] (http://stackoverflow.com/preguntas/5033012/auto-escala-TextView de texto a-fit-dentro-fuera) –

+0

Esta solución es ordenada: [texto escala en una vista para encajar?] [1] [1]: http://stackoverflow.com/a/7259136/3857562 –

Respuesta

2

quizás tratar de establecer setHoriztonallyScrolling() true antes de tomar mediciones de texto para que el Textview no trata de la disposición de su texto en varias líneas

+1

Gracias, pero quiero que el texto se muestre en varias líneas. Solo necesito que todas las líneas permanezcan dentro de los límites de la vista de texto y que sean lo más grandes posible. – RyanM

3

yo era capaz de responder a mi propia pregunta usando el siguiente código (véase más adelante), pero mi solución fue muy específica para la aplicación. Por ejemplo, esto probablemente solo se verá bien y/o funcionará para un TextView con un tamaño de aprox. 1/2 de la pantalla (con un margen superior de 40px y márgenes laterales de 20px ... sin margen inferior).

Sin embargo, utilizando este enfoque, puede crear su propia implementación similar. El método estático básicamente solo mira el número de caracteres y determina un factor de escala para aplicar al tamaño de texto del TextView, y luego incrementa el tamaño del texto hasta la altura total (una altura estimada - usando el ancho del texto, el texto alto, y el ancho del TextView) está justo debajo del TextView. Los parámetros necesarios para determinar el factor de escala (es decir, las sentencias if/else if) se establecieron mediante adivinar y verificar. Es probable que tenga que jugar con los números para que funcione para su aplicación particular.

Esta no es la solución más elegante, aunque fue fácil de codificar y me funciona. ¿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.

+19

¿Alguna razón para no colapsar el rango de 15 a 75 caracteres en una sola declaración? – Mark

0

Si su único requisito es que el texto se divida automáticamente y continúe en la siguiente línea y la altura no es importante, solo tiene que hacerlo así.

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

Esto tendrá su envoltura de TextView en su contenido verticalmente según su valor de maxEms.

3

que tenían el mismo problema y escribió una clase que parece funcionar para mí. Básicamente, utilicé un diseño estático para dibujar el texto en un lienzo por separado y volver a medir hasta que encuentre un tamaño de letra que se ajuste. Puedes ver la clase publicada en el tema a continuación. Espero que ayude.

Auto Scale TextView Text to Fit within Bounds

3

topamos con este mientras buscan una solución a mí mismo ...Había probado todas las otras soluciones que podía ver en el desbordamiento de la pila, etc. pero ninguna funcionaba realmente, así que escribí la mía.

Básicamente envolviendo la vista de texto en un diseño personalizado lineal que he sido capaz de medir correctamente el texto correctamente, asegurando que se mide con un ancho fijo.

<!-- TextView wrapped in the custom LinearLayout that expects one child TextView --> 
<!-- This view should specify the size you would want the text view to be displayed at --> 
<com.custom.ResizeView 
    android:layout_width="fill_parent" 
    android:layout_height="0dp" 
    android:layout_margin="10dp" 
    android:layout_weight="1" 
    android:orientation="horizontal" > 

    <TextView 
     android:id="@+id/CustomTextView" 
     android:layout_width="fill_parent" 
     android:layout_height="fill_parent" 
</com.custom.ResizeView> 

A continuación, el código de diseño lineal

public class ResizeView extends LinearLayout { 

    public ResizeView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
    } 

    public ResizeView(Context context) { 
     super(context); 
    } 

    @Override 
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 
     super.onLayout(changed, left, top, right, bottom); 

     // oldWidth used as a fixed width when measuring the size of the text 
     // view at different font sizes 
     final int oldWidth = getMeasuredWidth() - getPaddingBottom() - getPaddingTop(); 
     final int oldHeight = getMeasuredHeight() - getPaddingLeft() - getPaddingRight(); 

     // Assume we only have one child and it is the text view to scale 
     TextView textView = (TextView) getChildAt(0); 

     // This is the maximum font size... we iterate down from this 
     // I've specified the sizes in pixels, but sp can be used, just modify 
     // the call to setTextSize 

     float size = getResources().getDimensionPixelSize(R.dimen.solutions_view_max_font_size); 

     for (int textViewHeight = Integer.MAX_VALUE; textViewHeight > oldHeight; size -= 0.1f) { 
      textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, size); 

      // measure the text views size using a fixed width and an 
      // unspecified height - the unspecified height means measure 
      // returns the textviews ideal height 
      textView.measure(MeasureSpec.makeMeasureSpec(oldWidth, MeasureSpec.EXACTLY), MeasureSpec.UNSPECIFIED); 

      textViewHeight = textView.getMeasuredHeight(); 
     } 
    } 
} 

Esperamos que esto ayude a alguien.

+3

R.dimen.solutions_view_max_font_size ??? – AndroidDev

2

Una forma sería para especificar diferentes dimensiones SP para cada uno de los tamaños de pantalla generalizadas. Por ejemplo, proporcione 8sp para pantallas pequeñas, 12sp para pantallas normales, 16 sp para grandes y 20 sp para xlarge. Entonces simplemente haga que sus diseños se refieran a @dimen text_size o lo que sea, y puede estar tranquilo, ya que la densidad se soluciona a través de la unidad sp. Consulte el siguiente enlace para obtener más información sobre este enfoque.

http://www.developer.android.com/guide/topics/resources/more-resources.html#Dimension

debo señalar, sin embargo, que el apoyo más idiomas significa más trabajo durante la fase de prueba, especialmente si usted está interesado en mantener el texto en una línea, ya que algunos idiomas tienen palabras mucho más largos. En ese caso, cree un archivo dimens.xml en la carpeta values-de-large, por ejemplo, y modifique el valor manualmente. Espero que esto ayude.

+0

Desarrolle el modelo de trabajo para su idioma nativo (o el primero en ser demostrado) y cambie las contrapartes para soporte en varios idiomas después de una prueba exitosa. –

3

He jugado con esto durante bastante tiempo, tratando de corregir los tamaños de fuente en una amplia variedad de tabletas de 7 "(kindle fire, Nexus7 y algunas de bajo costo en China con pantallas de baja resolución) y dispositivos.

El enfoque que finalmente funcionó para mí es el siguiente: el "32" es un factor arbitrario que básicamente da más de 70 caracteres en una línea horizontal de 7 ", que es el tamaño de fuente que estaba buscando. Ajustar en consecuencia.

textView.setTextSize(getFontSize(activity)); 


public static int getFontSize (Activity activity) { 

    DisplayMetrics dMetrics = new DisplayMetrics(); 
    activity.getWindowManager().getDefaultDisplay().getMetrics(dMetrics); 

    // lets try to get them back a font size realtive to the pixel width of the screen 
    final float WIDE = activity.getResources().getDisplayMetrics().widthPixels; 
    int valueWide = (int)(WIDE/32.0f/(dMetrics.scaledDensity)); 
    return valueWide; 
} 
+0

Gracias por publicar esto. Esto funciona perfecto y lo voy a modificar para aceptar el tamaño de texto entrante y luego escupir el correcto. – a54studio

+0

Buena mejora sugerida, sí, puede especificar un tamaño objetivo entrando y luego puede configurar el tamaño universal para todos los dispositivos. –

1

Aquí hay una solución que he creado en función de otros comentarios. Esta solución le permite establecer el tamaño del texto en XML, que será el tamaño máximo y se ajustará para ajustarse a la altura de la vista. Size Adjusting TextView

private float findNewTextSize(int width, int height, CharSequence text) { 
      TextPaint textPaint = new TextPaint(getPaint()); 

      float targetTextSize = textPaint.getTextSize(); 

      int textHeight = getTextHeight(text, textPaint, width, targetTextSize); 
      while(textHeight > height && targetTextSize > mMinTextSize) { 
        targetTextSize = Math.max(targetTextSize - 1, mMinTextSize); 
        textHeight = getTextHeight(text, textPaint, width, targetTextSize); 
      } 
      return targetTextSize; 
    } 
private int getTextHeight(CharSequence source, TextPaint paint, int width, float textSize) { 
      paint.setTextSize(textSize); 
      StaticLayout layout = new StaticLayout(source, paint, width, Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, true); 
      return layout.getHeight(); 
    } 
+0

Perdón por eso. – Elliott

+0

no hay problema - gracias por la edición :-) – kleopatra

20

La biblioteca AutofitTextView de MavenCentral maneja esta muy bien.La fuente alojado en Github (1k + estrellas) en https://github.com/grantland/android-autofittextview

Añadir lo siguiente a su app/build.gradle

repositories { 
    mavenCentral() 
} 

dependencies { 
    compile 'me.grantland:autofittextview:0.2.+' 
} 

Activar cualquier vista que se extiende TextView en código:

AutofitHelper.create(textView); 

permitir a cualquier vista que se extiende TextView en XML :

<me.grantland.widget.AutofitLayout 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    > 
    <Button 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:singleLine="true" 
     /> 
</me.grantland.widget.AutofitLayout> 

Usa el buil t en Widget en código o XML:

<me.grantland.widget.AutofitTextView 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:singleLine="true" 
    /> 
+0

Gracias, excelente respuesta, ¡lo recomendaría dos veces! Me tomó un tiempo entender que el atributo 'TextView's textSize se usa ahora como un límite de tamaño máximo, por lo que debe especificarlo lo más grande posible ... – Mixaz

+0

Me gustó el aspecto de esta biblioteca, pero parece que no lo hace Realmente es compatible con StyleSpans, es probable que provoquen un corte de texto. –

+0

¡La solución perfecta!Una funcionalidad similar a adjustsFontSizeToFitWidth – Offek

0

Esto se basa en la respuesta de mattmook. Funcionó bien en algunos dispositivos, pero no en todos. Moví el cambio de tamaño al paso de medición, convertí el tamaño máximo de fuente en un atributo personalizado, tomé los márgenes en cuenta y extendí FrameLayout en lugar de LineairLayout.

public class ResizeView extends FrameLayout { 
    protected float max_font_size; 

    public ResizeView(Context context, AttributeSet attrs) { 
     super(context, attrs); 

     TypedArray a = context.getTheme().obtainStyledAttributes(
       attrs, 
       R.styleable.ResizeView, 
       0, 0); 
     max_font_size = a.getDimension(R.styleable.ResizeView_maxFontSize, 30.0f); 
    } 

    public ResizeView(Context context) { 
     super(context); 
    } 

    @Override 
    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { 
     // Use the parent's code for the first measure 
     super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
     // Assume we only have one child and it is the text view to scale 
     final TextView textView = (TextView) getChildAt(0); 

     // Check if the default measure resulted in a fitting textView 
     LayoutParams childLayout = (LayoutParams) textView.getLayoutParams(); 
     final int textHeightAvailable = getMeasuredHeight() - getPaddingTop() - getPaddingBottom() - childLayout.topMargin - childLayout.bottomMargin; 
     int textViewHeight = textView.getMeasuredHeight(); 
     if (textViewHeight < textHeightAvailable) { 
      return; 
     } 

     final int textWidthSpec = MeasureSpec.makeMeasureSpec(
       MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight() - childLayout.leftMargin - childLayout.rightMargin, 
       MeasureSpec.EXACTLY); 
     final int textHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); 

     for (float size = max_font_size; size >= 1.05f; size-=0.1f) { 
      textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, size); 
      textView.measure(textWidthSpec, textHeightSpec); 

      textViewHeight = textView.getMeasuredHeight(); 
      if (textViewHeight <= textHeightAvailable) { 
       break; 
      } 
     } 
    } 
} 

Y esto en attrs.xml:

<declare-styleable name="ResizeView"> 
    <attr name="maxFontSize" format="reference|dimension"/> 
</declare-styleable> 

Y finalmente utilizó la siguiente manera:

<PACKAGE_NAME.ui.ResizeView xmlns:custom="http://schemas.android.com/apk/res/PACKAGE_NAME" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:gravity="start|center_vertical" 
    android:padding="5dp" 
    custom:maxFontSize="@dimen/normal_text"> 

    <TextView android:id="@+id/tabTitle2" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent"/> 

</PACKAGE_NAME.ui.ResizeView> 
0

probar este ...

tv.setText("Give a very large text anc check , this xample is very usefull"); 
    countLine=tv.getLineHeight(); 
    System.out.println("LineCount " + countLine); 
    if (countLine>=40){ 
     tv.setTextSize(15); 
    } 
2

Nuevo desde Android O:

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

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

de iOS o si utiliza el apoyo lib: aplicación: autoSizeTextType = "uniforme" aplicación: autoSizeMinTextSize = "12SP" aplicación: autoSizeMaxTextSize = "100SP" aplicación: autoSizeStepGranularity = "2sp" con el uso de : xmlns: app = "http://schemas.android.com/apk/res-auto" – letroll

Cuestiones relacionadas