wake-up-neo.com

Automatische Skalierung von TextView-Text, um ihn an Grenzen anzupassen

Ich suche nach einer optimalen Möglichkeit, die Größe von Textumbrüchen in TextView zu ändern, damit er in die Grenzen getHeight und getWidth passt. Ich bin nicht nur auf der Suche nach einer Möglichkeit, den Text umzubrechen. Ich möchte sicherstellen, dass beide Zeilen umgebrochen werden und klein genug sind, um vollständig auf den Bildschirm zu passen.

Ich habe einige Fälle auf StackOverflow gesehen, in denen eine automatische Größenänderung erforderlich war, aber es handelt sich entweder um sehr spezielle Fälle mit Hack-Lösungen oder um Fälle ohne Lösung, in denen das TextView rekursiv neu gezeichnet wird, bis es klein genug ist (was ist) Speicherintensiv und zwingt den Benutzer, den Text bei jeder Rekursion schrittweise zu verkleinern.

Aber ich bin mir sicher, dass jemand da draußen eine gute Lösung gefunden hat, die nichts damit zu tun hat, was ich tue: Schreiben Sie mehrere schwere Routinen, die den Text analysieren und messen, die Größe des Texts ändern und wiederholen, bis eine angemessen kleine Größe gefunden wurde.

Welche Routinen verwendet TextView, um den Text umzubrechen? Könnten diese nicht irgendwie verwendet werden, um vorherzusagen, ob der Text klein genug sein wird?

tl; dr: Gibt es eine Best-Practice-Methode, um die Größe eines TextView automatisch so zu ändern, dass es in die Grenzen getHeight und getWidth passt?

796
Nathan Fig

Seit Juni 2018 unterstützt Android diese Funktion offiziell für Android 4.0 (API-Level 14) und höher.
Check it out at: TextViews automatisch anpassen

Mit Android 8.0 (API-Level 26) und höher :

<?xml version="1.0" encoding="utf-8"?>
<TextView
    Android:layout_width="match_parent"
    Android:layout_height="200dp"
    Android:autoSizeTextType="uniform"
    Android:autoSizeMinTextSize="12sp"
    Android:autoSizeMaxTextSize="100sp"
    Android:autoSizeStepGranularity="2sp" />

Programmatisch:

setAutoSizeTextTypeUniformWithConfiguration(int autoSizeMinTextSize, int autoSizeMaxTextSize, 
        int autoSizeStepGranularity, int unit)

textView.setAutoSizeTextTypeUniformWithConfiguration(
                1, 17, 1, TypedValue.COMPLEX_UNIT_DIP);


Android-Versionen vor Android 8.0 (API-Stufe 26) :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent">

  <TextView
      Android:layout_width="match_parent"
      Android:layout_height="200dp"
      app:autoSizeTextType="uniform"
      app:autoSizeMinTextSize="12sp"
      app:autoSizeMaxTextSize="100sp"
      app:autoSizeStepGranularity="2sp" />

</LinearLayout>

Programmatisch:

TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(
int autoSizeMinTextSize, int autoSizeMaxTextSize, int autoSizeStepGranularity, int unit) 

TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(textView, 1, 17, 1,
TypedValue.COMPLEX_UNIT_DIP);

Achtung: TextView muss layout_width = " match_parent "oder absolute Größe!

86

Als mobiler Entwickler war ich traurig, dass ich nichts Natives gefunden habe, das die automatische Größenänderung unterstützt. Meine Suche hat nichts ergeben, was für mich funktioniert hat, und am Ende habe ich die bessere Hälfte meines Wochenendes verbracht und meine eigene Textansicht mit automatischer Größenänderung erstellt. Ich werde den Code hier posten und hoffe, dass er für jemand anderen nützlich ist.

Diese Klasse verwendet ein statisches Layout mit dem Text Paint der ursprünglichen Textansicht, um die Höhe zu messen. Von dort trete ich um 2 Schriftpixel zurück und messe erneut, bis ich eine Größe habe, die passt. Wenn der Text immer noch nicht passt, füge ich am Ende eine Ellipse hinzu. Ich hatte Anforderungen, um den Text zu animieren und Ansichten wiederzuverwenden, und dies scheint auf den Geräten, die ich habe, gut zu funktionieren und für mich schnell genug zu laufen.

/**
 *               DO WHAT YOU WANT TO PUBLIC LICENSE
 *                    Version 2, December 2004
 * 
 * Copyright (C) 2004 Sam Hocevar <[email protected]>
 * 
 * Everyone is permitted to copy and distribute verbatim or modified
 * copies of this license document, and changing it is allowed as long
 * as the name is changed.
 * 
 *            DO WHAT YOU WANT TO PUBLIC LICENSE
 *   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
 * 
 *  0. You just DO WHAT YOU WANT TO.
 */

import Android.content.Context;
import Android.text.Layout.Alignment;
import Android.text.StaticLayout;
import Android.text.TextPaint;
import Android.util.AttributeSet;
import Android.util.TypedValue;
import Android.widget.TextView;

/**
 * Text view that auto adjusts text size to fit within the view.
 * If the text size equals the minimum text size and still does not
 * fit, append with an Ellipsis.
 * 
 * @author Chase Colburn
 * @since Apr 4, 2011
 */
public class AutoResizeTextView extends TextView {

    // Minimum text size for this text view
    public static final float MIN_TEXT_SIZE = 20;

    // Interface for resize notifications
    public interface OnTextResizeListener {
        public void onTextResize(TextView textView, float oldSize, float newSize);
    }

    // Our ellipse string
    private static final String mEllipsis = "...";

    // Registered resize listener
    private OnTextResizeListener mTextResizeListener;

    // Flag for text and/or size changes to force a resize
    private boolean mNeedsResize = false;

    // Text size that is set from code. This acts as a starting point for resizing
    private float mTextSize;

    // Temporary upper bounds on the starting text size
    private float mMaxTextSize = 0;

    // Lower bounds for text size
    private float mMinTextSize = MIN_TEXT_SIZE;

    // Text view line spacing multiplier
    private float mSpacingMult = 1.0f;

    // Text view additional line spacing
    private float mSpacingAdd = 0.0f;

    // Add Ellipsis to text that overflows at the smallest text size
    private boolean mAddEllipsis = true;

    // Default constructor override
    public AutoResizeTextView(Context context) {
        this(context, null);
    }

    // Default constructor when inflating from XML file
    public AutoResizeTextView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    // Default constructor override
    public AutoResizeTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mTextSize = getTextSize();
    }

    /**
     * When text changes, set the force resize flag to true and reset the text size.
     */
    @Override
    protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) {
        mNeedsResize = true;
        // Since this view may be reused, it is good to reset the text size
        resetTextSize();
    }

    /**
     * If the text view size changed, set the force resize flag to true
     */
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        if (w != oldw || h != oldh) {
            mNeedsResize = true;
        }
    }

    /**
     * Register listener to receive resize notifications
     * @param listener
     */
    public void setOnResizeListener(OnTextResizeListener listener) {
        mTextResizeListener = listener;
    }

    /**
     * Override the set text size to update our internal reference values
     */
    @Override
    public void setTextSize(float size) {
        super.setTextSize(size);
        mTextSize = getTextSize();
    }

    /**
     * Override the set text size to update our internal reference values
     */
    @Override
    public void setTextSize(int unit, float size) {
        super.setTextSize(unit, size);
        mTextSize = getTextSize();
    }

    /**
     * Override the set line spacing to update our internal reference values
     */
    @Override
    public void setLineSpacing(float add, float mult) {
        super.setLineSpacing(add, mult);
        mSpacingMult = mult;
        mSpacingAdd = add;
    }

    /**
     * Set the upper text size limit and invalidate the view
     * @param maxTextSize
     */
    public void setMaxTextSize(float maxTextSize) {
        mMaxTextSize = maxTextSize;
        requestLayout();
        invalidate();
    }

    /**
     * Return upper text size limit
     * @return
     */
    public float getMaxTextSize() {
        return mMaxTextSize;
    }

    /**
     * Set the lower text size limit and invalidate the view
     * @param minTextSize
     */
    public void setMinTextSize(float minTextSize) {
        mMinTextSize = minTextSize;
        requestLayout();
        invalidate();
    }

    /**
     * Return lower text size limit
     * @return
     */
    public float getMinTextSize() {
        return mMinTextSize;
    }

    /**
     * Set flag to add Ellipsis to text that overflows at the smallest text size
     * @param addEllipsis
     */
    public void setAddEllipsis(boolean addEllipsis) {
        mAddEllipsis = addEllipsis;
    }

    /**
     * Return flag to add Ellipsis to text that overflows at the smallest text size
     * @return
     */
    public boolean getAddEllipsis() {
        return mAddEllipsis;
    }

    /**
     * Reset the text to the original size
     */
    public void resetTextSize() {
        if (mTextSize > 0) {
            super.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);
            mMaxTextSize = mTextSize;
        }
    }

    /**
     * Resize text after measuring
     */
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        if (changed || mNeedsResize) {
            int widthLimit = (right - left) - getCompoundPaddingLeft() - getCompoundPaddingRight();
            int heightLimit = (bottom - top) - getCompoundPaddingBottom() - getCompoundPaddingTop();
            resizeText(widthLimit, heightLimit);
        }
        super.onLayout(changed, left, top, right, bottom);
    }

    /**
     * Resize the text size with default width and height
     */
    public void resizeText() {

        int heightLimit = getHeight() - getPaddingBottom() - getPaddingTop();
        int widthLimit = getWidth() - getPaddingLeft() - getPaddingRight();
        resizeText(widthLimit, heightLimit);
    }

    /**
     * Resize the text size with specified width and height
     * @param width
     * @param height
     */
    public void resizeText(int width, int height) {
        CharSequence text = getText();
        // Do not resize if the view does not have dimensions or there is no text
        if (text == null || text.length() == 0 || height <= 0 || width <= 0 || mTextSize == 0) {
            return;
        }

        if (getTransformationMethod() != null) {
            text = getTransformationMethod().getTransformation(text, this);
        }

        // Get the text view's Paint object
        TextPaint textPaint = getPaint();

        // Store the current text size
        float oldTextSize = textPaint.getTextSize();
        // If there is a max text size set, use the lesser of that and the default text size
        float targetTextSize = mMaxTextSize > 0 ? Math.min(mTextSize, mMaxTextSize) : mTextSize;

        // Get the required text height
        int textHeight = getTextHeight(text, textPaint, width, targetTextSize);

        // Until we either fit within our text view or we had reached our min text size, incrementally try smaller sizes
        while (textHeight > height && targetTextSize > mMinTextSize) {
            targetTextSize = Math.max(targetTextSize - 2, mMinTextSize);
            textHeight = getTextHeight(text, textPaint, width, targetTextSize);
        }

        // If we had reached our minimum text size and still don't fit, append an Ellipsis
        if (mAddEllipsis && targetTextSize == mMinTextSize && textHeight > height) {
            // Draw using a static layout
            // modified: use a copy of TextPaint for measuring
            TextPaint Paint = new TextPaint(textPaint);
            // Draw using a static layout
            StaticLayout layout = new StaticLayout(text, Paint, width, Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, false);
            // Check that we have a least one line of rendered text
            if (layout.getLineCount() > 0) {
                // Since the line at the specific vertical position would be cut off,
                // we must trim up to the previous line
                int lastLine = layout.getLineForVertical(height) - 1;
                // If the text would not even fit on a single line, clear it
                if (lastLine < 0) {
                    setText("");
                }
                // Otherwise, trim to the previous line and add an Ellipsis
                else {
                    int start = layout.getLineStart(lastLine);
                    int end = layout.getLineEnd(lastLine);
                    float lineWidth = layout.getLineWidth(lastLine);
                    float ellipseWidth = textPaint.measureText(mEllipsis);

                    // Trim characters off until we have enough room to draw the Ellipsis
                    while (width < lineWidth + ellipseWidth) {
                        lineWidth = textPaint.measureText(text.subSequence(start, --end + 1).toString());
                    }
                    setText(text.subSequence(0, end) + mEllipsis);
                }
            }
        }

        // Some devices try to auto adjust line spacing, so force default line spacing
        // and invalidate the layout as a side effect
        setTextSize(TypedValue.COMPLEX_UNIT_PX, targetTextSize);
        setLineSpacing(mSpacingAdd, mSpacingMult);

        // Notify the listener if registered
        if (mTextResizeListener != null) {
            mTextResizeListener.onTextResize(this, oldTextSize, targetTextSize);
        }

        // Reset force resize flag
        mNeedsResize = false;
    }

    // Set the text size of the text Paint object and use a static layout to render text off screen before measuring
    private int getTextHeight(CharSequence source, TextPaint Paint, int width, float textSize) {
        // modified: make a copy of the original TextPaint object for measuring
        // (apparently the object gets modified while measuring, see also the
        // docs for TextView.getPaint() (which states to access it read-only)
        TextPaint paintCopy = new TextPaint(Paint);
        // Update the text Paint object
        paintCopy.setTextSize(textSize);
        // Measure using a static layout
        StaticLayout layout = new StaticLayout(source, paintCopy, width, Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, true);
        return layout.getHeight();
    }

}

Warnung. Es gibt einen wichtigen behobenen Fehler, der Android 3.1 - 4.04 betrifft und dazu führt, dass alle AutoResizingTextView-Widgets nicht funktionieren. Bitte lesen Sie: https://stackoverflow.com/a/21851157/2075875

1093
Chase

UPDATE: Der folgende Code erfüllt auch die Anforderung eines idealen AutoScaleTextView wie beschrieben hier: Auto-Fit TextView für Android und wird als Gewinner markiert.

UPDATE 2: Unterstützung von Maxlines hinzugefügt, funktioniert jetzt auch vor API Level 16.

Update 3: Unterstützung für die Tags Android:drawableLeft, Android:drawableRight, Android:drawableTop und Android:drawableBottom hinzugefügt, danke zu MartinHs einfachem Fix hier .


Meine Anforderungen waren etwas anders. Ich brauchte eine effiziente Methode zum Anpassen der Größe, da ich eine ganze Zahl von 0 bis ~ 4000 in TextView in 2 Sekunden animierte, und ich wollte die Größe entsprechend anpassen. Meine Lösung funktioniert etwas anders. So sieht das Endergebnis aus:

enter image description here

und der Code, der es erzeugt hat:

<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:orientation="vertical"
    Android:padding="16dp" >

    <com.vj.widgets.AutoResizeTextView
        Android:layout_width="match_parent"
        Android:layout_height="100dp"
        Android:ellipsize="none"
        Android:maxLines="2"
        Android:text="Auto Resized Text, max 2 lines"
        Android:textSize="100sp" /> <!-- maximum size -->

    <com.vj.widgets.AutoResizeTextView
        Android:layout_width="match_parent"
        Android:layout_height="100dp"
        Android:ellipsize="none"
        Android:gravity="center"
        Android:maxLines="1"
        Android:text="Auto Resized Text, max 1 line"
        Android:textSize="100sp" /> <!-- maximum size -->

    <com.vj.widgets.AutoResizeTextView
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:text="Auto Resized Text"
        Android:textSize="500sp" /> <!-- maximum size -->

</LinearLayout>

Und zum Schluss der Java Code:

import Android.annotation.TargetApi;
import Android.content.Context;
import Android.content.res.Resources;
import Android.graphics.RectF;
import Android.os.Build;
import Android.text.Layout.Alignment;
import Android.text.StaticLayout;
import Android.text.TextPaint;
import Android.util.AttributeSet;
import Android.util.SparseIntArray;
import Android.util.TypedValue;
import Android.widget.TextView;

