2013-04-18 10 views
17

Ich habe nach einer Möglichkeit gesucht, einen Text in einer Textansicht automatisch anzupassen.Wie wird die Textgröße in einem mehrzeiligen TextView entsprechend der maximalen Ansicht der Ansicht automatisch angepasst?

Aber wie viele andere diese nicht mein Problem lösen: Durch meine Suche habe ich viele Lösungen wie gefunden. Sie funktionieren nicht wie erwartet, wenn wir eine TextView mit Multilinie verwenden.

Grundsätzlich mein Ziel ist es diese:

Phase 1 Phase 2 Phase 3 Phase 4

Wie Sie sehen können, wird der Text passt die Größe basierend auf Breite, Höhe und achtet auch auf den Zeilenumbruch, eine Schaffung mehrzeilige Textansicht. Auch in der Lage sein, die Schriftart zu ändern.

Eine meiner Ideen, diese zu lösen war so etwas wie dieses:

int size = CONSTANT_MAX_SIZE; 
TextView tv = (TextView) findViewById(R.id.textview1) 
while(Math.abs(tv.getMeasuredHeight()) >= TEXTVIEW_MAX_HEIGHT) { 
    size--; 
    tv.setTextSize(size); 
    tv.measure(MeasureSpec.UNSPECIFIED,MeasureSpec.UNSPECIFIED); 
    i++; 
} 

CONSTANT_MAX_SIZE ist eine Konstante, die die maximale Größe der Schrift (Textgröße Anstand in einem Textview)

TEXTVIEW_MAX_HEIGHT definiert eine Konstante ist Das definiert die maximale Größe, die die Textansicht haben kann.

Dies wurde jedes Mal aufgerufen, wenn der Text in der Textansicht geändert wurde.

Die Textview xml war so etwas wie dieses:

<TextView 
    android:id="@+id/textview1" 
    android:layout_width="200dp" 
    android:layout_height="wrap_content" 
    android:singleLine="false" 
    android:inputType="textMultiLine" 
    android:text="" 
    android:textSize="150sp" /> 

Da die Breite in der XML beschränkt wäre, nur die Höhe der Ansicht muss da passt ihn zu betrachten android würde ein mehrzeiliges automatisch erstellen, wenn nötig .

Obwohl dies eine mögliche Lösung ist funktioniert nicht perfekt (weit davon entfernt) und es unterstützt nicht die Größe zu verkleinern (wenn Sie Text löschen).

Irgendwelche Vorschläge und/oder Ideen?

+0

etwas Glück damit? – NaserShaikh

+0

Überprüfen Sie die Antwort unter – nunofmendes

Antwort

15

Während ich auf eine mögliche Lösung für diese Frage wartete, habe ich experimentiert und versucht, es herauszufinden.

Die nächste Lösung für dieses Problem wurde in einer Methode aus Farbe basiert.

Grundsätzlich Lack hat ein Verfahren namens 'BREAK', die:

public int BREAK (CharSequence text, int Start, int Ende, boolean measureForwards, float maxWidth, float [] measuredWidth)

Hinzugefügt in API Ebene 1

Messen Sie den Text und stoppen Sie früh, wenn die gemessene Breite maxWidth überschreitet. Geben Sie die Anzahl der gemessenen Zeichen zurück, und wenn measuredWidth nicht null ist, geben Sie die tatsächlich gemessene Breite zurück.

Ich kombiniere das mit Farbe 'getTextBounds' das:

public void getTextBounds (String text, int Start, int Ende, Rect Grenzen)

Added in API-Ebene 1

Zurückgeben in Grenzen (vom Aufrufer zugewiesen) das kleinste Rechteck, das alle Zeichen> einschließt, mit einem impliziten Ursprung bei (0,0).

So jetzt kann ich die Anzahl der Zeichen erhalten, die in einer bestimmten Breite und der Höhe dieser Zeichen passen.

Mit einem während Sie halten die Entfernung von Zeichen aus dem String bewegen Sie die Anzahl der Zeilen zu messen und zu erhalten (durch einen während der Verwendung (Index < string.length)) und multiplizieren Sie diese mit der Höhe in dem getTextBounds erhalten .

Zusätzlich müssen Sie eine variable Höhe für jeweils zwei Zeilen hinzufügen, die den Abstand zwischen den Zeilen darstellen (der in den getTextBounds nicht berücksichtigt wird).

Als Beispiel-Code, die Funktion, die Höhe eines mehrzeilige Text zu wissen, ist so etwas wie dieses:

