webentwicklung-frage-antwort-db.com.de

Wie skaliere/skaliere ich Text in eine TextView

Ich versuche, eine Methode zum Ändern der Größe von mehrzeiligem Text in einer TextView so zu erstellen, dass sie in die Grenzen (sowohl die X- als auch die Y-Dimension) der TextView passt.

Gegenwärtig habe ich etwas, aber die Größe des Textes muss so geändert werden, dass nur der erste Buchstabe bzw. das erste Zeichen des Textes die Dimensionen des TextView ausfüllt (d. H. Nur der erste Buchstabe ist sichtbar und groß). Ich brauche es, um alle Zeilen des Textes innerhalb der Grenzen des TextView zu platzieren.

Folgendes habe 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 liegt in der Verwendung von tv.getPaint().getTextBounds(...). Es gibt immer kleine Zahlen für die Textgrenzen zurück ... klein relativ zu den Werten von tv.getWidth() und tv.getHeight() ..., auch wenn die Textgröße viel größer als die Breite oder Höhe von TextView ist.

27
RyanM

Die AutofitTextView-Bibliothek von MavenCentral erledigt dies gut. Die Quelle wurde auf Github (1k + Sterne) gehostet unter https://github.com/grantland/Android-autofittextview

Fügen Sie Ihrem app/build.gradle Folgendes hinzu:

repositories {
    mavenCentral()
}

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

Aktivieren Sie jede Ansicht, die TextView im Code erweitert:

AutofitHelper.create(textView);

Aktivieren Sie eine Ansicht, die TextView in XML erweitert:

<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 integrierte Widget in Code oder XML:

<me.grantland.widget.AutofitTextView
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content"
    Android:singleLine="true"
    />
20
13rac1

Neu seit 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"
/>
5
Javatar

Ich habe schon eine ganze Weile damit gespielt und versucht, meine Schriftgrößen auf einer Vielzahl von 7-Zoll-Tablets (Kindle Fire, Nexus7 und einige preiswerte in China mit niedrigauflösenden Bildschirmen) und Geräten zu korrigieren.

Der Ansatz, der für mich schließlich funktioniert hat, ist wie folgt. Die "32" ist ein willkürlicher Faktor, der im Wesentlichen etwa 70+ Zeichen über eine horizontale 7-Zoll-Tablette gibt. Dies ist eine Schriftgröße, nach der ich gesucht habe. Passen Sie die Einstellung entsprechend an.

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;
}
3
Mark W

Ich konnte meine eigene Frage mit dem folgenden Code beantworten (siehe unten), aber meine Lösung war sehr spezifisch für die Anwendung. Zum Beispiel sieht das wahrscheinlich nur gut aus und/oder funktioniert für ein TextView mit einer Größe von ca. 1/2 des Bildschirms (auch mit 40px oberen Rand und 20px Seitenrändern ... kein unterer Rand).

Mit diesem Ansatz können Sie jedoch eine eigene ähnliche Implementierung erstellen. Die statische Methode betrachtet im Wesentlichen nur die Anzahl der Zeichen und bestimmt einen Skalierungsfaktor, der auf die Textgröße der TextView angewendet werden soll, und erhöht dann schrittweise die Textgröße bis zur Gesamthöhe (geschätzte Höhe - anhand der Breite des Textes den Text) Höhe und die Breite des TextView) liegt knapp unter der des TextView. Die Parameter, die zur Bestimmung des Skalierungsfaktors erforderlich sind (d. H. Die if/else if -Anweisungen), wurden durch Guess-and-Check festgelegt. 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 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);
    }

Vielen Dank.

3
RyanM

Stolperte darüber, während ich selbst nach einer Lösung suchte ... Ich hatte alle anderen Lösungen da draußen ausprobiert, die ich auf Stack-Überlauf usw. sehen konnte, aber keine funktionierte, also schrieb ich meine eigene.

Durch das Einbetten der Textansicht in ein benutzerdefiniertes lineares Layout konnte ich den Text erfolgreich messen, indem er 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 der lineare Layoutcode

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 jemandem.

3
appmattus

Ich hatte das gleiche Problem und schrieb eine Klasse, die für mich zu funktionieren scheint. Grundsätzlich habe ich ein statisches Layout verwendet, um den Text in einer separaten Leinwand zu zeichnen und erneut zu messen, bis ich eine passende Schriftgröße gefunden habe. Sie können die Klasse im folgenden Thema anzeigen. Ich hoffe, es hilft.

TextView-Text automatisch an Grenzen anpassen

3
Chase

Eine Möglichkeit wäre, unterschiedliche sp-Dimensionen für jede der verallgemeinerten Bildschirmgrößen anzugeben. Stellen Sie beispielsweise 8sp für kleine Bildschirme, 12sp für normale Bildschirme, 16 sp für große Bildschirme und 20 sp für xlarge bereit. Dann lassen Sie Ihre Layouts einfach auf @dimen text_size oder was auch immer verweisen und Sie können sich darauf verlassen, dass die Dichte über die sp-Einheit sichergestellt wird. Weitere Informationen zu diesem Ansatz finden Sie unter dem folgenden Link.

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

Ich muss jedoch beachten, dass die Unterstützung von mehr Sprachen in der Testphase mehr Arbeit bedeutet, insbesondere wenn Sie daran interessiert sind, Text in einer Zeile zu halten, da einige Sprachen viel längere Wörter haben. Erstellen Sie in diesem Fall beispielsweise eine dimens.xml-Datei im Ordner "values-de-large" und passen Sie den Wert manuell an. Hoffe das hilft.

2
Mark

setzen Sie vielleicht setHoriztonallyScrolling () auf true, bevor Sie Textmessungen vornehmen, damit textView nicht versucht, Ihren Text in mehreren Zeilen anzuordnen

2
snctln

Hier ist eine Lösung, die ich aufgrund anderer Rückmeldungen erstellt habe. Mit dieser Lösung können Sie die Größe des Textes in XML festlegen, dh die maximale Größe, die sich an die Ansichtshöhe anpasst. Größenanpassung von 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();
    }
1
Elliott

Wenn Ihre einzige Anforderung darin besteht, dass der Text automatisch geteilt und in der nächsten Zeile fortgesetzt wird und die Höhe nicht wichtig ist, dann haben Sie es einfach so.

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

Dadurch wird Ihr TextView vertikal abhängig von Ihrem maxEms-Wert in den Inhalt umgebrochen.

0

Dies basiert auf der Antwort von mattmook. Bei einigen Geräten hat es gut funktioniert, aber nicht bei 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 anstelle von 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:

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

Und schließlich so verwendet:

<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
Black Mantha

Prüfen Sie, ob meine Lösung Ihnen hilft:

TextView-Text automatisch an Grenzen anpassen

0
vsm

Versuche dies...

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);
    }
0
Mathan Chinna
0
Lassi Kinnunen