public class AutoResizeTextView extends TextView {
private interface SizeTester {
    /**
     * 
     * @param suggestedSize
     *            Size of text to be tested
     * @param availableSpace
     *            available space in which text must fit
     * @return an integer < 0 if after applying {@code suggestedSize} to
     *         text, it takes less space than {@code availableSpace}, > 0
     *         otherwise
     */
    public int onTestSize(int suggestedSize, RectF availableSpace);
}

private RectF mTextRect = new RectF();

private RectF mAvailableSpaceRect;

private SparseIntArray mTextCachedSizes;

private TextPaint mPaint;

private float mMaxTextSize;

private float mSpacingMult = 1.0f;

private float mSpacingAdd = 0.0f;

private float mMinTextSize = 20;

private int mWidthLimit;

private static final int NO_LINE_LIMIT = -1;
private int mMaxLines;

private boolean mEnableSizeCache = true;
private boolean mInitiallized;

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

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

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

private void initialize() {
    mPaint = new TextPaint(getPaint());
    mMaxTextSize = getTextSize();
    mAvailableSpaceRect = new RectF();
    mTextCachedSizes = new SparseIntArray();
    if (mMaxLines == 0) {
        // no value was assigned during construction
        mMaxLines = NO_LINE_LIMIT;
    }
    mInitiallized = true;
}

@Override
public void setText(final CharSequence text, BufferType type) {
    super.setText(text, type);
    adjustTextSize(text.toString());
}

@Override
public void setTextSize(float size) {
    mMaxTextSize = size;
    mTextCachedSizes.clear();
    adjustTextSize(getText().toString());
}

@Override
public void setMaxLines(int maxlines) {
    super.setMaxLines(maxlines);
    mMaxLines = maxlines;
    reAdjust();
}

public int getMaxLines() {
    return mMaxLines;
}

@Override
public void setSingleLine() {
    super.setSingleLine();
    mMaxLines = 1;
    reAdjust();
}

@Override
public void setSingleLine(boolean singleLine) {
    super.setSingleLine(singleLine);
    if (singleLine) {
        mMaxLines = 1;
    } else {
        mMaxLines = NO_LINE_LIMIT;
    }
    reAdjust();
}

@Override
public void setLines(int lines) {
    super.setLines(lines);
    mMaxLines = lines;
    reAdjust();
}

@Override
public void setTextSize(int unit, float size) {
    Context c = getContext();
    Resources r;

    if (c == null)
        r = Resources.getSystem();
    else
        r = c.getResources();
    mMaxTextSize = TypedValue.applyDimension(unit, size,
            r.getDisplayMetrics());
    mTextCachedSizes.clear();
    adjustTextSize(getText().toString());
}

@Override
public void setLineSpacing(float add, float mult) {
    super.setLineSpacing(add, mult);
    mSpacingMult = mult;
    mSpacingAdd = add;
}

/**
 * Set the lower text size limit and invalidate the view
 * 
 * @param minTextSize
 */
public void setMinTextSize(float minTextSize) {
    mMinTextSize = minTextSize;
    reAdjust();
}

private void reAdjust() {
    adjustTextSize(getText().toString());
}

private void adjustTextSize(String string) {
    if (!mInitiallized) {
        return;
    }
    int startSize = (int) mMinTextSize;
    int heightLimit = getMeasuredHeight() - getCompoundPaddingBottom()
        - getCompoundPaddingTop();
    mWidthLimit = getMeasuredWidth() - getCompoundPaddingLeft()
        - getCompoundPaddingRight();
    mAvailableSpaceRect.right = mWidthLimit;
    mAvailableSpaceRect.bottom = heightLimit;
    super.setTextSize(
            TypedValue.COMPLEX_UNIT_PX,
            efficientTextSizeSearch(startSize, (int) mMaxTextSize,
                    mSizeTester, mAvailableSpaceRect));
}

private final SizeTester mSizeTester = new SizeTester() {
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    @Override
    public int onTestSize(int suggestedSize, RectF availableSPace) {
        mPaint.setTextSize(suggestedSize);
        String text = getText().toString();
        boolean singleline = getMaxLines() == 1;
        if (singleline) {
            mTextRect.bottom = mPaint.getFontSpacing();
            mTextRect.right = mPaint.measureText(text);
        } else {
            StaticLayout layout = new StaticLayout(text, mPaint,
                    mWidthLimit, Alignment.ALIGN_NORMAL, mSpacingMult,
                    mSpacingAdd, true);
            // return early if we have more lines
            if (getMaxLines() != NO_LINE_LIMIT
                    && layout.getLineCount() > getMaxLines()) {
                return 1;
            }
            mTextRect.bottom = layout.getHeight();
            int maxWidth = -1;
            for (int i = 0; i < layout.getLineCount(); i++) {
                if (maxWidth < layout.getLineWidth(i)) {
                    maxWidth = (int) layout.getLineWidth(i);
                }
            }
            mTextRect.right = maxWidth;
        }

        mTextRect.offsetTo(0, 0);
        if (availableSPace.contains(mTextRect)) {
            // may be too small, don't worry we will find the best match
            return -1;
        } else {
            // too big
            return 1;
        }
    }
};

/**
 * Enables or disables size caching, enabling it will improve performance
 * where you are animating a value inside TextView. This stores the font
 * size against getText().length() Be careful though while enabling it as 0
 * takes more space than 1 on some fonts and so on.
 * 
 * @param enable
 *            enable font size caching
 */
public void enableSizeCache(boolean enable) {
    mEnableSizeCache = enable;
    mTextCachedSizes.clear();
    adjustTextSize(getText().toString());
}

private int efficientTextSizeSearch(int start, int end,
        SizeTester sizeTester, RectF availableSpace) {
    if (!mEnableSizeCache) {
        return binarySearch(start, end, sizeTester, availableSpace);
    }
    String text = getText().toString();
    int key = text == null ? 0 : text.length();
    int size = mTextCachedSizes.get(key);
    if (size != 0) {
        return size;
    }
    size = binarySearch(start, end, sizeTester, availableSpace);
    mTextCachedSizes.put(key, size);
    return size;
}

private static int binarySearch(int start, int end, SizeTester sizeTester,
        RectF availableSpace) {
    int lastBest = start;
    int lo = start;
    int hi = end - 1;
    int mid = 0;
    while (lo <= hi) {
        mid = (lo + hi) >>> 1;
        int midValCmp = sizeTester.onTestSize(mid, availableSpace);
        if (midValCmp < 0) {
            lastBest = lo;
            lo = mid + 1;
        } else if (midValCmp > 0) {
            hi = mid - 1;
            lastBest = hi;
        } else {
            return mid;
        }
    }
    // make sure to return last best
    // this is what should always be returned
    return lastBest;

}

@Override
protected void onTextChanged(final CharSequence text, final int start,
        final int before, final int after) {
    super.onTextChanged(text, start, before, after);
    reAdjust();
}

@Override
protected void onSizeChanged(int width, int height, int oldwidth,
        int oldheight) {
    mTextCachedSizes.clear();
    super.onSizeChanged(width, height, oldwidth, oldheight);
    if (width != oldwidth || height != oldheight) {
        reAdjust();
    }
}
}
143
M-WaJeEh

Tatsächlich befindet sich eine Lösung in der Klasse DialogTitle von Google ... obwohl sie nicht so effektiv ist wie die akzeptierte, ist sie viel einfacher und lässt sich leicht anpassen.

public class SingleLineTextView extends TextView {

  public SingleLineTextView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    setSingleLine();
    setEllipsize(TruncateAt.END);
  }

  public SingleLineTextView(Context context, AttributeSet attrs) {
    super(context, attrs);
    setSingleLine();
    setEllipsize(TruncateAt.END);
  }

  public SingleLineTextView(Context context) {
    super(context);
    setSingleLine();
    setEllipsize(TruncateAt.END);
  }

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

    final Layout layout = getLayout();
    if (layout != null) {
      final int lineCount = layout.getLineCount();
      if (lineCount > 0) {
        final int ellipsisCount = layout.getEllipsisCount(lineCount - 1);
        if (ellipsisCount > 0) {

          final float textSize = getTextSize();

          // textSize is already expressed in pixels
          setTextSize(TypedValue.COMPLEX_UNIT_PX, (textSize - 1));

          super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
      }
    }
  }

}
40
crios

