2010-04-07 6 views
27

Ich versuche, eine Methode zum Ändern der Größe von mehrzeiligen Text in einem TextView so, dass es innerhalb der Grenzen (sowohl die X-und Y-Abmessungen) der TextView.Wie skaliere/skaliere ich Text, um ihn an TextView anzupassen?

Derzeit habe ich etwas, aber alles was es tut, ist die Größe des Textes so, dass nur der erste Buchstabe/Zeichen des Textes die Abmessungen der TextView (d. H. Nur der erste Buchstabe ist sichtbar, und es ist riesig). Ich brauche es, um alle Zeilen des Textes innerhalb der Grenzen des TextView anzupassen.

Hier ist, was ich bisher:

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); 
     } 
    } 
} 

Ich denke, das Problem bei der Verwendung von tv.getPaint().getTextBounds(...) ist. Es gibt immer kleine Zahlen für die Textgrenzen zurück ... klein relativ zu den Werten tv.getWidth() und tv.getHeight() ... auch wenn die Textgröße viel größer als die Breite oder Höhe der TextView ist. vielleicht

+0

Mögliche Duplikat: http://stackoverflow.com/a/7875656/435605 –

+0

möglich Duplikat [Auto Scale Textview Text innerhalb Bounds anpassen] (http://stackoverflow.com/Fragen/5033012/auto-Scale-Textview-Text-to-fit-in-bounds) –

+0

Diese Lösung ist ordentlich: [Skalieren von Text in einer Ansicht zu passen?] [1] [1]: http://StackOverflow.com/a/7259136/3857562 –

Antwort

2

versuchen setHoriztonallyScrolling() auf true setzen, bevor Text Messungen, so dass die Textview nicht Ihren Text nicht versuchen, auf mehreren Linien Layout

+1

Danke, aber ich möchte den Text in mehreren Zeilen angezeigt werden. Ich brauche nur alle Zeilen, um innerhalb der Grenzen der Textansicht zu bleiben und so groß wie möglich zu sein. – RyanM

3

konnte ich meine eigene Frage mit dem folgenden Code (siehe unten) beantworten, aber meine Lösung war sehr spezifisch für die Anwendung. Zum Beispiel wird dies wahrscheinlich nur gut aussehen und/oder funktionieren für eine TextView Größe von ca. 1/2 Bildschirm (mit einem 40px oberen Rand und 20px Seitenrändern ... kein unterer Rand).

Mithilfe dieses Ansatzes können Sie jedoch Ihre eigene ähnliche Implementierung erstellen. Die statische Methode betrachtet im Grunde nur die Anzahl der Zeichen und bestimmt einen Skalierungsfaktor, der auf die TextView-Textgröße angewendet wird, und erhöht dann schrittweise die Textgröße bis zur Gesamthöhe (eine geschätzte Höhe - unter Verwendung der Breite des Textes, des Textes height und die Breite der TextView) liegt knapp unter der von TextView. Die Parameter, die notwendig sind, um den Skalierungsfaktor zu bestimmen (d. H. Die if/else if-Anweisungen), wurden durch Raten und Prüfen gesetzt. Sie müssen wahrscheinlich mit den Zahlen herumspielen, damit es für Ihre spezielle Anwendung funktioniert.

Dies ist nicht die eleganteste Lösung, obwohl es einfach zu programmieren war und es für mich funktioniert. Hat jemand einen besseren Ansatz?

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); 
    } 

Danke.

+19

Gibt es einen Grund, den Bereich von 15 bis 75 Zeichen nicht auf eine Anweisung zu reduzieren? – Mark

0

Wenn Ihre einzige Anforderung ist, den Text automatisch zu teilen und in der nächsten Zeile fortzusetzen und die Höhe ist nicht wichtig, dann haben Sie es einfach so.

Dadurch wird Ihr TextView vertikal abhängig von Ihrem maxEms-Wert auf seinen Inhalt umbrochen.

3

Ich hatte das gleiche Problem und schrieb eine Klasse, die für mich zu arbeiten scheint. Im Grunde habe ich ein statisches Layout verwendet, um den Text in einer separaten Leinwand zu zeichnen und neu zu messen, bis ich eine passende Schriftgröße gefunden habe. Sie können die Klasse sehen, die im folgenden Thema veröffentlicht wurde. Ich hoffe, es hilft.

Auto Scale TextView Text to Fit within Bounds

3

auf diese Stumbled für eine Lösung selbst während der Suche ...Ich hatte alle anderen Lösungen ausprobiert, die ich auf Stack-Overflow usw. sehen konnte, aber keiner funktionierte wirklich, also schrieb ich mein eigenes.

Grundsätzlich konnte ich die Textansicht in einem benutzerdefinierten linearen Layout umwickeln, so dass ich den Text erfolgreich messen konnte, indem ich sicherstellte, dass er mit einer festen Breite gemessen wurde.