public int getHeightOfMultiLineText(String text,int textSize, int maxWidth) { 
    paint = new TextPaint(); 
    paint.setTextSize(textSize); 
    int index = 0; 
    int linecount = 0; 
    while(index < text.length()) { 
     index += paint.breakText(text,index,text.length,true,maxWidth,null); 
     linecount++; 
     } 

    Rect bounds = new Rect(); 
    paint.getTextBounds("Yy", 0, 2, bounds); 
    // obtain space between lines 
    double lineSpacing = Math.max(0,((lineCount - 1) * bounds.height()*0.25)); 

    return (int)Math.floor(lineSpacing + lineCount * bounds.height()); 

Hinweis: maxWidth Variable ist in Pixel

Dann müssen Sie Rufen Sie diese Methode innerhalb einer Weile auf, um zu bestimmen, wie groß die maximale Schriftgröße für diese Höhe ist. Ein Beispiel-Code wäre:

textSize = 100; 
int maxHeight = 50; 
while(getHeightOfMultiLineText(text,textSize,maxWidth) > maxHeight) 
    textSize--; 

Leider war dies die einzige (soweit ich weiß) Art und Weise ich in der Lage war, den Aspekt aus den Bildern oben zu erreichen.

Ich hoffe, dies kann jedem helfen, der versucht, dieses Hindernis zu überwinden.

+0

Sie Kumpel ist ein Lebensretter! +1 –

+0

Was hast du für die maximale Breite gemacht? Sind das Pixel oder Zeichen? – DDukesterman

+0

Es ist in Pixel. – nunofmendes

1

Sie haben eine ziemlich gute Lösung für dieses Problem, aber ich habe es aus einem anderen Blickwinkel betrachtet. Ich denke, dass die Logik zumindest für die neuen Android-Entwickler einfacher und verständlicher ist. Die Idee basiert auf dem Einfügen des '\ n' Zeichens zwischen die Leerzeichen der großen Zeichenfolge. Dann setze ich die bearbeitete Zeichenfolge in der textView als Anzeigetext. So , hier ist der Code ..

private String insertNewLineCharAtString(String str, int step){ 
    StringBuilder sb = new StringBuilder(); 
    int spaceCounter = 0; 
    String[] strArray = str.split(" "); 
    for(int i = 0; i < strArray.length; i++){ 



     if(spaceCounter == step){ 
      sb.append('\n'); 
      sb.append(strArray[i]); 
      spaceCounter = 0; 
     }else{ 
      sb.append(" "+strArray[i]); 
     } 
     spaceCounter++; 


    } 
    return sb.toString(); 
} 

Die Parameter sind einfach.Str ist die Zeichenfolge, die wir bearbeiten werden, und der Variablenschritt definiert, in wie viele Leerzeichen die Methode das Zeichen '/ n' einfügen soll. Dann rufen Sie einfach so etwas wie dieses:

myTextView.setText(insertNewLineCharactersAtString(yourDisplayingStr); 

hoffe, das hilft, viele von Ihnen, dass Sie nicht gehen wollen tiefer mit TextPaint, Rects und mathematische Funktionen ..

-2

Der übersetzbar, festgelegten Code : diese eine copy-Paste, nicht akzeptiert man, weil man es beheben müssen :)

public static int getHeightOfMultiLineText(String text, int textSize, int maxWidth) { 
    TextPaint paint = new TextPaint(); 
    paint.setTextSize(textSize); 
    int index = 0; 
    int lineCount = 0; 
    while (index < text.length()) { 
     index += paint.breakText(text, index, text.length(), true, maxWidth, null); 
     lineCount++; 
    } 

    Rect bounds = new Rect(); 
    paint.getTextBounds("Yy", 0, 2, bounds); 
    // obtain space between lines 
    double lineSpacing = Math.max(0, ((lineCount - 1) * bounds.height() * 0.25)); 

    return (int) Math.floor(lineSpacing + lineCount * bounds.height()); 
} 

verwenden müssen, danke.

Wichtig: Sie müssen den Skalierungsfaktor

int textSize = 50; 
    int maxHeight = boundsTv.height(); 
    while (getHeightOfMultiLineText(text, textSize, params.width) > maxHeight) { 
     textSize--; 
    } 
    float scaleFactor = mainActivity.getResources().getDisplayMetrics().scaledDensity;  
    float temp = 1.2f; 

    tvText.setTextSize((float) (textSize/(temp*scaleFactor))); 
1

Dank für Ihre Lösung Sie sind Übermenschen betrachten! Ich habe Ihren Code in der abgeleiteten Klasse von TextView implementiert. Der Code wird in C# -Code für Xamarin Android konvertiert. Sie können bei Bedarf problemlos in Java konvertieren (entfernen Sie den ersten Konstruktor für Java).

public class AutoFitTextView : TextView 
{ 
    public AutoFitTextView(System.IntPtr javaReference, Android.Runtime.JniHandleOwnership transfer) 
     : base(javaReference, transfer) 
    { 
    } 

    public AutoFitTextView(Context context) 
     : base(context) 
    { 

    } 

    public AutoFitTextView(Context context, IAttributeSet attrs) 
     : base(context, attrs) 
    { 

    } 


    public void ResizeFontSize() 
    { 
     int textSize = 50; 
     int maxHeight = this.Height; 
     while (GetHeightOfMultiLineText(this.Text, textSize, this.Width) > maxHeight) 
     { 
      textSize--; 
     } 
     float scaleFactor = Context.Resources.DisplayMetrics.ScaledDensity; 
     float additionalFactor = 1.2f; 

     TextSize = ((float)(textSize/(additionalFactor * scaleFactor))); 
    } 


    private int GetHeightOfMultiLineText(string text, int textSize, int maxWidth) 
    { 
     TextPaint paint = new TextPaint(); 
     paint.TextSize = textSize; 
     int index = 0; 
     int lineCount = 0; 
     while (index < text.Length) 
     { 
      index += paint.BreakText(text, index, text.Length, true, maxWidth, null); 
      lineCount++; 
     } 

     Rect bounds = new Rect(); 
     paint.GetTextBounds("Yy", 0, 2, bounds); 
     // obtain space between lines 
     double lineSpacing = Math.Max(0, ((lineCount - 1) * bounds.Height() * 0.25)); 

     return (int)Math.Floor(lineSpacing + lineCount * bounds.Height()); 
    } 

} 

Hier AXML Code ist

 <touchtest.AutoFitTextView 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" 
     android:id="@+id/tvLabel2" 
     android:singleLine="false" 
     android:inputType="textMultiLine" 
     android:text="I am here To test this text" /> 
1

Mit the idea from nunofmendes, schrieb ich eine abgeleitete Klasse von Textview, die Autotext ändert die Größe und unterstützt mehrere Linien.

import android.content.Context; 
import android.os.Handler; 
import android.text.Layout; 
import android.text.TextPaint; 
import android.text.TextUtils.TruncateAt; 
import android.util.AttributeSet; 
import android.util.TypedValue; 

public class AutoResizeTextView extends TextView { 

    private Handler measureHandler = new Handler(); 
    private Runnable requestLayout = new Runnable() { 
     @Override 
     public void run() { 
      requestLayout(); 
     } 
    }; 

    public AutoResizeTextView(Context context, AttributeSet attrs, int defStyle) { 
     super(context, attrs, defStyle); 
    } 

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

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

    @Override 
    protected void onMeasure(final int widthMeasureSpec, 
          final int heightMeasureSpec) { 
     super.onMeasure(widthMeasureSpec, heightMeasureSpec); 

     final float maxWidth = getWidth(); 
     final float maxHeight = getHeight(); 
     if (maxWidth < 1.0f || maxHeight < 1.0f) { 
      return; 
     } 

     int index = 0; 
     int lineCount = 0; 
     CharSequence text = getText(); 
     final TextPaint paint = getPaint(); 
     while (index < text.length()) { 
      index += paint.breakText(text, index, text.length(), true, maxWidth, null); 
      lineCount++; 
     } 
     final float height = lineCount * getLineHeight() + (lineCount > 0 ? 
       (lineCount - 1) * paint.getFontSpacing() : 0); 
     if (height > maxHeight) { 
      final float textSize = getTextSize(); 
      setTextSize(TypedValue.COMPLEX_UNIT_PX, (textSize - 1)); 
      measureHandler.post(requestLayout); 
     } 
    } 
} 
0

enter image description hereenter image description hereenter image description here

Etwas bessere Antwort von phamducgiam. Es zeigt Text mit bereits angepasster Größe, nicht Skretchs nach dem Anzeigen. Sieht im Editor hässlich aus, weil textSize 100 gestartet wird, funktioniert aber so wie es ausgeführt werden sollte. Hier ist der Code:

import android.content.Context; 
import android.text.TextPaint; 
import android.util.AttributeSet; 
import android.util.TypedValue; 
import android.widget.TextView; 

public class FontFitTextView extends TextView { 

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

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

    public FontFitTextView(Context context, AttributeSet attrs, int defStyleAttr) { 
     super(context, attrs, defStyleAttr); 
    } 

    @Override 
    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { 
     super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
     // start decreasing font size from 100 
     setTextSize(TypedValue.COMPLEX_UNIT_PX, 100); 

     // calculate dimensions. don't forget about padding 
     final float maxWidth = getWidth()-(getPaddingLeft()+getPaddingRight()); 
     final float maxHeight = getHeight()-(getPaddingTop()+getPaddingBottom()); 

     if (maxWidth < 1.0f || maxHeight < 1.0f) { 
      return; 
     } 

     CharSequence text = getText(); 
     int lineCount = getLineCount(maxWidth, text); 
     float height = getHeight(lineCount); 
     // keep decreasing font size until it fits 
     while (height > maxHeight) { 
      final float textSize = getTextSize(); 
      setTextSize(TypedValue.COMPLEX_UNIT_PX, (textSize - 1)); 
      height = getHeight(getLineCount(maxWidth, getText())); 
     } 
     // show fitted textView 
     requestLayout(); 
    } 

    private float getHeight(int lineCount) { 
     return lineCount * getLineHeight() + (lineCount > 0 ? (lineCount - 1) * getPaint().getFontSpacing() : 0); 
    } 

    private int getLineCount(float maxWidth, CharSequence text) { 
     int lineCount = 0; 
     int index = 0; 
     final TextPaint paint = getPaint(); 
     while (index < text.length()) { 
      index += paint.breakText(text, index, text.length(), true, maxWidth, null); 
      lineCount++; 
     } 
     return lineCount; 
    } 
}