Ich habe mit der Lösung von Chase begonnen, musste aber zwei Dinge anpassen, bevor sie auf meinem Gerät (Galaxy Nexus, Android 4.1) wie erwartet funktioniert:

  1. verwenden einer Kopie von TextPaint zum Messen des Layouts In der Dokumentation zu TextView.getPaint () ist festgelegt, dass diese schreibgeschützt sein soll. Daher habe ich an beiden Stellen, an denen wir das Paint-Objekt zum Messen verwenden, eine Kopie erstellt:

    // 1. in resizeText()
    if (mAddEllipsis && targetTextSize == mMinTextSize && textHeight > height) {
      // Draw using a static layout
      // modified: use a copy of TextPaint for measuring
      TextPaint Paint = new TextPaint(textPaint);
    
    // 2. in getTextHeight()
    private int getTextHeight(CharSequence source, TextPaint originalPaint, int width, float textSize) {
      // modified: make a copy of the original TextPaint object for measuring
      // (apparently the object gets modified while measuring, see also the
      // docs for TextView.getPaint() (which states to access it read-only)
      TextPaint Paint = new TextPaint(originalPaint);
      // Update the text Paint object
      Paint.setTextSize(textSize);
      ...
    
  2. hinzufügen einer Einheit zum Einstellen der Textgröße

    // modified: setting text size via this.setTextSize (instead of textPaint.setTextSize(targetTextSize))
    setTextSize(TypedValue.COMPLEX_UNIT_PX, targetTextSize);
    setLineSpacing(mSpacingAdd, mSpacingMult);
    

Mit diesen beiden Modifikationen funktioniert die Lösung perfekt für mich, danke Chase! Ich weiß nicht, ob es an Android 4.x liegt, dass die ursprüngliche Lösung nicht funktioniert hat. Wenn Sie es in Aktion sehen oder testen möchten, ob es wirklich auf Ihrem Gerät funktioniert, können Sie sich meine Karteikarten-App ansehen Flashcards ToGo wobei ich diese Lösung verwende, um den Text einer Karteikarte zu skalieren. Der Text kann eine beliebige Länge haben und die Karteikarten werden in verschiedenen Aktivitäten angezeigt, manchmal kleiner, manchmal größer, plus im Quer- und Hochformat-Modus. Ich habe keinen Eckfall gefunden, in dem die Lösung nicht richtig funktionieren würde.

36
onoelle

Ich habe mit der AutoResizeTextView-Klasse von Chase begonnen und eine geringfügige Änderung vorgenommen, damit sie sowohl vertikal als auch horizontal passt.

Ich habe auch einen Fehler entdeckt, der im Layout-Editor (in Eclipse) unter einigen eher unklaren Bedingungen eine Nullzeiger-Ausnahme verursacht.

Änderung 1: Passen Sie den Text sowohl vertikal als auch horizontal an

In der Originalversion von Chase wird die Textgröße reduziert, bis sie vertikal passt, der Text jedoch breiter als das Ziel ist. In meinem Fall musste der Text eine bestimmte Breite haben.

Durch diese Änderung wird die Größe geändert, bis der Text sowohl vertikal als auch horizontal passt.

In resizeText( int , int ) ändern von:

// Get the required text height
int textHeight = getTextHeight(text, textPaint, width, targetTextSize);

// Until we either fit within our text view or we had reached our min text size, incrementally try smaller sizes
while(textHeight > height && targetTextSize > mMinTextSize) {
    targetTextSize = Math.max(targetTextSize - 2, mMinTextSize);
    textHeight = getTextHeight(text, textPaint, width, targetTextSize);
    }

zu:

// Get the required text height
int textHeight = getTextHeight(text, textPaint, width, targetTextSize);
int textWidth  = getTextWidth(text, textPaint, width, targetTextSize);

// Until we either fit within our text view or we had reached our min text size, incrementally try smaller sizes
while(((textHeight >= height) || (textWidth >= width) ) && targetTextSize > mMinTextSize) {
    targetTextSize = Math.max(targetTextSize - 2, mMinTextSize);
    textHeight = getTextHeight(text, textPaint, width, targetTextSize);
    textWidth  = getTextWidth(text, textPaint, width, targetTextSize);
    }

Fügen Sie dann am Ende der Datei die Routine getTextWidth() an. Es ist nur eine leicht modifizierte getTextHeight(). Es wäre wahrscheinlich effizienter, sie zu einer Routine zu kombinieren, die sowohl Höhe als auch Breite zurückgibt.

// Set the text size of the text Paint object and use a static layout to render text off screen before measuring
private int getTextWidth(CharSequence source, TextPaint Paint, int width, float textSize) {
    // Update the text Paint object
    Paint.setTextSize(textSize);
    // Draw using a static layout
    StaticLayout layout = new StaticLayout(source, Paint, width, Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, true);
    layout.draw(sTextResizeCanvas);
    return layout.getWidth();
}  




Änderung 2: Beheben einer EmptyStackException im Eclipse Android Layout-Editor

Unter eher unklaren und sehr genauen Bedingungen zeigt der Layout-Editor die grafische Darstellung des Layouts nicht an. Es wird eine "EmptyStackException: null" -Ausnahme in com.Android.ide.Eclipse.adt ausgelöst.

Die erforderlichen Bedingungen sind:
- Erstellen Sie ein AutoResizeTextView-Widget
- Erstellt einen Stil für dieses Widget
- Geben Sie das Textelement im Stil an. nicht in der Widget-Definition

wie in:

res/layout/main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="fill_parent"
    Android:layout_height="fill_parent"
    Android:orientation="vertical" >

    <com.ajw.DemoCrashInADT.AutoResizeTextView
        Android:id="@+id/resizingText"
        style="@style/myTextStyle" />

</LinearLayout>

res/values ​​/ myStyles.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <style name="myTextStyle" parent="@Android:style/Widget.TextView">
        <item name="Android:layout_height">wrap_content</item>
        <item name="Android:layout_width">fill_parent</item>
        <item name="Android:text">some message</item>
    </style>

</resources>

Wenn Sie in diesen Dateien die Registerkarte Grafisches Layout auswählen, wird beim Bearbeiten von main.xml Folgendes angezeigt:

error!
EmptyStackException: null
Ausnahmedetails werden in Fenster> Ansicht anzeigen> Fehlerprotokoll protokolliert

anstelle der grafischen Ansicht des Layouts.

Um eine bereits zu lange Geschichte kürzer zu halten, habe ich sie in den folgenden Zeilen nachverfolgt (wiederum in resizeText):

// If there is a max text size set, use the lesser of that and the default text size
float targetTextSize = mMaxTextSize > 0 ? Math.min(mTextSize, mMaxTextSize) : mTextSize;

Das Problem ist, dass mTextSize unter bestimmten Bedingungen niemals initialisiert wird. es hat den Wert 0.

Damit wird targetTextSize auf Null gesetzt (als Ergebnis von Math.min).

Diese Null wird an getTextHeight() (und getTextWidth()) als Argument textSize übergeben. Wenn es soweit ist
layout.draw(sTextResizeCanvas);
wir bekommen die Ausnahme.

Es ist effizienter zu testen, ob (mTextSize == 0) am Anfang von resizeText() steht, als in getTextHeight() und getTextWidth() zu testen. Ein früheres Testen erspart alle zwischenzeitlichen Arbeiten.

Mit diesen Updates lautet die Datei (wie in meiner Crash-Demo-Test-App) jetzt:

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

package com.ajw.DemoCrashInADT;

import Android.content.Context;
import Android.graphics.Canvas;
import Android.text.Layout.Alignment;
import Android.text.StaticLayout;
import Android.text.TextPaint;
import Android.util.AttributeSet;
import Android.util.TypedValue;
import Android.widget.TextView;

/**
 * Text view that auto adjusts text size to fit within the view. If the text
 * size equals the minimum text size and still does not fit, append with an
 * Ellipsis.
 *
 * 2011-10-29 changes by Alan Jay Weiner
 *              * change to fit both vertically and horizontally  
 *              * test mTextSize for 0 in resizeText() to fix exception in Layout Editor
 *
 * @author Chase Colburn
 * @since Apr 4, 2011
 */
public class AutoResizeTextView extends TextView {

    // Minimum text size for this text view
    public static final float MIN_TEXT_SIZE = 20;

    // Interface for resize notifications
    public interface OnTextResizeListener {
        public void onTextResize(TextView textView, float oldSize, float newSize);
    }

    // Off screen canvas for text size rendering
    private static final Canvas sTextResizeCanvas = new Canvas();

    // Our ellipse string
    private static final String mEllipsis = "...";

    // Registered resize listener
    private OnTextResizeListener mTextResizeListener;

    // Flag for text and/or size changes to force a resize
    private boolean mNeedsResize = false;

    // Text size that is set from code. This acts as a starting point for
    // resizing
    private float mTextSize;

    // Temporary upper bounds on the starting text size
    private float mMaxTextSize = 0;

    // Lower bounds for text size
    private float mMinTextSize = MIN_TEXT_SIZE;

    // Text view line spacing multiplier
    private float mSpacingMult = 1.0f;

    // Text view additional line spacing
    private float mSpacingAdd = 0.0f;

    // Add Ellipsis to text that overflows at the smallest text size
    private boolean mAddEllipsis = true;


    // Default constructor override
    public AutoResizeTextView(Context context) {
        this(context, null);
    }


    // Default constructor when inflating from XML file
    public AutoResizeTextView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }


    // Default constructor override
    public AutoResizeTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mTextSize = getTextSize();
    }


    /**
     * When text changes, set the force resize flag to true and reset the text
     * size.
     */
    @Override
    protected void onTextChanged(final CharSequence text, final int start,
            final int before, final int after) {
        mNeedsResize = true;
        // Since this view may be reused, it is good to reset the text size
        resetTextSize();
    }


    /**
     * If the text view size changed, set the force resize flag to true
     */
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        if (w != oldw || h != oldh) {
            mNeedsResize = true;
        }
    }


    /**
     * Register listener to receive resize notifications
     *
     * @param listener
     */
    public void setOnResizeListener(OnTextResizeListener listener) {
        mTextResizeListener = listener;
    }


    /**
     * Override the set text size to update our internal reference values
     */
    @Override
    public void setTextSize(float size) {
        super.setTextSize(size);
        mTextSize = getTextSize();
    }


    /**
     * Override the set text size to update our internal reference values
     */
    @Override
    public void setTextSize(int unit, float size) {
        super.setTextSize(unit, size);
        mTextSize = getTextSize();
    }


    /**
     * Override the set line spacing to update our internal reference values
     */
    @Override
    public void setLineSpacing(float add, float mult) {
        super.setLineSpacing(add, mult);
        mSpacingMult = mult;
        mSpacingAdd = add;
    }


    /**
     * Set the upper text size limit and invalidate the view
     *
     * @param maxTextSize
     */
    public void setMaxTextSize(float maxTextSize) {
        mMaxTextSize = maxTextSize;
        requestLayout();
        invalidate();
    }


    /**
     * Return upper text size limit
     *
     * @return
     */
    public float getMaxTextSize() {
        return mMaxTextSize;
    }


    /**
     * Set the lower text size limit and invalidate the view
     *
     * @param minTextSize
     */
    public void setMinTextSize(float minTextSize) {
        mMinTextSize = minTextSize;
        requestLayout();
        invalidate();
    }


    /**
     * Return lower text size limit
     *
     * @return
     */
    public float getMinTextSize() {
        return mMinTextSize;
    }


    /**
     * Set flag to add Ellipsis to text that overflows at the smallest text size
     *
     * @param addEllipsis
     */
    public void setAddEllipsis(boolean addEllipsis) {
        mAddEllipsis = addEllipsis;
    }


    /**
     * Return flag to add Ellipsis to text that overflows at the smallest text
     * size
     *
     * @return
     */
    public boolean getAddEllipsis() {
        return mAddEllipsis;
    }


    /**
     * Reset the text to the original size
     */
    public void resetTextSize() {
        super.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);
        mMaxTextSize = mTextSize;
    }


    /**
     * Resize text after measuring
     */
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        if (changed || mNeedsResize) {
            int widthLimit = (right - left) - getCompoundPaddingLeft()
                    - getCompoundPaddingRight();
            int heightLimit = (bottom - top) - getCompoundPaddingBottom()
                    - getCompoundPaddingTop();
            resizeText(widthLimit, heightLimit);
        }
        super.onLayout(changed, left, top, right, bottom);
    }


    /**
     * Resize the text size with default width and height
     */
    public void resizeText() {
        int heightLimit = getHeight() - getPaddingBottom() - getPaddingTop();
        int widthLimit = getWidth() - getPaddingLeft() - getPaddingRight();
        resizeText(widthLimit, heightLimit);
    }


    /**
     * Resize the text size with specified width and height
     *
     * @param width
     * @param height
     */
    public void resizeText(int width, int height) {
        CharSequence text = getText();
        // Do not resize if the view does not have dimensions or there is no
        // text
        // or if mTextSize has not been initialized
        if (text == null || text.length() == 0 || height <= 0 || width <= 0
                || mTextSize == 0) {
            return;
        }

        // Get the text view's Paint object
        TextPaint textPaint = getPaint();

        // Store the current text size
        float oldTextSize = textPaint.getTextSize();

        // If there is a max text size set, use the lesser of that and the
        // default text size
        float targetTextSize = mMaxTextSize > 0 ? Math.min(mTextSize, mMaxTextSize)
                : mTextSize;

        // Get the required text height
        int textHeight = getTextHeight(text, textPaint, width, targetTextSize);
        int textWidth = getTextWidth(text, textPaint, width, targetTextSize);

        // Until we either fit within our text view or we had reached our min
        // text size, incrementally try smaller sizes
        while (((textHeight > height) || (textWidth > width))
                && targetTextSize > mMinTextSize) {
            targetTextSize = Math.max(targetTextSize - 2, mMinTextSize);
            textHeight = getTextHeight(text, textPaint, width, targetTextSize);
            textWidth = getTextWidth(text, textPaint, width, targetTextSize);
        }

        // If we had reached our minimum text size and still don't fit, append
        // an Ellipsis
        if (mAddEllipsis && targetTextSize == mMinTextSize && textHeight > height) {
            // Draw using a static layout
            StaticLayout layout = new StaticLayout(text, textPaint, width,
                    Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, false);
            layout.draw(sTextResizeCanvas);
            int lastLine = layout.getLineForVertical(height) - 1;
            int start = layout.getLineStart(lastLine);
            int end = layout.getLineEnd(lastLine);
            float lineWidth = layout.getLineWidth(lastLine);
            float ellipseWidth = textPaint.measureText(mEllipsis);

            // Trim characters off until we have enough room to draw the
            // Ellipsis
            while (width < lineWidth + ellipseWidth) {
                lineWidth = textPaint.measureText(text.subSequence(start, --end + 1)
                        .toString());
            }
            setText(text.subSequence(0, end) + mEllipsis);

        }

        // Some devices try to auto adjust line spacing, so force default line
        // spacing
        // and invalidate the layout as a side effect
        textPaint.setTextSize(targetTextSize);
        setLineSpacing(mSpacingAdd, mSpacingMult);

        // Notify the listener if registered
        if (mTextResizeListener != null) {
            mTextResizeListener.onTextResize(this, oldTextSize, targetTextSize);
        }

        // Reset force resize flag
        mNeedsResize = false;
    }


    // Set the text size of the text Paint object and use a static layout to
    // render text off screen before measuring
    private int getTextHeight(CharSequence source, TextPaint Paint, int width,
            float textSize) {
        // Update the text Paint object
        Paint.setTextSize(textSize);
        // Draw using a static layout
        StaticLayout layout = new StaticLayout(source, Paint, width,
                Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, true);
        layout.draw(sTextResizeCanvas);
        return layout.getHeight();
    }


    // Set the text size of the text Paint object and use a static layout to
    // render text off screen before measuring
    private int getTextWidth(CharSequence source, TextPaint Paint, int width,
            float textSize) {
        // Update the text Paint object
        Paint.setTextSize(textSize);
        // Draw using a static layout
        StaticLayout layout = new StaticLayout(source, Paint, width,
                Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, true);
        layout.draw(sTextResizeCanvas);
        return layout.getWidth();
    }

}



Ein großes Dankeschön an Chase für die Veröffentlichung des ursprünglichen Codes. Ich habe es genossen, es durchzulesen, um zu sehen, wie es funktioniert, und ich freue mich, es ergänzen zu können.

22
Alan Jay Weiner

Eine Problemumgehung für Android 4.x:

Ich habe AutoResizeTextView gefunden und es funktioniert hervorragend mit meinem Android 2.1-Emulator. Ich habe es so geliebt. Aber leider ist es auf meinem eigenen 4.0.4 Handy und 4.1 Emulator gescheitert. Nachdem ich es ausprobiert hatte, stellte ich fest, dass es leicht gelöst werden konnte, indem die folgenden Attribute in der AutoResizeTextView-Klasse in der XML hinzugefügt wurden:

Android: ellipsize = "none"

Android: singleLine = "true"

Mit den beiden obigen Zeilen funktioniert AutoResizeTextView jetzt perfekt auf meinen 2.1- und 4.1-Emulatoren und meinem eigenen 4.0.4-Handy.

Hoffe das hilft dir. :-)

17
agudoe2

Warnung, Fehler in Android Honeycomb and Ice Cream Sandwich

Androids-Versionen: 3.1 - 4.04 haben einen Fehler, der besagt, dass setTextSize () in TextView nur zum ersten Mal funktioniert (1. Aufruf).

Der Fehler wird hier beschrieben: http://code.google.com/p/Android/issues/detail?id=22493http: // code .google.com/p/Android/issues/detail? id = 17343 # c9

behelfslösung besteht darin, Text, der TextView zugewiesen wurde, ein neues Zeilenzeichen hinzuzufügen, bevor die Größe geändert wird:

final String DOUBLE_BYTE_SPACE = "\u3000";
textView.append(DOUBLE_BYTE_SPACE);

Ich benutze es in meinem Code wie folgt:

final String DOUBLE_BYTE_SPACE = "\u3000";
AutoResizeTextView textView = (AutoResizeTextView) view.findViewById(R.id.aTextView);
String fixString = "";
if (Android.os.Build.VERSION.SDK_INT >= Android.os.Build.VERSION_CODES.HONEYCOMB_MR1
   && Android.os.Build.VERSION.SDK_INT <= Android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {  
    fixString = DOUBLE_BYTE_SPACE;
}
textView.setText(fixString + "The text" + fixString);

Ich füge dieses "\ u3000" -Zeichen links und rechts neben meinem Text ein, um ihn zentriert zu halten. Wenn Sie es linksbündig ausgerichtet haben, hängen Sie es nur rechts an. Natürlich kann es auch mit dem AutoResizeTextView-Widget eingebettet werden, aber ich wollte den Fixcode draußen lassen.

16
Malachiasz

AppcompatTextView unterstützt jetzt die automatische Größenanpassung ab Support Library 26.0. TextView in Android O funktioniert auf die gleiche Weise. Weitere Informationen finden Sie hier. Eine einfache Demo-App finden Sie hier .

<LinearLayout
      xmlns:Android="http://schemas.Android.com/apk/res/Android"
      xmlns:app="http://schemas.Android.com/apk/res-auto"
      Android:layout_width="match_parent"
      Android:layout_height="wrap_content">

      <TextView
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        app:autoSizeTextType="uniform"
        app:autoSizeMinTextSize="12sp"
        app:autoSizeMaxTextSize="100sp"
        app:autoSizeStepGranularity="2sp"
      />

</LinearLayout>
16
makata

Ich musste die Größe des Texts ändern, um die Ansichtsgrenzen perfekt anzupassen. Die Lösung von Chase reduziert nur die Textgröße, diese vergrößert auch den Text, wenn genügend Platz vorhanden ist.

Um alles zu machen schnell & präzise habe ich eine Halbierungsmethode anstelle einer iterativen while-Methode verwendet, wie Sie in der resizeText() -Methode sehen können. Deshalb haben Sie auch die Option MAX_TEXT_SIZE. Ich habe auch die Tipps von onoelle aufgenommen.

Getestet auf Android 4.4

/**
 *               DO WHAT YOU WANT TO PUBLIC LICENSE
 *                    Version 2, December 2004
 *
 * Copyright (C) 2004 Sam Hocevar <[email protected]>
 *
 * Everyone is permitted to copy and distribute verbatim or modified
 * copies of this license document, and changing it is allowed as long
 * as the name is changed.
 *
 *            DO WHAT YOU WANT TO PUBLIC LICENSE
 *   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
 *
 *  0. You just DO WHAT YOU WANT TO.
 */

import Android.content.Context;
import Android.text.Layout.Alignment;
import Android.text.StaticLayout;
import Android.text.TextPaint;
import Android.util.AttributeSet;
import Android.util.TypedValue;
import Android.widget.TextView;

/**
 * Text view that auto adjusts text size to fit within the view.
 * If the text size equals the minimum text size and still does not
 * fit, append with an Ellipsis.
 *
 * @author Chase Colburn
 * @since Apr 4, 2011
 */
public class AutoResizeTextView extends TextView {

    // Minimum text size for this text view
    public static final float MIN_TEXT_SIZE = 26;

    // Maximum text size for this text view
    public static final float MAX_TEXT_SIZE = 128;

    private static final int BISECTION_LOOP_WATCH_DOG = 30;

    // Interface for resize notifications
    public interface OnTextResizeListener {
        public void onTextResize(TextView textView, float oldSize, float newSize);
    }

    // Our ellipse string
    private static final String mEllipsis = "...";

    // Registered resize listener
    private OnTextResizeListener mTextResizeListener;

    // Flag for text and/or size changes to force a resize
    private boolean mNeedsResize = false;

    // Text size that is set from code. This acts as a starting point for resizing
    private float mTextSize;

    // Temporary upper bounds on the starting text size
    private float mMaxTextSize = MAX_TEXT_SIZE;

    // Lower bounds for text size
    private float mMinTextSize = MIN_TEXT_SIZE;

    // Text view line spacing multiplier
    private float mSpacingMult = 1.0f;

    // Text view additional line spacing
    private float mSpacingAdd = 0.0f;

    // Add Ellipsis to text that overflows at the smallest text size
    private boolean mAddEllipsis = true;

    // Default constructor override
    public AutoResizeTextView(Context context) {
        this(context, null);
    }

    // Default constructor when inflating from XML file
    public AutoResizeTextView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    // Default constructor override
    public AutoResizeTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mTextSize = getTextSize();
    }

    /**
     * When text changes, set the force resize flag to true and reset the text size.
     */
    @Override
    protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) {
        mNeedsResize = true;
        // Since this view may be reused, it is good to reset the text size
        resetTextSize();
    }

    /**
     * If the text view size changed, set the force resize flag to true
     */
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        if (w != oldw || h != oldh) {
            mNeedsResize = true;
        }
    }

    /**
     * Register listener to receive resize notifications
     * @param listener
     */
    public void setOnResizeListener(OnTextResizeListener listener) {
        mTextResizeListener = listener;
    }

    /**
     * Override the set text size to update our internal reference values
     */
    @Override
    public void setTextSize(float size) {
        super.setTextSize(size);
        mTextSize = getTextSize();
    }

    /**
     * Override the set text size to update our internal reference values
     */
    @Override
    public void setTextSize(int unit, float size) {
        super.setTextSize(unit, size);
        mTextSize = getTextSize();
    }

    /**
     * Override the set line spacing to update our internal reference values
     */
    @Override
    public void setLineSpacing(float add, float mult) {
        super.setLineSpacing(add, mult);
        mSpacingMult = mult;
        mSpacingAdd = add;
    }

    /**
     * Set the upper text size limit and invalidate the view
     * @param maxTextSize
     */
    public void setMaxTextSize(float maxTextSize) {
        mMaxTextSize = maxTextSize;
        requestLayout();
        invalidate();
    }

    /**
     * Return upper text size limit
     * @return
     */
    public float getMaxTextSize() {
        return mMaxTextSize;
    }

    /**
     * Set the lower text size limit and invalidate the view
     * @param minTextSize
     */
    public void setMinTextSize(float minTextSize) {
        mMinTextSize = minTextSize;
        requestLayout();
        invalidate();
    }

    /**
     * Return lower text size limit
     * @return
     */
    public float getMinTextSize() {
        return mMinTextSize;
    }

    /**
     * Set flag to add Ellipsis to text that overflows at the smallest text size
     * @param addEllipsis
     */
    public void setAddEllipsis(boolean addEllipsis) {
        mAddEllipsis = addEllipsis;
    }

    /**
     * Return flag to add Ellipsis to text that overflows at the smallest text size
     * @return
     */
    public boolean getAddEllipsis() {
        return mAddEllipsis;
    }

    /**
     * Reset the text to the original size
     */
    public void resetTextSize() {
        if(mTextSize > 0) {
            super.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);
            //mMaxTextSize = mTextSize;
        }
    }

    /**
     * Resize text after measuring
     */
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        if(changed || mNeedsResize) {
            int widthLimit = (right - left) - getCompoundPaddingLeft() - getCompoundPaddingRight();
            int heightLimit = (bottom - top) - getCompoundPaddingBottom() - getCompoundPaddingTop();
            resizeText(widthLimit, heightLimit);
        }
        super.onLayout(changed, left, top, right, bottom);
    }


    /**
     * Resize the text size with default width and height
     */
    public void resizeText() {
        int heightLimit = getHeight() - getPaddingBottom() - getPaddingTop();
        int widthLimit = getWidth() - getPaddingLeft() - getPaddingRight();
        resizeText(widthLimit, heightLimit);
    }

    /**
     * Resize the text size with specified width and height
     * @param width
     * @param height
     */
    public void resizeText(int width, int height) {
        CharSequence text = getText();
        // Do not resize if the view does not have dimensions or there is no text
        if(text == null || text.length() == 0 || height <= 0 || width <= 0 || mTextSize == 0) {
            return;
        }

        // Get the text view's Paint object
        TextPaint textPaint = getPaint();

        // Store the current text size
        float oldTextSize = textPaint.getTextSize();

        // Bisection method: fast & precise
        float lower = mMinTextSize;
        float upper = mMaxTextSize;
        int loop_counter=1;
        float targetTextSize = (lower+upper)/2;
        int textHeight = getTextHeight(text, textPaint, width, targetTextSize);
        while(loop_counter < BISECTION_LOOP_WATCH_DOG && upper - lower > 1) {
            targetTextSize = (lower+upper)/2;
            textHeight = getTextHeight(text, textPaint, width, targetTextSize);
            if(textHeight > height)
                upper = targetTextSize;
            else
                lower = targetTextSize;
            loop_counter++;
        }

        targetTextSize = lower;
        textHeight = getTextHeight(text, textPaint, width, targetTextSize);

        // If we had reached our minimum text size and still don't fit, append an Ellipsis
        if(mAddEllipsis && targetTextSize == mMinTextSize && textHeight > height) {
            // Draw using a static layout
            // modified: use a copy of TextPaint for measuring
            TextPaint paintCopy = new TextPaint(textPaint);
            paintCopy.setTextSize(targetTextSize);
            StaticLayout layout = new StaticLayout(text, paintCopy, width, Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, false);
            // Check that we have a least one line of rendered text
            if(layout.getLineCount() > 0) {
                // Since the line at the specific vertical position would be cut off,
                // we must trim up to the previous line
                int lastLine = layout.getLineForVertical(height) - 1;
                // If the text would not even fit on a single line, clear it
                if(lastLine < 0) {
                    setText("");
                }
                // Otherwise, trim to the previous line and add an Ellipsis
                else {
                    int start = layout.getLineStart(lastLine);
                    int end = layout.getLineEnd(lastLine);
                    float lineWidth = layout.getLineWidth(lastLine);
                    float ellipseWidth = paintCopy.measureText(mEllipsis);

                    // Trim characters off until we have enough room to draw the Ellipsis
                    while(width < lineWidth + ellipseWidth) {
                        lineWidth = paintCopy.measureText(text.subSequence(start, --end + 1).toString());
                    }
                    setText(text.subSequence(0, end) + mEllipsis);
                }
            }
        }

        // Some devices try to auto adjust line spacing, so force default line spacing
        // and invalidate the layout as a side effect
        setTextSize(TypedValue.COMPLEX_UNIT_PX, targetTextSize);
        setLineSpacing(mSpacingAdd, mSpacingMult);

        // Notify the listener if registered
        if(mTextResizeListener != null) {
            mTextResizeListener.onTextResize(this, oldTextSize, targetTextSize);
        }

        // Reset force resize flag
        mNeedsResize = false;
    }

    // Set the text size of the text Paint object and use a static layout to render text off screen before measuring
    private int getTextHeight(CharSequence source, TextPaint originalPaint, int width, float textSize) {
        // modified: make a copy of the original TextPaint object for measuring
        // (apparently the object gets modified while measuring, see also the
        // docs for TextView.getPaint() (which states to access it read-only)
        TextPaint Paint = new TextPaint(originalPaint);
        // Update the text Paint object
        Paint.setTextSize(textSize);
        // Measure using a static layout
        StaticLayout layout = new StaticLayout(source, Paint, width, Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, true);
        return layout.getHeight();
    }

}
12
tuxone

