如何根据 android 中的最长值自动调整微调器的字体大小

How to auto adjust the font size of a spinner based on its longest value in android

我有一个微调器,我想在其中显示几个名字。 我根据 sqlite 查询获取名称。如何根据最长名称的长度减小字体大小?我可以使用并检查

textView.getText().length()  ?

天真:

  1. 寻找最长的字符串。
  2. 测量 TextView 的最大尺寸。
  3. 测量 适合 TextView 的文本。 (使用TextPaint,提供测量文本方法。)

你只需要搞清楚算法就可以了。可能是这样的。

size = TextView.getWidth();
paint = new TextPaint();
paint.setTextSize(SOME_BIG_NUMBER);
while(true){
   if (paint.measureText(longest_text) < size){
       break;
   } else {
       paint.setTextSize(paint.getTextSize() -1f);
   }
}
result size = paint.getTextSize();

另一种方法是使用 AutoResizeTextView。设置数据后,只需遍历所有 AutoResizeTextViews,找到最小值,然后将其设置为所有 TextViews。但它非常昂贵,用户会注意到绘图。

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 Typeface mTypeFace;

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();
    ArrayList<String> list = new ArrayList<>();
    int index = -1;
    for (String st : list) {
        index = index < st.length() ? st.length() :index;
    }

    TextPaint paint = new TextPaint();
    paint.measureText(list.get(index));
}

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());
    if (mTypeFace != null) {
        mPaint.setTypeface(mTypeFace);
    }
    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();
    if (mWidthLimit < 0) {
        mWidthLimit = 0;
    }
    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, Layout.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 {
            if (mTextRect.bottom < availableSPace.bottom
                && mTextRect.right > availableSPace.right) {
                // hack :O
                return -1;
            }
            // 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();
    }
}

@Override
public void setTypeface(Typeface tf) {
    super.setTypeface(tf);
    mTypeFace = tf;
    if (mInitiallized) {
        mPaint.setTypeface(tf);
        mTextCachedSizes.clear();
        reAdjust();
    }
}

@Override
public CharSequence getAccessibilityClassName() {
    return TextView.class.getName();
}
}

最好的方法是结合这些方法。查找最长的文本。简化 AutoResizeTextView 以仅计算 textSize。将此大小设置为您的 textViews。