<!-- 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> 

Dann wird das lineare Layout-Code

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(); 
     } 
    } 
} 

hoffe, das hilft jemand.

+3

R.dimen.solutions_view_max_font_size ??? – AndroidDev

2

Eine Möglichkeit wäre, unterschiedliche sp Dimensionen für jede der generaliBildschirmGrößen zu spezifizieren. Stellen Sie zum Beispiel 8sp für kleine Bildschirme, 12sp für normale Bildschirme, 16 sp für große und 20 sp für xlarge. Dann haben Sie einfach Ihre Layouts auf @dimen text_size oder was auch immer und Sie können beruhigt sein, da die Dichte über die sp-Einheit besorgt wird. Weitere Informationen zu diesem Ansatz finden Sie unter folgendem Link.

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

Ich muß jedoch beachten, dass mehr Sprachen bedeutet, während der Testphase mehr Arbeit zu unterstützen, vor allem, wenn Sie im Einklang Text auf einer Linie interessiert sind, da einige Sprachen viel mehr Worte. In diesem Fall erstellen Sie beispielsweise eine Datei "dimens.xml" im Ordner "values-de-large" und optimieren den Wert manuell. Hoffe das hilft.

+0

Entwickeln Sie das Arbeitsmodell für Ihre Muttersprache (oder das erste, das demonstriert werden soll), und tauschen Sie die Kollegen nach mehrmaligem Sprachenaustausch nach erfolgreichen Tests aus. –

3

Ich habe mit dieser für eine ganze Weile gespielt, versuchen, meine Schriftgrößen auf einer Vielzahl von 7 "Tabletten (zünden Feuer, Nexus7, und einige billige in China mit Low-Res-Bildschirme) und Geräte zu korrigieren.

Der Ansatz, der schließlich für mich funktionierte, ist wie folgt: Die "32" ist ein willkürlicher Faktor, der im Prinzip über 70 Zeichen über eine horizontale 7 "-Tabellenzeile gibt, die eine gesuchte Schriftgröße ist. Dementsprechend anpassen.

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

Danke für das Posten. Das funktioniert perfekt und ich werde es modifizieren, um die Textgröße zu akzeptieren und dann die richtige auszuspucken. – a54studio

+0

Nette vorgeschlagene Verbesserung, ja, Sie können eine Zielgröße angeben und dann die universelle Größe für alle Geräte einrichten. –

1

Hier ist eine Lösung, die ich basierend auf einigen anderen Feedbacks erstellt habe. Mit dieser Lösung können Sie die Größe des Texts in XML festlegen, die die maximale Größe hat und sich an die Höhe der Ansicht anpassen. 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

Entschuldigung dafür. – Elliott

+0

kein Problem - vielen Dank für die Bearbeitung :-) – kleopatra

20

Die AutofitTextView Bibliothek von MavenCentral Griffe schön dieser.Die Quelle gehostet auf Github (1k + Sterne) in https://github.com/grantland/android-autofittextview

Fügen Sie den folgenden Code zu Ihrem app/build.gradle

repositories { 
    mavenCentral() 
} 

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

Aktivieren jeder Ansicht Textview in Code erstreckt:

AutofitHelper.create(textView); 

Aktivieren jede Ansicht erstreckt Textview in 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> 

Verwenden Sie das Gebäude t in Widget in Code oder XML:

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

Danke, ausgezeichnete Antwort, ich würde zweimal dafür upvote! Es dauerte eine Weile, bis ich verstand, dass das textSize-Attribut von TextView jetzt als maximale Größe verwendet wird. Sie müssen es also so groß wie möglich angeben ... – Mixaz

+0

Ich mochte das Aussehen dieser Bibliothek, aber es sieht so aus Wenn Sie StyleSpans wirklich unterstützen, verursachen sie wahrscheinlich eine Textunterbrechung. –

+0

Perfekte Lösung!Eine ähnliche Funktionalität zu iOS adjustsFontSizeToFitWidth – Offek

0

Dies basiert auf Mattmooks Antwort. Es funktionierte gut auf einigen Geräten, aber nicht auf allen. Ich habe die Größenänderung auf den Messschritt verschoben, die maximale Schriftgröße zu einem benutzerdefinierten Attribut gemacht, Ränder berücksichtigt und FrameLayout statt LineairLayout erweitert.

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; 
      } 
     } 
    } 
} 

Und das in attrs.xml:

<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

dieses ...

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

Neu seit Android Versuchen:

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

Und schließlich wie folgt verwendet 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

oder wenn Sie Unterstützung lib verwenden: App: autoSizeTextType = "Uniform" App: autoSizeMinTextSize = "12sp" App: autoSizeMaxTextSize = "100SP" App: autoSizeStepGranularity = "2sp" mit dem Einsatz von : xmlns: app = "http://schemas.android.com/apk/res-auto" – letroll