Auf der Konferenz google IO im Jahr 2017 hat google die Eigenschaft autoSize von TextView eingeführt

https://youtu.be/fjUdJ2aVqE4

<Android.support.v7.widget.AppCompatTextView
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        Android:text="@string/my_text"
        app:autoSizeTextType="uniform"
        app:autoSizeMaxTextSize="10sp"
        app:autoSizeMinTextSize="6sp"
        app:autoSizeStepGranularity="1sp"/>
11
MiladAhmadi

Da ich schon immer danach gesucht habe und vor einiger Zeit eine Lösung gefunden habe, die hier fehlt, werde ich sie hier schreiben, um sie auch für die Zukunft zu nutzen.

Hinweis: Dieser Code wurde vor einiger Zeit direkt von GoogleAndroidLollipop Dialer übernommen. Ich erinnere mich nicht, ob zu diesem Zeitpunkt Änderungen vorgenommen wurden. Ich weiß auch nicht, unter welcher Lizenz das ist, aber ich habe Grund zu der Annahme, dass es Apache 2.0 ist.

Klasse ResizeTextView, die aktuelle View

public class ResizeTextView extends TextView {

private final int mOriginalTextSize;
private final int mMinTextSize;
private final static int sMinSize = 20;
public ResizeTextView(Context context, AttributeSet attrs) {
    super(context, attrs);
    mOriginalTextSize = (int) getTextSize();
    mMinTextSize = (int) sMinSize;
}
@Override
protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
    super.onTextChanged(text, start, lengthBefore, lengthAfter);
    ViewUtil.resizeText(this, mOriginalTextSize, mMinTextSize);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    ViewUtil.resizeText(this, mOriginalTextSize, mMinTextSize);
}

Diese ResizeTextView -Klasse könnte TextView und alle seine untergeordneten Elemente erweitern, wenn ich sie verstehe, also auch EditText.

Klasse ViewUtil mit Methode resizeText(...)

/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.Apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import Android.graphics.Paint;
import Android.util.TypedValue;
import Android.widget.TextView;

public class ViewUtil {

    private ViewUtil() {}

    public static void resizeText(TextView textView, int originalTextSize, int minTextSize) {
        final Paint paint = textView.getPaint();
        final int width = textView.getWidth();
        if (width == 0) return;
        textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, originalTextSize);
        float ratio = width / Paint.measureText(textView.getText().toString());
        if (ratio <= 1.0f) {
            textView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
                    Math.max(minTextSize, originalTextSize * ratio));
        }
    }
}

Sie sollten Ihre Ansicht als festlegen

<yourpackage.yourapp.ResizeTextView
            Android:layout_width="match_parent"
            Android:layout_height="64dp"
            Android:gravity="center"
            Android:maxLines="1"/>

Ich hoffe es hilft!

10
Matteo

Hier ist eine einfache Lösung, die TextView selbst mit einem hinzugefügten TextChangedListened verwendet:

expressionView = (TextView) findViewById(R.id.expressionView);
expressionView.addTextChangedListener(textAutoResizeWatcher(expressionView, 25, 55));

private TextWatcher textAutoResizeWatcher(final TextView view, final int MIN_SP, final int MAX_SP) {
    return new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}

        @Override
        public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}

        @Override
        public void afterTextChanged(Editable editable) {

            final int widthLimitPixels = view.getWidth() - view.getPaddingRight() - view.getPaddingLeft();
            Paint paint = new Paint();
            float fontSizeSP = pixelsToSp(view.getTextSize());
            Paint.setTextSize(spToPixels(fontSizeSP));

            String viewText = view.getText().toString();

            float widthPixels = Paint.measureText(viewText);

            // Increase font size if necessary.
            if (widthPixels < widthLimitPixels){
                while (widthPixels < widthLimitPixels && fontSizeSP <= MAX_SP){
                    ++fontSizeSP;
                    Paint.setTextSize(spToPixels(fontSizeSP));
                    widthPixels = Paint.measureText(viewText);
                }
                --fontSizeSP;
            }
            // Decrease font size if necessary.
            else {
                while (widthPixels > widthLimitPixels || fontSizeSP > MAX_SP) {
                    if (fontSizeSP < MIN_SP) {
                        fontSizeSP = MIN_SP;
                        break;
                    }
                    --fontSizeSP;
                    Paint.setTextSize(spToPixels(fontSizeSP));
                    widthPixels = Paint.measureText(viewText);
                }
            }

            view.setTextSize(fontSizeSP);
        }
    };
}

private float pixelsToSp(float px) {
    float scaledDensity = getResources().getDisplayMetrics().scaledDensity;
    return px/scaledDensity;
}

private float spToPixels(float sp) {
    float scaledDensity = getResources().getDisplayMetrics().scaledDensity;
    return sp * scaledDensity;
}

Dieser Ansatz erhöht oder verringert die Schriftgröße nach Bedarf, um sie an den Text anzupassen, wobei die als Parameter empfangenen MIN_SP- und MAX_SP-Grenzen berücksichtigt werden.

8
ilharco

Ich hoffe das hilft dir

import Android.content.Context;
import Android.graphics.Rect;
import Android.text.TextPaint;
import Android.util.AttributeSet;
import Android.widget.TextView;

/* Based on 
 * from http://stackoverflow.com/questions/2617266/how-to-adjust-text-font-size-to-fit-textview
 */
public class FontFitTextView extends TextView {

private static float MAX_TEXT_SIZE = 20;

public FontFitTextView(Context context) {
    this(context, null);
}

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

    float size = this.getTextSize();
    if (size > MAX_TEXT_SIZE)
        setTextSize(MAX_TEXT_SIZE);
}

private void refitText(String text, int textWidth) {
    if (textWidth > 0) {
        float availableWidth = textWidth - this.getPaddingLeft()
                - this.getPaddingRight();

        TextPaint tp = getPaint();
        Rect rect = new Rect();
        tp.getTextBounds(text, 0, text.length(), rect);
        float size = rect.width();

        if (size > availableWidth)
            setTextScaleX(availableWidth / size);
    }
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
    int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
    refitText(this.getText().toString(), parentWidth);
    this.setMeasuredDimension(parentWidth, parentHeight);
}

@Override
protected void onTextChanged(final CharSequence text, final int start,
        final int before, final int after) {
    refitText(text.toString(), this.getWidth());
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    if (w != oldw) {
        refitText(this.getText().toString(), w);
    }
}
}

HINWEIS: Ich verwende MAX_TEXT_SIZE, wenn die Textgröße größer als 20 ist, da ich keine großen Schriftarten für meine Ansicht zulassen möchte. Wenn dies nicht der Fall ist, können Sie sie einfach entfernen.

7
vsm

Ich habe einen Blog-Post darüber geschrieben.

Ich habe eine Komponente namens ResizableButton basierend auf Kirill Grouchnikovs Blog-Beitrag über benutzerdefinierte Komponenten erstellt, die in der neuen Markt-App Android verwendet werden. Ich habe den Quellcode gesetzt hier .

Auf der anderen Seite mosabua las meinen Beitrag und sagte mir, dass er seine Implementierung als Open Source veröffentlichen würde, die schneller als meine war. Ich hoffe er veröffentlicht es bald genug :)

7
Macarse

Meine Implementierung ist etwas komplexer, bringt jedoch die folgenden Extras mit:

  • berücksichtigt die verfügbare Breite und verfügbare Höhe
  • funktioniert mit einzeiligen und mehrzeiligen Beschriftungen
  • verwendet Ellipse, wenn die Mindestschriftgröße erreicht ist
  • da die interne Textdarstellung geändert wird, wird der ursprünglich festgelegte Text in einer separaten Variablen gespeichert
  • stellt sicher, dass die Zeichenfläche immer nur so groß ist, wie sie sein muss, während sie die gesamte verfügbare Höhe des übergeordneten Elements verwendet
/**
 * Text view that auto adjusts text size to fit within the view. If the text
 * size equals the minimum text size and still does not fit, append with an
 * Ellipsis.
 * 
 * Based on the original work from Chase Colburn
 * &lt;http://stackoverflow.com/a/5535672/305532>
 *
 * @author Thomas Keller &lt;[email protected]>
 */
public class AutoResizeTextView extends TextView {

    // in dip
    private static final int MIN_TEXT_SIZE = 20;

    private static final boolean SHRINK_TEXT_SIZE = true;

    private static final char Ellipsis = '\u2026';

    private static final float LINE_SPACING_MULTIPLIER_MULTILINE = 0.8f;

    private static final float LINE_SPACING_MULTIPLIER_SINGLELINE = 1f;

    private static final float LINE_SPACING_EXTRA = 0.0f;

    private CharSequence mOriginalText;

    // temporary upper bounds on the starting text size
    private float mMaxTextSize;

    // lower bounds for text size
    private float mMinTextSize;

    // determines whether we're currently in the process of measuring ourselves,
    // so we do not enter onMeasure recursively
    private boolean mInMeasure = false;

    // if the text size should be shrinked or if the text size should be kept
    // constant and only characters should be removed to hit the boundaries
    private boolean mShrinkTextSize;

    public AutoResizeTextView(Context context) {
        this(context, null);
        init(context, null);
    }

    public AutoResizeTextView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
        init(context, attrs);
    }

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

    private void init(Context context, AttributeSet attrs) {
        // the current text size is used as maximum text size we can apply to
        // our widget
        mMaxTextSize = getTextSize();
        if (attrs != null) {
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AutoResizeTextView);
            mMinTextSize = a.getFloat(R.styleable.AutoResizeTextView_minFontSize, MIN_TEXT_SIZE);
            mShrinkTextSize = a.getBoolean(R.styleable.AutoResizeTextView_shrinkTextSize, SHRINK_TEXT_SIZE);
            a.recycle();
        }
    }

    @Override
    public void setTextSize(float size) {
        mMaxTextSize = size;
        super.setTextSize(size);
    }

    /**
     * Returns the original, unmodified text of this widget
     * 
     * @return
     */
    public CharSequence getOriginalText() {
        // text has not been resized yet
        if (mOriginalText == null) {
            return getText();
        }
        return mOriginalText;
    }

    @Override
    public void setText(CharSequence text, BufferType type) {
        if (!mInMeasure) {
            mOriginalText = text.toString();
        }
        super.setText(text, type);
    }

    @SuppressLint("DrawAllocation")
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        mInMeasure = true;
        try {
            int availableWidth = MeasureSpec.getSize(widthMeasureSpec) - getCompoundPaddingLeft()
                    - getCompoundPaddingRight();
            int availableHeight = MeasureSpec.getSize(heightMeasureSpec) - getCompoundPaddingTop()
                    - getCompoundPaddingBottom();

            // Do not resize if the view does not have dimensions or there is no
            // text
            if (mOriginalText == null || mOriginalText.length() == 0 || availableWidth <= 0) {
                return;
            }

            TextPaint textPaint = getPaint();

            // start with the recorded max text size
            float targetTextSize = mMaxTextSize;
            String originalText = mOriginalText.toString();
            String finalText = originalText;

            Rect textSize = getTextSize(originalText, textPaint, targetTextSize);
            boolean textExceedsBounds = textSize.height() > availableHeight || textSize.width() > availableWidth;
            if (mShrinkTextSize && textExceedsBounds) {
                // check whether all lines can be rendered in the available
                // width / height without violating the bounds of the parent and
                // without using a text size that is smaller than the minimum
                // text size
                float heightMultiplier = availableHeight / (float) textSize.height();
                float widthMultiplier = availableWidth / (float) textSize.width();
                float multiplier = Math.min(heightMultiplier, widthMultiplier);
                targetTextSize = Math.max(targetTextSize * multiplier, mMinTextSize);

                // measure again
                textSize = getTextSize(finalText, textPaint, targetTextSize);
            }

            // we cannot shrink the height further when we hit the available
            // height, but we can shrink the width by applying an Ellipsis on
            // each line
            if (textSize.width() > availableWidth) {
                StringBuilder modifiedText = new StringBuilder();
                String lines[] = originalText.split(System.getProperty("line.separator"));
                for (int i = 0; i < lines.length; i++) {
                    modifiedText.append(resizeLine(textPaint, lines[i], availableWidth));
                    // add the separator back to all but the last processed line
                    if (i != lines.length - 1) {
                        modifiedText.append(System.getProperty("line.separator"));
                    }
                }
                finalText = modifiedText.toString();

                // measure again
                textSize = getTextSize(finalText, textPaint, targetTextSize);
            }

            textPaint.setTextSize(targetTextSize);
            boolean isMultiline = finalText.indexOf('\n') > -1;
            // do not include extra font padding (for accents, ...) for
            // multiline texts, this will prevent proper placement with
            // Gravity.CENTER_VERTICAL
            if (isMultiline) {
                setLineSpacing(LINE_SPACING_EXTRA, LINE_SPACING_MULTIPLIER_MULTILINE);
                setIncludeFontPadding(false);
            } else {
                setLineSpacing(LINE_SPACING_EXTRA, LINE_SPACING_MULTIPLIER_SINGLELINE);
                setIncludeFontPadding(true);
            }

            // according to
            // <http://code.google.com/p/Android/issues/detail?id=22493>
            // we have to add a unicode character to trigger the text centering
            // in ICS. this particular character is known as "zero-width" and
            // does no harm.
            setText(finalText + "\u200B");

            int measuredWidth = textSize.width() + getCompoundPaddingLeft() + getCompoundPaddingRight();
            int measuredHeight = textSize.height() + getCompoundPaddingTop() + getCompoundPaddingBottom();

            // expand the view to the parent's height in case it is smaller or
            // to the minimum height that has been set
            // FIXME: honor the vertical measure mode (EXACTLY vs AT_MOST) here
            // somehow
            measuredHeight = Math.max(measuredHeight, MeasureSpec.getSize(heightMeasureSpec));
            setMeasuredDimension(measuredWidth, measuredHeight);
        } finally {
            mInMeasure = false;
        }
    }

    private Rect getTextSize(String text, TextPaint textPaint, float textSize) {
        textPaint.setTextSize(textSize);
        // StaticLayout depends on a given width in which it should lay out the
        // text (and optionally also split into separate lines).
        // Therefor we calculate the current text width manually and start with
        // a fake (read: maxmimum) width for the height calculation.
        // We do _not_ use layout.getLineWidth() here since this returns
        // slightly smaller numbers and therefor would lead to exceeded text box
        // drawing.
        StaticLayout layout = new StaticLayout(text, textPaint, Integer.MAX_VALUE, Alignment.ALIGN_NORMAL, 1f, 0f, true);
        int textWidth = 0;
        String lines[] = text.split(System.getProperty("line.separator"));
        for (int i = 0; i < lines.length; ++i) {
            textWidth = Math.max(textWidth, measureTextWidth(textPaint, lines[i]));
        }
        return new Rect(0, 0, textWidth, layout.getHeight());
    }

    private String resizeLine(TextPaint textPaint, String line, int availableWidth) {
        checkArgument(line != null && line.length() > 0, "expected non-empty string");
        int textWidth = measureTextWidth(textPaint, line);
        int lastDeletePos = -1;
        StringBuilder builder = new StringBuilder(line);
        while (textWidth > availableWidth && builder.length() > 0) {
            lastDeletePos = builder.length() / 2;
            builder.deleteCharAt(builder.length() / 2);
            // don't forget to measure the Ellipsis character as well; it
            // doesn't matter where it is located in the line, it just has to be
            // there, since there are no (known) ligatures that use this glyph
            String textToMeasure = builder.toString() + Ellipsis;
            textWidth = measureTextWidth(textPaint, textToMeasure);
        }
        if (lastDeletePos > -1) {
            builder.insert(lastDeletePos, Ellipsis);
        }
        return builder.toString();
    }

    // there are several methods in Android to determine the text width, namely
    // getBounds() and measureText().
    // The latter works for us the best as it gives us the best / nearest
    // results without that our text canvas needs to wrap its text later on
    // again.
    private int measureTextWidth(TextPaint textPaint, String line) {
        return Math.round(textPaint.measureText(line));
    }
}

[überarbeitet am 21.11.2012]

  • platzierung der Ellipse korrigiert
  • überarbeitete Textgrößenberechnung; Jetzt wird immer der vollständige Text einschließlich der Zeilenumbrüche gemessen, um Probleme zu beheben, wenn die Addition der Höhe zweier einzelner gemessener Zeilen nicht zum gleichen Ergebnis wie die Messung der Texthöhe insgesamt geführt hat
  • statt eine Schleife zu erstellen, um die kleinste verfügbare Textgröße zu finden, berechnen Sie sie einfach nach der ersten Messung
5
Thomas Keller

Ich fand das Folgende gut für mich. Es gibt keine Schleife und berücksichtigt sowohl die Höhe als auch die Breite. Beachten Sie, dass es wichtig ist, die PX-Einheit anzugeben, wenn Sie setTextSize in der Ansicht aufrufen.

Paint paint = adjustTextSize(getPaint(), numChars, maxWidth, maxHeight);
setTextSize(TypedValue.COMPLEX_UNIT_PX,Paint.getTextSize());

Hier ist die Routine, die ich benutze, indem ich getPaint () aus der Ansicht übergebe. Eine 10-stellige Zeichenfolge mit einem 'breiten' Zeichen wird verwendet, um die Breite unabhängig von der tatsächlichen Zeichenfolge zu schätzen.

private static final String text10="OOOOOOOOOO";
public static Paint adjustTextSize(Paint paint, int numCharacters, int widthPixels, int heightPixels) {
    float width = Paint.measureText(text10)*numCharacters/text10.length();
    float newSize = (int)((widthPixels/width)*Paint.getTextSize());
    Paint.setTextSize(newSize);

    // remeasure with font size near our desired result
    width = Paint.measureText(text10)*numCharacters/text10.length();
    newSize = (int)((widthPixels/width)*Paint.getTextSize());
    Paint.setTextSize(newSize);

    // Check height constraints
    FontMetricsInt metrics = Paint.getFontMetricsInt();
    float textHeight = metrics.descent-metrics.ascent;
    if (textHeight > heightPixels) {
        newSize = (int)(newSize * (heightPixels/textHeight));
        Paint.setTextSize(newSize);
    }

    return Paint;
}
4
Glenn

Hier ist eine Aufzählung dessen, was ich noch für jemanden gefunden habe, der noch sucht:

1) Hier ist eine Lösung , die die Textansicht rekursiv neu malt, bis sie passt. Dies bedeutet, dass Sie buchstäblich zusehen müssen, wie Ihr Text zusammenschrumpft, aber zumindest passt er, wenn er fertig ist. Der Code muss etwas optimiert werden, um ihn zu implementieren, ist aber größtenteils vorhanden.

2) Sie können versuchen, eine benutzerdefinierte Lösung wie this oder dunni's class in this zu hacken, was ich mit getPaint (). MeasureText (str) gemacht habe, um zu suchen für die richtige Größe, aber es wurde viel unordentlicher, da ich es brauche, um nur auf Leerzeichen zu wickeln ...

3) Sie können weiter suchen - ich habe mehr Alternativen ausprobiert, als ich zählen kann. Teds Rat zu StaticLayout hat sich für mich nicht ausgezahlt, aber vielleicht gibt es dort etwas. Ich habe versucht, mithilfe der StaticLayout.getEllipsis (Zeile) zu bestimmen, ob der Text ohne Wirkung vom Bildschirm verschwindet. Siehe meinen (derzeit nicht beantworteten) Beitrag dazu hier .

3
Nathan Fig

Ich brauchte eine bestimmte Lösung. Ich habe einen Edittext und eine Textansicht in meinem Layout. Die Textansicht hat eine feste Höhe und Breite. Wenn der Benutzer mit der Eingabe des Edittext beginnt, sollte der Text sofort in der Textansicht angezeigt werden. Der Text im Textfeld sollte automatisch an die Textansicht angepasst werden. Also habe ich Chases Lösung aktualisiert, damit sie für mich funktioniert. Wenn sich der Text in der Textansicht ändert, beginnt die Größenänderung. Der Unterschied zwischen meiner und Chases Lösung: Die Größe wird geändert, auch wenn der Benutzer einige Zeichen LÖSCHT. Ich hoffe es kann jemandem helfen.

public class TextFitTextView extends TextView {

// Minimum text size for this text view
public static final float MIN_TEXT_SIZE = 10;

// Maximum text size for this text view - if it is 0, then the text acts
// like match_parent
public static final float MAX_TEXT_SIZE = 0;

// Our ellipse string
private static final String mEllipsis = "...";

// Text size that is set from code. This acts as a starting point for
// resizing
private float mTextSize;

// Lower bounds for text size
private float mMinTextSize = MIN_TEXT_SIZE;

// Max bounds for text size
private float mMaxTextSize = MAX_TEXT_SIZE;

// Text view line spacing multiplier
private float mSpacingMult = 1.0f;

// Text view additional line spacing
private float mSpacingAdd = 0.0f;

// Add Ellipsis to text that overflows at the smallest text size
private boolean mAddEllipsis = true;

// Add Ellipsis to text that overflows at the smallest text size
private int heightLimit;
private int widthLimit;

// Default constructor override
public TextFitTextView(Context context) {
    this(context, null);
}

// Default constructor when inflating from XML file
public TextFitTextView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
}

// Default constructor override
public TextFitTextView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    mTextSize = getTextSize();
}

/**
 * When text changes resize the text size.
 */
@Override
protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) {
    // if we are adding new chars to text
    if (before <= after && after != 1) {
        resizeText(true);
        // now we are deleting chars
    } else {
        resizeText(false);
    }
}

/**
 * Override the set text size to update our internal reference values
 */
@Override
public void setTextSize(float size) {
    super.setTextSize(size);
    mTextSize = getTextSize();
}

/**
 * Override the set text size to update our internal reference values
 */
@Override
public void setTextSize(int unit, float size) {
    super.setTextSize(unit, size);
    mTextSize = getTextSize();
}

/**
 * Override the set line spacing to update our internal reference values
 */
@Override
public void setLineSpacing(float add, float mult) {
    super.setLineSpacing(add, mult);
    mSpacingMult = mult;
    mSpacingAdd = add;
}

/**
 * Set the lower text size limit and invalidate the view
 * 
 * @param minTextSize
 */
public void setMinTextSize(float minTextSize) {
    mMinTextSize = minTextSize;
    requestLayout();
    invalidate();
}

/**
 * Return lower text size limit
 * 
 * @return
 */
public float getMinTextSize() {
    return mMinTextSize;
}

/**
 * Set flag to add Ellipsis to text that overflows at the smallest text size
 * 
 * @param addEllipsis
 */
public void setAddEllipsis(boolean addEllipsis) {
    mAddEllipsis = addEllipsis;
}

/**
 * Return flag to add Ellipsis to text that overflows at the smallest text
 * size
 * 
 * @return
 */
public boolean getAddEllipsis() {
    return mAddEllipsis;
}

/**
 * Get width and height limits
 */
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    if (widthLimit == 0 && heightLimit == 0) {
        widthLimit = (right - left) - getCompoundPaddingLeft() - getCompoundPaddingRight();
        heightLimit = (bottom - top) - getCompoundPaddingBottom() - getCompoundPaddingTop();
    }
    super.onLayout(changed, left, top, right, bottom);
}

/**
 * Resize the text size with specified width and height
 * 
 * @param width
 * @param height
 */
public void resizeText(boolean increase) {
    CharSequence text = getText();
    // Do not resize if the view does not have dimensions or there is no
    // text
    if (text == null || text.length() == 0 || heightLimit <= 0 || widthLimit <= 0 || mTextSize == 0) {
        return;
    }

    // Get the text view's Paint object
    TextPaint textPaint = getPaint();

    // Get the required text height
    int textHeight = getTextHeight(text, textPaint, widthLimit, mTextSize);


    // If the text length is increased 
    // Until we either fit within our text view or we had reached our min
    // text size, incrementally try smaller sizes
    if (increase) {
        while (textHeight > heightLimit && mTextSize > mMinTextSize) {
            mTextSize = Math.max(mTextSize - 2, mMinTextSize);
            textHeight = getTextHeight(text, textPaint, widthLimit, mTextSize);
        }
    } 
//      text length has been decreased
    else {
//          if max test size is set then add it to while condition
        if (mMaxTextSize != 0) {
            while (textHeight < heightLimit && mTextSize <= mMaxTextSize) {
                mTextSize = mTextSize + 2;
                textHeight = getTextHeight(text, textPaint, widthLimit, mTextSize);
            }
        } else {
            while (textHeight < heightLimit) {
                mTextSize = mTextSize + 2;
                textHeight = getTextHeight(text, textPaint, widthLimit, mTextSize);
            }
        }
        mTextSize = textHeight > heightLimit ? mTextSize - 2 : mTextSize;
    }

    // If we had reached our minimum text size and still don't fit, append
    // an Ellipsis
    if (mAddEllipsis && mTextSize == mMinTextSize && textHeight > heightLimit) {
        // Draw using a static layout
        TextPaint Paint = new TextPaint(textPaint);
        StaticLayout layout = new StaticLayout(text, Paint, widthLimit, Alignment.ALIGN_NORMAL, mSpacingMult,
                mSpacingAdd, false);
        // Check that we have a least one line of rendered text
        if (layout.getLineCount() > 0) {
            // Since the line at the specific vertical position would be cut
            // off,
            // we must trim up to the previous line
            int lastLine = layout.getLineForVertical(heightLimit) - 1;
            // If the text would not even fit on a single line, clear it
            if (lastLine < 0) {
                setText("");
            }
            // Otherwise, trim to the previous line and add an Ellipsis
            else {
                int start = layout.getLineStart(lastLine);
                int end = layout.getLineEnd(lastLine);
                float lineWidth = layout.getLineWidth(lastLine);
                float ellipseWidth = Paint.measureText(mEllipsis);

                // Trim characters off until we have enough room to draw the
                // Ellipsis
                while (widthLimit < lineWidth + ellipseWidth) {
                    lineWidth = Paint.measureText(text.subSequence(start, --end + 1).toString());
                }
                setText(text.subSequence(0, end) + mEllipsis);
            }
        }
    }

    // Some devices try to auto adjust line spacing, so force default line
    // spacing
    // and invalidate the layout as a side effect
    setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);
    setLineSpacing(mSpacingAdd, mSpacingMult);

}

// Set the text size of the text Paint object and use a static layout to
// render text off screen before measuring
private int getTextHeight(CharSequence source, TextPaint originalPaint, int width, float textSize) {
    // Update the text Paint object
    TextPaint Paint = new TextPaint(originalPaint);
    Paint.setTextSize(textSize);
    // Measure using a static layout
    StaticLayout layout = new StaticLayout(source, Paint, width, Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd,
            true);
    return layout.getHeight();
}

}
2
granko87

Bereitstellung dieser Version der Top-Antwort am C # für diejenigen, die auf Xamarin.Android codieren. Hat für mich gut funktioniert.

 /**
 *               DO WHAT YOU WANT TO PUBLIC LICENSE
 *                    Version 2, December 2004
 * 
 * Copyright (C) 2004 Sam Hocevar <[email protected]>
 * 
 * Everyone is permitted to copy and distribute verbatim or modified
 * copies of this license document, and changing it is allowed as long
 * as the name is changed.
 * 
 *            DO WHAT YOU WANT TO PUBLIC LICENSE
 *   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
 * 
 *  0. You just DO WHAT YOU WANT TO.
 */


using System;
using Android.Content;
using Android.Runtime;
using Android.Text;
using Android.Util;
using Android.Widget;
using Java.Lang;

namespace App.GuestGuide.Droid.Controls
{
    public class OnTextResizeEventArgs : EventArgs
    {
        public TextView TextView { get; set; }
        public float OldSize { get; set; }
        public float NewSize { get; set; }
    }

    /// <inheritdoc />
    /// <summary>
    /// Text view that auto adjusts text size to fit within the view.
    /// If the text size equals the minimum text size and still does not
    /// fit, append with an Ellipsis.
    /// </summary>
    public class AutoResizeTextView : TextView
    {
        /// <summary>
        /// Minimum text size for this text view
        /// </summary>
        public static float MIN_TEXT_SIZE = 10;

        /// <summary>
        /// Our ellipse string
        /// </summary>
        private const string Ellipsis = "...";


        private float _mMaxTextSize;

        private float _mMinTextSize = MIN_TEXT_SIZE;

        /// <summary>
        /// Register subscriber to receive resize notifications
        /// </summary>
        public event EventHandler<OnTextResizeEventArgs> OnTextResize;

        /// <summary>
        /// Flag for text and/or size changes to force a resize
        /// </summary>
        private bool _needsResize;

        /// <summary>
        /// Text size that is set from code. This acts as a starting point for resizing
        /// </summary>
        private float _textSize;

        /// <summary>
        /// Text view line spacing multiplier
        /// </summary>
        private float _spacingMult = 1.0f;

        /// <summary>
        /// Text view additional line spacing
        /// </summary>
        private float _spacingAdd;

        /// <summary>
        /// Add Ellipsis to text that overflows at the smallest text size
        /// </summary>
        public bool ShouldAddEllipsis { get; set; }

        /// <inheritdoc />
        /// <summary>
        /// Override the set text size to update our internal reference values
        /// </summary>
        public override float TextSize
        {
            get => base.TextSize;
            set
            {
                base.TextSize = value;
                _textSize = TextSize;
            }
        }

        /// <summary>
        /// Temporary upper bounds on the starting text size
        /// </summary>
        public float MaxTextSize
        {
            get => _mMaxTextSize;
            // Set the upper text size limit and invalidate the view
            set
            {
                _mMaxTextSize = value;
                RequestLayout();
                Invalidate();
            }
        }

        /// <summary>
        /// Lower bounds for text size
        /// </summary>
        public float MinTextSize
        {
            get => _mMinTextSize;
            //Set the lower text size limit and invalidate the view
            set
            {
                _mMinTextSize = value;
                RequestLayout();
                Invalidate();
            }
        }

        public AutoResizeTextView(Context context) : this(context, null)
        {
        }

        public AutoResizeTextView(Context context, IAttributeSet attrs) : this(context, attrs, 0)
        {
        }

        public AutoResizeTextView(Context context, IAttributeSet attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)
        {
            _textSize = TextSize;
        }

        public AutoResizeTextView(Context context, IAttributeSet attrs, int defStyleAttr, int defStyleRes) : base(context, attrs, defStyleAttr, defStyleRes)
        {
            _textSize = TextSize;
        }

        protected AutoResizeTextView(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
        {
            _textSize = TextSize;
        }

        /// <inheritdoc />
        /// <summary>
        /// When text changes, set the force resize flag to true and reset the text size.
        /// </summary>
        /// <param name="text"></param>
        /// <param name="start"></param>
        /// <param name="lengthBefore"></param>
        /// <param name="lengthAfter"></param>
        protected override void OnTextChanged(ICharSequence text, int start, int lengthBefore, int lengthAfter)
        {
            _needsResize = true;
            // Since this view may be reused, it is good to reset the text size
            ResetTextSize();
        }

        /// <inheritdoc />
        /// <summary>
        /// If the text view size changed, set the force resize flag to true
        /// </summary>
        /// <param name="w"></param>
        /// <param name="h"></param>
        /// <param name="oldw"></param>
        /// <param name="oldh"></param>
        protected override void OnSizeChanged(int w, int h, int oldw, int oldh)
        {
            if (w != oldw || h != oldh)
            {
                _needsResize = true;
            }
        }

        public override void SetTextSize([GeneratedEnum] ComplexUnitType unit, float size)
        {
            base.SetTextSize(unit, size);
            _textSize = TextSize;
        }

        /// <inheritdoc />
        /// <summary>
        /// Override the set line spacing to update our internal reference values
        /// </summary>
        /// <param name="add"></param>
        /// <param name="mult"></param>
        public override void SetLineSpacing(float add, float mult)
        {
            base.SetLineSpacing(add, mult);
            _spacingMult = mult;
            _spacingAdd = add;
        }

        /// <summary>
        /// Reset the text to the original size
        /// </summary>
        public void ResetTextSize()
        {
            if (_textSize > 0)
            {
                base.SetTextSize(ComplexUnitType.Px, _textSize);
                _mMaxTextSize = _textSize;
            }
        }

        /// <inheritdoc />
        /// <summary>
        /// Resize text after measuring
        /// </summary>
        /// <param name="changed"></param>
        /// <param name="left"></param>
        /// <param name="top"></param>
        /// <param name="right"></param>
        /// <param name="bottom"></param>
        protected override void OnLayout(bool changed, int left, int top, int right, int bottom)
        {
            if (changed || _needsResize)
            {
                var widthLimit = (right - left) - CompoundPaddingLeft - CompoundPaddingRight;
                var heightLimit = (bottom - top) - CompoundPaddingBottom - CompoundPaddingTop;
                ResizeText(widthLimit, heightLimit);
            }

            base.OnLayout(changed, left, top, right, bottom);
        }

        /// <summary>
        /// Resize the text size with default width and height
        /// </summary>
        public void ResizeText()
        {
            var heightLimit = Height - PaddingBottom - PaddingTop;
            var widthLimit = Width - PaddingLeft - PaddingRight;
            ResizeText(widthLimit, heightLimit);
        }

        /// <summary>
        /// Resize the text size with specified width and height
        /// </summary>
        /// <param name="width"></param>
        /// <param name="height"></param>
        public void ResizeText(int width, int height)
        {
            ICharSequence text = null;

            if (!string.IsNullOrEmpty(Text))
            {
                text = new Java.Lang.String(Text);
            }

            // Do not resize if the view does not have dimensions or there is no text
            if (text == null || text.Length() == 0 || height <= 0 || width <= 0 || _textSize == 0)
            {
                return;
            }

            if (TransformationMethod != null)
            {
                text = TransformationMethod.GetTransformationFormatted(text, this);
            }

            // Get the text view's Paint object
            var textPaint = Paint;
            // Store the current text size
            var oldTextSize = textPaint.TextSize;
            // If there is a max text size set, use the lesser of that and the default text size
            var targetTextSize = _mMaxTextSize > 0 ? System.Math.Min(_textSize, _mMaxTextSize) : _textSize;

            // Get the required text height
            var textHeight = GetTextHeight(text, textPaint, width, targetTextSize);

            // Until we either fit within our text view or we had reached our min text size, incrementally try smaller sizes
            while (textHeight > height && targetTextSize > _mMinTextSize)
            {
                targetTextSize = System.Math.Max(targetTextSize - 2, _mMinTextSize);
                textHeight = GetTextHeight(text, textPaint, width, targetTextSize);
            }

            // If we had reached our minimum text size and still don't fit, append an Ellipsis
            if (ShouldAddEllipsis && targetTextSize == _mMinTextSize && textHeight > height)
            {
                // Draw using a static layout
                // modified: use a copy of TextPaint for measuring
                var Paint = new TextPaint(textPaint);
                // Draw using a static layout
                var layout = new StaticLayout(text, Paint, width, Layout.Alignment.AlignNormal, _spacingMult, _spacingAdd, false);

                // Check that we have a least one line of rendered text
                if (layout.LineCount > 0)
                {
                    // Since the line at the specific vertical position would be cut off,
                    // we must trim up to the previous line
                    var lastLine = layout.GetLineForVertical(height) - 1;
                    // If the text would not even fit on a single line, clear it
                    if (lastLine < 0)
                    {
                        Text = string.Empty;
                    }
                    // Otherwise, trim to the previous line and add an Ellipsis
                    else
                    {
                        var start = layout.GetLineStart(lastLine);
                        var end = layout.GetLineEnd(lastLine);
                        var lineWidth = layout.GetLineWidth(lastLine);
                        var ellipseWidth = textPaint.MeasureText(Ellipsis);

                        // Trim characters off until we have enough room to draw the Ellipsis
                        while (width < lineWidth + ellipseWidth)
                        {
                            lineWidth = textPaint.MeasureText(text.SubSequence(start, --end + 1));
                        }

                        Text = text.SubSequence(0, end) + Ellipsis;
                    }
                }
            }

            // Some devices try to auto adjust line spacing, so force default line spacing
            // and invalidate the layout as a side effect
            SetTextSize(ComplexUnitType.Px, targetTextSize);
            SetLineSpacing(_spacingAdd, _spacingMult);

            var notifyArgs = new OnTextResizeEventArgs
            {
                TextView = this,
                NewSize = targetTextSize,
                OldSize = oldTextSize
            };

            // Notify the listener if registered
            OnTextResize?.Invoke(this, notifyArgs);

            // Reset force resize flag
            _needsResize = false;
        }

        /// <summary>
        /// Set the text size of the text Paint object and use a static layout to render text off screen before measuring
        /// </summary>
        /// <param name="source"></param>
        /// <param name="Paint"></param>
        /// <param name="width"></param>
        /// <param name="textSize"></param>
        /// <returns></returns>
        private int GetTextHeight(ICharSequence source, TextPaint Paint, int width, float textSize)
        {
            // modified: make a copy of the original TextPaint object for measuring
            // (apparently the object gets modified while measuring, see also the
            // docs for TextView.getPaint() (which states to access it read-only)
            // Update the text Paint object
            var paintCopy = new TextPaint(Paint)
            {
                TextSize = textSize
            };

            // Measure using a static layout
            var layout = new StaticLayout(source, paintCopy, width, Layout.Alignment.AlignNormal, _spacingMult, _spacingAdd, true);

            return layout.Height;
        }
    }
}
1
Oleg Kosuakiv

Meine Methode ist:

public void changeTextSize(int initialSize, TextView tv) {

    DisplayMetrics displayMetrics = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
    double width = displayMetrics.widthPixels / displayMetrics.xdpi;
    double height = displayMetrics.heightPixels / displayMetrics.ydpi;

    Log.i("LOG", "The width of the tested emulator is: " + width);
    Log.i("LOG", "The height of the tested emulator is: " + height);

    double scale = Math.min(width / 2.25, height / 4.0); //See the logcat >>> width = 2.25 and heigt = 4.0
    tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, (int) (initialSize * scale));

}

Zum Beispiel:

changeTextSize(16, findViewById(R.id.myTextView));
changeTextSize(12, findViewById(R.id.myEditText));
1
gadolf

Sie können hierfür die Klasse Android.text.StaticLayout verwenden. Das ist, was TextView intern verwendet.

1
Ted Hopp

Ich habe einige der oben genannten Vorschläge kombiniert, um einen Vorschlag zu erstellen, der mit der Bisektionsmethode auf und ab skaliert. Es skaliert auch innerhalb der Breite.

/**
 *               DO WHAT YOU WANT TO PUBLIC LICENSE
 *                    Version 2, December 2004
 *
 * Copyright (C) 2004 Sam Hocevar <[email protected]>
 *
 * Everyone is permitted to copy and distribute verbatim or modified
 * copies of this license document, and changing it is allowed as long
 * as the name is changed.
 *
 *            DO WHAT YOU WANT TO PUBLIC LICENSE
 *   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
 *
 *  0. You just DO WHAT YOU WANT TO.
 */

import Android.content.Context;
import Android.text.Layout.Alignment;
import Android.text.StaticLayout;
import Android.text.TextPaint;
import Android.util.AttributeSet;
import Android.util.TypedValue;
import Android.widget.TextView;

/**
 * Text view that auto adjusts text size to fit within the view. If the text
 * size equals the minimum text size and still does not fit, append with an
 * Ellipsis.
 * 
 * @author Chase Colburn
 * @since Apr 4, 2011
 */
public class AutoResizeTextView extends TextView {

    // Minimum text size for this text view
    public static final float MIN_TEXT_SIZE = 10;

    // Minimum text size for this text view
    public static final float MAX_TEXT_SIZE = 128;

    private static final int BISECTION_LOOP_WATCH_DOG = 30;

    // Interface for resize notifications
    public interface OnTextResizeListener {
        public void onTextResize(TextView textView, float oldSize, float newSize);
    }

    // Our ellipse string
    private static final String mEllipsis = "...";

    // Registered resize listener
    private OnTextResizeListener mTextResizeListener;

    // Flag for text and/or size changes to force a resize
    private boolean mNeedsResize = false;

    // Text size that is set from code. This acts as a starting point for
    // resizing
    private float mTextSize;

    // Temporary upper bounds on the starting text size
    private float mMaxTextSize = MAX_TEXT_SIZE;

    // Lower bounds for text size
    private float mMinTextSize = MIN_TEXT_SIZE;

    // Text view line spacing multiplier
    private float mSpacingMult = 1.0f;

    // Text view additional line spacing
    private float mSpacingAdd = 0.0f;

    // Add Ellipsis to text that overflows at the smallest text size
    private boolean mAddEllipsis = true;

    // Default constructor override
    public AutoResizeTextView(Context context) {
        this(context, null);
    }

    // Default constructor when inflating from XML file
    public AutoResizeTextView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    // Default constructor override
    public AutoResizeTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mTextSize = getTextSize();
    }

    /**
     * When text changes, set the force resize flag to true and reset the text
     * size.
     */
    @Override
    protected void onTextChanged(final CharSequence text, final int start,
            final int before, final int after) {
        mNeedsResize = true;
        // Since this view may be reused, it is good to reset the text size
        resetTextSize();
    }

    /**
     * If the text view size changed, set the force resize flag to true
     */
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        if (w != oldw || h != oldh) {
            mNeedsResize = true;
        }
    }

    /**
     * Register listener to receive resize notifications
     * 
     * @param listener
     */
    public void setOnResizeListener(OnTextResizeListener listener) {
        mTextResizeListener = listener;
    }

    /**
     * Override the set text size to update our internal reference values
     */
    @Override
    public void setTextSize(float size) {
        super.setTextSize(size);
        mTextSize = getTextSize();
    }

    /**
     * Override the set text size to update our internal reference values
     */
    @Override
    public void setTextSize(int unit, float size) {
        super.setTextSize(unit, size);
        mTextSize = getTextSize();
    }

    /**
     * Override the set line spacing to update our internal reference values
     */
    @Override
    public void setLineSpacing(float add, float mult) {
        super.setLineSpacing(add, mult);
        mSpacingMult = mult;
        mSpacingAdd = add;
    }

    /**
     * Set the upper text size limit and invalidate the view
     * 
     * @param maxTextSize
     */
    public void setMaxTextSize(float maxTextSize) {
        mMaxTextSize = maxTextSize;
        requestLayout();
        invalidate();
    }

    /**
     * Return upper text size limit
     * 
     * @return
     */
    public float getMaxTextSize() {
        return mMaxTextSize;
    }

    /**
     * Set the lower text size limit and invalidate the view
     * 
     * @param minTextSize
     */
    public void setMinTextSize(float minTextSize) {
        mMinTextSize = minTextSize;
        requestLayout();
        invalidate();
    }

    /**
     * Return lower text size limit
     * 
     * @return
     */
    public float getMinTextSize() {
        return mMinTextSize;
    }

    /**
     * Set flag to add Ellipsis to text that overflows at the smallest text size
     * 
     * @param addEllipsis
     */
    public void setAddEllipsis(boolean addEllipsis) {
        mAddEllipsis = addEllipsis;
    }

    /**
     * Return flag to add Ellipsis to text that overflows at the smallest text
     * size
     * 
     * @return
     */
    public boolean getAddEllipsis() {
        return mAddEllipsis;
    }

    /**
     * Reset the text to the original size
     */
    public void resetTextSize() {
        if (mTextSize > 0) {
            super.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);
            // mMaxTextSize = mTextSize;
        }
    }

    /**
     * Resize text after measuring
     */

    @Override
    protected void onLayout(boolean changed, int left, int top, int right,
            int bottom) {
        if (changed || mNeedsResize) {
            int widthLimit = (right - left) - getCompoundPaddingLeft()
                    - getCompoundPaddingRight();
            int heightLimit = (bottom - top) - getCompoundPaddingBottom()
                    - getCompoundPaddingTop();
            resizeText(widthLimit, heightLimit);
        }
        super.onLayout(changed, left, top, right, bottom);
    }

    /**
     * Resize the text size with default width and height
     */
    public void resizeText() {

        // Height and width with a padding as a percentage of height
        int heightLimit = getHeight() - getPaddingBottom() - getPaddingTop();
        int widthLimit = getWidth() - getPaddingLeft() - getPaddingRight();
        resizeText(widthLimit, heightLimit);
    }

    /**
     * Resize the text size with specified width and height
     * 
     * @param width
     * @param height
     */
    public void resizeText(int width, int height) {
        CharSequence text = getText();

        // Do not resize if the view does not have dimensions or there is no
        // text
        if (text == null || text.length() == 0 || height <= 0 || width <= 0
                || mTextSize == 0) {
            return;
        }

        // Get the text view's Paint object
        TextPaint textPaint = getPaint();

        // Store the current text size
        float oldTextSize = textPaint.getTextSize();

        // Bisection method: fast & precise
        float lower = mMinTextSize;
        float upper = mMaxTextSize;
        int loop_counter = 1;
        float targetTextSize = (lower + upper) / 2;
        int textHeight = getTextHeight(text, textPaint, width, targetTextSize);
        int textWidth = getTextWidth(text, textPaint, width, targetTextSize);

        while (loop_counter < BISECTION_LOOP_WATCH_DOG && upper - lower > 1) {
            targetTextSize = (lower + upper) / 2;
            textHeight = getTextHeight(text, textPaint, width, targetTextSize);
            textWidth = getTextWidth(text, textPaint, width, targetTextSize);
            if (textHeight > (height) || textWidth > (width))
                upper = targetTextSize;
            else
                lower = targetTextSize;
            loop_counter++;
        }

        targetTextSize = lower;
        textHeight = getTextHeight(text, textPaint, width, targetTextSize);

        // If we had reached our minimum text size and still don't fit, append
        // an Ellipsis
        if (mAddEllipsis && targetTextSize == mMinTextSize
                && textHeight > height) {
            // Draw using a static layout
            // modified: use a copy of TextPaint for measuring
            TextPaint paintCopy = new TextPaint(textPaint);
            paintCopy.setTextSize(targetTextSize);
            StaticLayout layout = new StaticLayout(text, paintCopy, width,
                    Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, false);
            // Check that we have a least one line of rendered text
            if (layout.getLineCount() > 0) {
                // Since the line at the specific vertical position would be cut
                // off,
                // we must trim up to the previous line
                int lastLine = layout.getLineForVertical(height) - 1;
                // If the text would not even fit on a single line, clear it
                if (lastLine < 0) {
                    setText("");
                }
                // Otherwise, trim to the previous line and add an Ellipsis
                else {
                    int start = layout.getLineStart(lastLine);
                    int end = layout.getLineEnd(lastLine);
                    float lineWidth = layout.getLineWidth(lastLine);
                    float ellipseWidth = paintCopy.measureText(mEllipsis);

                    // Trim characters off until we have enough room to draw the
                    // Ellipsis
                    while (width < lineWidth + ellipseWidth) {
                        lineWidth = paintCopy.measureText(text.subSequence(
                                start, --end + 1).toString());
                    }
                    setText(text.subSequence(0, end) + mEllipsis);
                }
            }
        }

        // Some devices try to auto adjust line spacing, so force default line
        // spacing
        // and invalidate the layout as a side effect
        setTextSize(TypedValue.COMPLEX_UNIT_PX, targetTextSize);
        setLineSpacing(mSpacingAdd, mSpacingMult);

        // Notify the listener if registered
        if (mTextResizeListener != null) {
            mTextResizeListener.onTextResize(this, oldTextSize, targetTextSize);
        }

        // Reset force resize flag
        mNeedsResize = false;
    }

    // Set the text size of the text Paint object and use a static layout to
    // render text off screen before measuring
    private int getTextHeight(CharSequence source, TextPaint originalPaint,
            int width, float textSize) {
        // modified: make a copy of the original TextPaint object for measuring
        // (apparently the object gets modified while measuring, see also the
        // docs for TextView.getPaint() (which states to access it read-only)
        TextPaint Paint = new TextPaint(originalPaint);
        // Update the text Paint object
        Paint.setTextSize(textSize);
        // Measure using a static layout
        StaticLayout layout = new StaticLayout(source, Paint, width,
                Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, true);
        return layout.getHeight();
    }

    // Set the text size of the text Paint object and use a static layout to
    // render text off screen before measuring
    private int getTextWidth(CharSequence source, TextPaint originalPaint,
            int width, float textSize) {
        // Update the text Paint object
        TextPaint Paint = new TextPaint(originalPaint);
        // Draw using a static layout
        Paint.setTextSize(textSize);

        StaticLayout layout = new StaticLayout(source, Paint, width,
                Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, true);

        return (int) layout.getLineWidth(0);
    }
}
1
Toxid

Wenn jemand es braucht, ist hier das gleiche Code-Snippet, aber für Xamarin.Android.

/**
 *               DO WHAT YOU WANT TO PUBLIC LICENSE
 *                    Version 1, December 2017
 * 
 * Copyright (C) 2017 Nathan Westfall
 * 
 * Everyone is permitted to copy and distribute verbatim or modified
 * copies of this license document, and changing it is allowed as long
 * as the name is changed.
 * 
 *            DO WHAT YOU WANT TO PUBLIC LICENSE
 *   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
 * 
 *  0. You just DO WHAT YOU WANT TO.
 */

using Android.Content;
using Android.Runtime;
using Android.Widget;
using Android.Util;
using Android.Text;

using Java.Lang;

namespace My.Text
{
    public class AutoResizeTextView : TextView
    {
        public const float MIN_TEXT_SIZE = 20;

        public interface OnTextResizeListener
        {
            void OnTextResize(TextView textView, float oldSize, float newSize);
        }

        private const string mEllipsis = "...";

        private OnTextResizeListener mTextResizeListener;

        private bool mNeedsResize = false;

        private float mTextSize;

        private float mMaxTextSize = 0;

        private float mMinTextSize = MIN_TEXT_SIZE;

        private float mSpacingMult = 1.0f;

        private float mSpacingAdd = 0.0f;

        public bool AddEllipsis { get; set; } = true;

        public AutoResizeTextView(Context context) : this(context, null) { }

        public AutoResizeTextView(Context context, IAttributeSet attrs) : this(context, attrs, 0) { }

        public AutoResizeTextView(Context context, IAttributeSet attrs, int defStyle): base(context, attrs, defStyle)
        {
            mTextSize = TextSize;
        }

        protected override void OnTextChanged(ICharSequence text, int start, int lengthBefore, int lengthAfter)
        {
            base.OnTextChanged(text, start, lengthBefore, lengthAfter);

            mNeedsResize = true;

            ResetTextSize();
        }

        protected override void OnSizeChanged(int w, int h, int oldw, int oldh)
        {
            base.OnSizeChanged(w, h, oldw, oldh);

            if (w != oldw || h != oldh)
                mNeedsResize = true;
        }

        public void SetOnResizeListener(OnTextResizeListener listener)
        {
            mTextResizeListener = listener;
        }

        public override void SetTextSize([GeneratedEnum] ComplexUnitType unit, float size)
        {
            base.SetTextSize(unit, size);

            mTextSize = TextSize;
        }

        public override void SetLineSpacing(float add, float mult)
        {
            base.SetLineSpacing(add, mult);

            mSpacingMult = mult;
            mSpacingAdd = add;
        }

        public void SetMaxTextSize(float maxTextSize)
        {
            mMaxTextSize = maxTextSize;
            RequestLayout();
            Invalidate();
        }

        public float GetMaxTextSize()
        {
            return mMaxTextSize;
        }

        public void SetMinTextSize(float minTextSize)
        {
            mMinTextSize = minTextSize;
            RequestLayout();
            Invalidate();
        }

        public float GetMinTextSize()
        {
            return mMinTextSize;
        }

        public void ResetTextSize()
        {
            if(mTextSize > 0)
            {
                base.SetTextSize(ComplexUnitType.Px, mTextSize);
                mMaxTextSize = mTextSize;
            }
        }

        protected override void OnLayout(bool changed, int left, int top, int right, int bottom)
        {
            if(changed || mNeedsResize)
            {
                int widthLimit = (right - left) - CompoundPaddingLeft - CompoundPaddingRight;
                int heightLimit = (bottom - top) - CompoundPaddingBottom - CompoundPaddingTop;
                ResizeText(widthLimit, heightLimit);
            }
            base.OnLayout(changed, left, top, right, bottom);

            base.OnLayout(changed, left, top, right, bottom);
        }

        public void ResizeText()
        {
            int heightLimit = Height - PaddingBottom - PaddingTop;
            int widthLimit = Width - PaddingLeft - PaddingRight;
            ResizeText(widthLimit, heightLimit);
        }

        public void ResizeText(int width, int height)
        {
            var text = TextFormatted;
            if (text == null || text.Length() == 0 || height <= 0 || width <= 0 || mTextSize == 0)
                return;

            if (TransformationMethod != null)
                text = TransformationMethod.GetTransformationFormatted(TextFormatted, this);

            TextPaint textPaint = Paint;

            float oldTextSize = textPaint.TextSize;
            float targetTextSize = mMaxTextSize > 0 ? System.Math.Min(mTextSize, mMaxTextSize) : mTextSize;

            int textHeight = GetTextHeight(text, textPaint, width, targetTextSize);

            while(textHeight > height && targetTextSize > mMinTextSize)
            {
                targetTextSize = System.Math.Max(targetTextSize - 2, mMinTextSize);
                textHeight = GetTextHeight(text, textPaint, width, targetTextSize);
            }

            if(AddEllipsis && targetTextSize == mMinTextSize && textHeight > height)
            {
                TextPaint Paint = new TextPaint(textPaint);

                StaticLayout layout = new StaticLayout(text, Paint, width, Layout.Alignment.AlignNormal, mSpacingMult, mSpacingAdd, false);
                if(layout.LineCount > 0)
                {
                    int lastLine = layout.GetLineForVertical(height) - 1;
                    if (lastLine < 0)
                        SetText("", BufferType.Normal);
                    else
                    {
                        int start = layout.GetLineStart(lastLine);
                        int end = layout.GetLineEnd(lastLine);
                        float lineWidth = layout.GetLineWidth(lastLine);
                        float ellipseWidth = textPaint.MeasureText(mEllipsis);

                        while (width < lineWidth + ellipseWidth)
                            lineWidth = textPaint.MeasureText(text.SubSequence(start, --end + 1).ToString());
                        SetText(text.SubSequence(0, end) + mEllipsis, BufferType.Normal);
                    }
                }
            }

            SetTextSize(ComplexUnitType.Px, targetTextSize);
            SetLineSpacing(mSpacingAdd, mSpacingMult);

            mTextResizeListener?.OnTextResize(this, oldTextSize, targetTextSize);

            mNeedsResize = false;
        }

        private int GetTextHeight(ICharSequence source, TextPaint Paint, int width, float textSize)
        {
            TextPaint paintCopy = new TextPaint(Paint);
            paintCopy.TextSize = textSize;
            StaticLayout layout = new StaticLayout(source, paintCopy, width, Layout.Alignment.AlignNormal, mSpacingMult, mSpacingAdd, false);
            return layout.Height;
        }
    }
}
1
nwestfall

Ich habe gerade die folgende Methode erstellt (basierend auf den Ideen von Chase), die Ihnen helfen könnte, wenn Sie Text auf eine beliebige Leinwand zeichnen möchten:

private static void drawText(Canvas canvas, int xStart, int yStart,
        int xWidth, int yHeigth, String textToDisplay,
        TextPaint paintToUse, float startTextSizeInPixels,
        float stepSizeForTextSizeSteps) {

    // Text view line spacing multiplier
    float mSpacingMult = 1.0f;
    // Text view additional line spacing
    float mSpacingAdd = 0.0f;
    StaticLayout l = null;
    do {
        paintToUse.setTextSize(startTextSizeInPixels);
        startTextSizeInPixels -= stepSizeForTextSizeSteps;
        l = new StaticLayout(textToDisplay, paintToUse, xWidth,
                Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, true);
    } while (l.getHeight() > yHeigth);

    int textCenterX = xStart + (xWidth / 2);
    int textCenterY = (yHeigth - l.getHeight()) / 2;

    canvas.save();
    canvas.translate(textCenterX, textCenterY);
    l.draw(canvas);
    canvas.restore();
}

Dies könnte z.B. in einer beliebigen onDraw () -Methode einer beliebigen benutzerdefinierten Ansicht.

1
Simon Heinen

Ich habe Code von Chase und M-WaJeEh verwendet und hier einige Vor- und Nachteile festgestellt

von der Jagd

Vorteil:

  • es ist perfekt für 1 Zeile TextView

Nachteil:

  • wenn es sich um mehr als eine Zeile mit benutzerdefinierter Schriftart handelt, wird ein Teil des Texts ausgeblendet

  • wenn es sich um eine aktivierte Ellipse handelt, wurde kein Platz für die Ellipse vorbereitet

  • wenn es sich um eine benutzerdefinierte Schriftart (Schriftart) handelt, wird diese nicht unterstützt

von M-WaJeEh

Vorteil:

  • es ist perfekt für Multi-Line

Nachteil:

  • wenn Sie die Höhe als Umbruchinhalt festlegen, beginnt dieser Code mit der Mindestgröße und wird auf die kleinste Größe reduziert, nicht mit der eingestellten Größe und wird um die begrenzte Breite reduziert

  • wenn es sich um eine benutzerdefinierte Schriftart (Schriftart) handelt, wird diese nicht unterstützt

1
Sruit A.Suk

Hier ist noch eine andere Lösung, nur für Kicks. Es ist wahrscheinlich nicht sehr effizient, aber es wird sowohl der Höhe als auch der Breite des Texts und des markierten Texts gerecht.

@Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec)
{
    if ((MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED)
            && (MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.UNSPECIFIED)) {

        final float desiredWidth = MeasureSpec.getSize(widthMeasureSpec);
        final float desiredHeight = MeasureSpec.getSize(heightMeasureSpec);

        float textSize = getTextSize();
        float lastScale = Float.NEGATIVE_INFINITY;
        while (textSize > MINIMUM_AUTO_TEXT_SIZE_PX) {
            // Measure how big the textview would like to be with the current text size.
            super.onMeasure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);

            // Calculate how much we'd need to scale it to fit the desired size, and
            // apply that scaling to the text size as an estimate of what we need.
            final float widthScale = desiredWidth / getMeasuredWidth();
            final float heightScale = desiredHeight / getMeasuredHeight();
            final float scale = Math.min(widthScale, heightScale);

            // If we don't need to shrink the text, or we don't seem to be converging, we're done.
            if ((scale >= 1f) || (scale <= lastScale)) {
                break;
            }

            // Shrink the text size and keep trying.
            textSize = Math.max((float) Math.floor(scale * textSize), MINIMUM_AUTO_TEXT_SIZE_PX);
            setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
            lastScale = scale;
        }
    }
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
1
Squatting Bear

Hier ist der Ansatz, den ich nehme. Es ist sehr einfach. Es verwendet die sukzessive Annäherung an die Schriftgröße und kann diese in der Regel in weniger als 10 Iterationen ermitteln. Ersetzen Sie "activityWidth" einfach durch die Breite der Ansicht, in der Sie den Text anzeigen. In meinem Beispiel wird die Breite des Bildschirms als privates Feld festgelegt. Die Anfangsschriftgröße von 198 wird nur für den Fall festgelegt, dass die Methode eine Ausnahme generiert (was eigentlich nie passieren sollte):

  private float GetFontSizeForScreenWidth(String text)
  {
    float fontsize = 198;

    try
    {
      Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
      Paint.setColor(Color.RED);
      Typeface typeface = Typeface.create("Helvetica", Typeface.BOLD);
      Paint.setTypeface(typeface);
      Paint.setTextAlign(Align.CENTER);

      int lowVal = 0;
      int highVal = 2000;
      int currentVal = highVal;

      /*
       * Successively approximate the screen size until it is 
       * within 2 pixels of the maximum screen width. Generally
       * this will get you to the closest font size within about 10
       * iterations.
       */

      do
      {
        Paint.setTextSize(currentVal);
        float textWidth = Paint.measureText(text);

        float diff = activityWidth - textWidth;

        if ((diff >= 0) && (diff <= 2))
        {
          fontsize = Paint.getTextSize();
          return fontsize;
        }

        if (textWidth > activityWidth)
          highVal = currentVal;
        else if (textWidth < activityWidth)
          lowVal = currentVal;
        else
        {
          fontsize = Paint.getTextSize();
          return fontsize;
        }

        currentVal = (highVal - lowVal) / 2 + lowVal;

      } while (true);      
    }
    catch (Exception ex)
    {
      return fontsize;
    }
  }
0
AndroidDev

Erweitern Sie TextView und überschreiben Sie onDraw mit dem folgenden Code. Das Seitenverhältnis des Texts bleibt erhalten, die Größe wird jedoch so angepasst, dass der Raum gefüllt wird. Sie können den Code bei Bedarf problemlos ändern, um ihn zu dehnen.

  @Override
  protected void onDraw(@NonNull Canvas canvas) {
    TextPaint textPaint = getPaint();
    textPaint.setColor(getCurrentTextColor());
    textPaint.setTextAlign(Paint.Align.CENTER);
    textPaint.drawableState = getDrawableState();

    String text = getText().toString();
    float desiredWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - 2;
    float desiredHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom() - 2;
    float textSize = textPaint.getTextSize();

    for (int i = 0; i < 10; i++) {
      textPaint.getTextBounds(text, 0, text.length(), rect);
      float width = rect.width();
      float height = rect.height();

      float deltaWidth = width - desiredWidth;
      float deltaHeight = height - desiredHeight;

      boolean fitsWidth = deltaWidth <= 0;
      boolean fitsHeight = deltaHeight <= 0;

      if ((fitsWidth && Math.abs(deltaHeight) < 1.0)
          || (fitsHeight && Math.abs(deltaWidth) < 1.0)) {
        // close enough
        break;
      }

      float adjustX = desiredWidth / width;
      float adjustY = desiredHeight / height;

      textSize = textSize * (adjustY < adjustX ? adjustY : adjustX);

      // adjust text size
      textPaint.setTextSize(textSize);
    }
    float x = desiredWidth / 2f;
    float y = desiredHeight / 2f - rect.top - rect.height() / 2f;
    canvas.drawText(text, x, y, textPaint);
  }
0
Greg Bacchus

Diese Lösung funktioniert bei uns:

public class CustomFontButtonTextFit extends CustomFontButton
{
    private final float DECREMENT_FACTOR = .1f;

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

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

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

    private synchronized void refitText(String text, int textWidth) {
        if (textWidth > 0) 
        {
            float availableWidth = textWidth - this.getPaddingLeft()
                    - this.getPaddingRight();

            TextPaint tp = getPaint();
            Rect rect = new Rect();
            tp.getTextBounds(text, 0, text.length(), rect);
            float size = rect.width();

            while(size > availableWidth)
            {
                setTextSize( getTextSize() - DECREMENT_FACTOR );
                tp = getPaint();

                tp.getTextBounds(text, 0, text.length(), rect);
                size = rect.width();
            }
        }
    }

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

        int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
        int parentHeight = MeasureSpec.getSize(heightMeasureSpec);

        refitText(this.getText().toString(), parentWidth);

        if(parentWidth < getSuggestedMinimumWidth())
            parentWidth = getSuggestedMinimumWidth();

        if(parentHeight < getSuggestedMinimumHeight())
            parentHeight = getSuggestedMinimumHeight();

        this.setMeasuredDimension(parentWidth, parentHeight);
    }

    @Override
    protected void onTextChanged(final CharSequence text, final int start,
            final int before, final int after) 
    {
        super.onTextChanged(text, start, before, after);

        refitText(text.toString(), this.getWidth());
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh)
    {
        super.onSizeChanged(w, h, oldw, oldh);

        if (w != oldw) 
            refitText(this.getText().toString(), w);
    }
}
0
Springfeed

Dank Chase und Onoelle, für die faulen Programmierer, lassen Sie mich hier eine funktionierende Version ihres fantastischen zusammengeführten Codes veröffentlichen, der auf einem Button anstatt auf einer Textansicht angepasst wurde.

Ersetzen Sie alle Ihre Schaltflächen (nicht ImageButtons) mit AutoResizeTextButtons und das gleiche langweilige Problem ist auch für sie behoben.

Hier ist der Code. Ich habe gerade die Importe entfernt.

/**
 *            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
 *                    Version 2, December 2004
 * 
 * Copyright (C) 2004 Sam Hocevar <[email protected]>
 * 
 * Everyone is permitted to copy and distribute verbatim or modified
 * copies of this license document, and changing it is allowed as long
 * as the name is changed.
 * 
 *            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
 *   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
 * 
 *  0. You just DO WHAT THE FUCK YOU WANT TO.
 *  made better by onoelle
 *  adapted for button by beppi
 */

/**
 * Text Button that auto adjusts text size to fit within the view.
 * If the text size equals the minimum text size and still does not
 * fit, append with an Ellipsis.
 * 
 * @author Chase Colburn
 * @since Apr 4, 2011
 */
public class AutoResizeTextButton extends Button {

    // Minimum text size for this text view
    public static final float MIN_TEXT_SIZE = 20;

    // Interface for resize notifications
    public interface OnTextResizeListener {
        public void onTextResize(Button textView, float oldSize, float newSize);
    }

    // Our ellipse string
    private static final String mEllipsis = "...";

    // Registered resize listener
    private OnTextResizeListener mTextResizeListener;

    // Flag for text and/or size changes to force a resize
    private boolean mNeedsResize = false;

    // Text size that is set from code. This acts as a starting point for resizing
    private float mTextSize;

    // Temporary upper bounds on the starting text size
    private float mMaxTextSize = 0;

    // Lower bounds for text size
    private float mMinTextSize = MIN_TEXT_SIZE;

    // Text view line spacing multiplier
    private float mSpacingMult = 1.0f;

    // Text view additional line spacing
    private float mSpacingAdd = 0.0f;

    // Add Ellipsis to text that overflows at the smallest text size
    private boolean mAddEllipsis = true;

    // Default constructor override
    public AutoResizeTextButton(Context context) {
        this(context, null);
    }

    // Default constructor when inflating from XML file
    public AutoResizeTextButton(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    // Default constructor override
    public AutoResizeTextButton(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mTextSize = getTextSize();
    }

    /**
     * When text changes, set the force resize flag to true and reset the text size.
     */
    @Override
    protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) {
        mNeedsResize = true;
        // Since this view may be reused, it is good to reset the text size
        resetTextSize();
    }

    /**
     * If the text view size changed, set the force resize flag to true
     */
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        if (w != oldw || h != oldh) {
            mNeedsResize = true;
        }
    }

    /**
     * Register listener to receive resize notifications
     * @param listener
     */
    public void setOnResizeListener(OnTextResizeListener listener) {
        mTextResizeListener = listener;
    }

    /**
     * Override the set text size to update our internal reference values
     */
    @Override
    public void setTextSize(float size) {
        super.setTextSize(size);
        mTextSize = getTextSize();
    }

    /**
     * Override the set text size to update our internal reference values
     */
    @Override
    public void setTextSize(int unit, float size) {
        super.setTextSize(unit, size);
        mTextSize = getTextSize();
    }

    /**
     * Override the set line spacing to update our internal reference values
     */
    @Override
    public void setLineSpacing(float add, float mult) {
        super.setLineSpacing(add, mult);
        mSpacingMult = mult;
        mSpacingAdd = add;
    }

    /**
     * Set the upper text size limit and invalidate the view
     * @param maxTextSize
     */
    public void setMaxTextSize(float maxTextSize) {
        mMaxTextSize = maxTextSize;
        requestLayout();
        invalidate();
    }

    /**
     * Return upper text size limit
     * @return
     */
    public float getMaxTextSize() {
        return mMaxTextSize;
    }

    /**
     * Set the lower text size limit and invalidate the view
     * @param minTextSize
     */
    public void setMinTextSize(float minTextSize) {
        mMinTextSize = minTextSize;
        requestLayout();
        invalidate();
    }

    /**
     * Return lower text size limit
     * @return
     */
    public float getMinTextSize() {
        return mMinTextSize;
    }

    /**
     * Set flag to add Ellipsis to text that overflows at the smallest text size
     * @param addEllipsis
     */
    public void setAddEllipsis(boolean addEllipsis) {
        mAddEllipsis = addEllipsis;
    }

    /**
     * Return flag to add Ellipsis to text that overflows at the smallest text size
     * @return
     */
    public boolean getAddEllipsis() {
        return mAddEllipsis;
    }

    /**
     * Reset the text to the original size
     */
    public void resetTextSize() {
        if(mTextSize > 0) {
            super.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);
            mMaxTextSize = mTextSize;
        }
    }

    /**
     * Resize text after measuring
     */
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        if(changed || mNeedsResize) {
            int widthLimit = (right - left) - getCompoundPaddingLeft() - getCompoundPaddingRight();
            int heightLimit = (bottom - top) - getCompoundPaddingBottom() - getCompoundPaddingTop();
            resizeText(widthLimit, heightLimit);
        }
        super.onLayout(changed, left, top, right, bottom);
    }


    /**
     * Resize the text size with default width and height
     */
    public void resizeText() {
        int heightLimit = getHeight() - getPaddingBottom() - getPaddingTop();
        int widthLimit = getWidth() - getPaddingLeft() - getPaddingRight();
        resizeText(widthLimit, heightLimit);
    }

    /**
     * Resize the text size with specified width and height
     * @param width
     * @param height
     */
    public void resizeText(int width, int height) {
        CharSequence text = getText();
        // Do not resize if the view does not have dimensions or there is no text
        if(text == null || text.length() == 0 || height <= 0 || width <= 0 || mTextSize == 0) {
            return;
        }

        // Get the text view's Paint object
        TextPaint textPaint = getPaint();

        // Store the current text size
        float oldTextSize = textPaint.getTextSize();
        // If there is a max text size set, use the lesser of that and the default text size
        float targetTextSize = mMaxTextSize > 0 ? Math.min(mTextSize, mMaxTextSize) : mTextSize;

        // Get the required text height
        int textHeight = getTextHeight(text, textPaint, width, targetTextSize);

        // Until we either fit within our text view or we had reached our min text size, incrementally try smaller sizes
        while(textHeight > height && targetTextSize > mMinTextSize) {
            targetTextSize = Math.max(targetTextSize - 2, mMinTextSize);
            textHeight = getTextHeight(text, textPaint, width, targetTextSize);
        }

        // If we had reached our minimum text size and still don't fit, append an Ellipsis
        if(mAddEllipsis && targetTextSize == mMinTextSize && textHeight > height) {
            // Draw using a static layout
            // modified: use a copy of TextPaint for measuring
            TextPaint Paint = new TextPaint(textPaint);
            StaticLayout layout = new StaticLayout(text, Paint, width, Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, false);
            // Check that we have a least one line of rendered text
            if(layout.getLineCount() > 0) {
                // Since the line at the specific vertical position would be cut off,
                // we must trim up to the previous line
                int lastLine = layout.getLineForVertical(height) - 1;
                // If the text would not even fit on a single line, clear it
                if(lastLine < 0) {
                    setText("");
                }
                // Otherwise, trim to the previous line and add an Ellipsis
                else {
                    int start = layout.getLineStart(lastLine);
                    int end = layout.getLineEnd(lastLine);
                    float lineWidth = layout.getLineWidth(lastLine);
                    float ellipseWidth = textPaint.measureText(mEllipsis);

                    // Trim characters off until we have enough room to draw the Ellipsis
                    while(width < lineWidth + ellipseWidth) {
                        lineWidth = textPaint.measureText(text.subSequence(start, --end + 1).toString());
                    }
                    setText(text.subSequence(0, end) + mEllipsis);
                }
            }
        }

        // Some devices try to auto adjust line spacing, so force default line spacing
        // and invalidate the layout as a side effect
//      textPaint.setTextSize(targetTextSize);
     // modified: setting text size via this.setTextSize (instead of textPaint.setTextSize(targetTextSize))
        setTextSize(TypedValue.COMPLEX_UNIT_PX, targetTextSize);
        setLineSpacing(mSpacingAdd, mSpacingMult);

        // Notify the listener if registered
        if(mTextResizeListener != null) {
            mTextResizeListener.onTextResize(this, oldTextSize, targetTextSize);
        }

        // Reset force resize flag
        mNeedsResize = false;
    }

    // Set the text size of the text Paint object and use a static layout to render text off screen before measuring
    private int getTextHeight(CharSequence source, TextPaint originalPaint, int width, float textSize) {
          // modified: make a copy of the original TextPaint object for measuring
          // (apparently the object gets modified while measuring, see also the
          // docs for TextView.getPaint() (which states to access it read-only)
        // Update the text Paint object
          TextPaint Paint = new TextPaint(originalPaint);
        Paint.setTextSize(textSize);
        // Measure using a static layout
        StaticLayout layout = new StaticLayout(source, Paint, width, Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, true);
        return layout.getHeight();
    }

}

Verwendungszweck:

fügen Sie anstelle eines normalen Buttons einen AutoResizeTextButton in Ihre XML-Datei ein, ohne etwas anderes zu ändern. In den onCreate () put (zum Beispiel):

    myButton = (AutoResizeTextButton)getView().findViewById(id.myButton);
    myButton.setMinTextSize(8f);
    myButton.resizeText();
0
Beppi's

Lesen Sie den ScalableTextView.Java hier Auto-Fit TextView für Android . Ich habe den Code hinzugefügt, um die Textansicht basierend auf der Textlänge zu verkleinern und zu erweitern

0
Devendra Vaja