2014-09-29 93 views
0

我非常感激地看到一段漂亮的代碼,它可以動態地測量一個textView框並相應地調整它內部的文本大小。這一切都很棒,但它只對一行文本有用,我將如何確保文本一旦到達框邊緣就開始新行?我怎樣才能跨越多行這個文本?

FillTextView

public class FillTextView extends TextView { 

// Scaling factor 
private static final float VERTICAL_FONT_SCALING_FACTOR = 0.9f; 

// Attributes 
private Paint mTestPaint; 

public FillTextView(Context context) { 
    super(context); 
    initialise(); 
} 

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

private void initialise() { 
    mTestPaint = new Paint(); 
    mTestPaint.set(this.getPaint()); 
} 

/* 
* Resize the font so the specified text fits in the text box assuming the 
* text box is the specified width. 
*/ 
private void refitText(String text, int textWidth, int textHeight) { 
    if (textHeight <= 0 || textWidth <= 0) { 
     return; 
    } 

    // Find target height 
    float targetTextSizeVertical = (textHeight - this.getPaddingTop() - this.getPaddingBottom()) * VERTICAL_FONT_SCALING_FACTOR; 

    // Find target width 
    float targetWidth = textWidth - this.getPaddingLeft() - this.getPaddingRight(); 

    float hi = 800; 
    float lo = 2; 
    final float threshold = 0.5f; // How close we have to be 

    mTestPaint.set(this.getPaint()); 

    while ((hi - lo) > threshold) { 
     float size = (hi + lo)/2; 
     mTestPaint.setTextSize(size); 
     if (mTestPaint.measureText(text) >= targetWidth) 
      hi = size; // too big 
     else 
      lo = size; // too small 
    } 
    float targetTextSizeHorizontal = lo; 

    // Set the text size 
    float targetTextSize = Math.min(targetTextSizeVertical, targetTextSizeHorizontal); 

    this.setTextSize(TypedValue.COMPLEX_UNIT_PX, (int) targetTextSize); 
} 

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

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

    refitText(this.getText().toString(), parentWidth, parentHeight); 
    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(), this.getHeight()); 
} 

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

} 

回答

0

經過一番徹底搜索這個作品在這裏請客

public class FillTextView extends TextView { 

/** Our ellipsis string. */ 
private static final String mEllipsis = "\u2026"; 

/** 
* Upper bounds for text size. 
* This acts as a starting point for resizing. 
*/ 
private int mMaxTextSizePixels; 

/** Lower bounds for text size. */ 
private int mMinTextSizePixels; 

/** TextView line spacing multiplier. */ 
private float mLineSpacingMultiplier = 1.0f; 

/** TextView additional line spacing. */ 
private float mLineSpacingExtra = 0.0f; 

/** 
* Default constructor override. 
* 
* @param context 
*/ 
public FillTextView(Context context) { 
    this(context, null); 
    initialise(); 
} 

/** 
* Default constructor when inflating from XML file. 
* 
* @param context 
* @param attrs 
*/ 
public FillTextView(Context context, AttributeSet attrs) { 
    this(context, attrs, 0); 
    initialise(); 
} 

/** 
* Default constructor override. 
* 
* @param context 
* @param attrs 
* @param defStyle 
*/ 
public FillTextView(
     Context context, 
     AttributeSet attrs, 
     int defStyle) { 
    super(context, attrs, defStyle); 
    initialise(); 
} 

@Override 
protected void onLayout (
     boolean changed, 
     int left, 
     int top, 
     int right, 
     int bottom) { 
    super.onLayout(changed, left, top, right, bottom); 
    resizeText(); 
} 

@Override 
protected void onTextChanged(
     CharSequence text, 
     int start, 
     int lengthBefore, 
     int lengthAfter) { 
    super.onTextChanged(text, start, lengthBefore, lengthAfter); 
    requestLayout(); 
} 

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

    if (w != oldw || h != oldh) { 
     requestLayout(); 
    } 
} 

@Override 
public void setTextSize(float size) { 
    setTextSize(TypedValue.COMPLEX_UNIT_SP, size); 
} 

@Override 
public void setTextSize(int unit, float size) { 
    super.setTextSize(unit, size); 
    mMaxTextSizePixels = (int) getTextSize(); 
    requestLayout(); 
} 

@Override 
public void setLineSpacing(float add, float mult) { 
    super.setLineSpacing(add, mult); 
    mLineSpacingMultiplier = mult; 
    mLineSpacingExtra = add; 
    requestLayout(); 
} 

@Override 
public void setEllipsize(TextUtils.TruncateAt where) { 
    super.setEllipsize(where); 
    requestLayout(); 
} 

/** 
* Sets the lower text size limit and invalidates the view. 
* 
* @param minTextSizeScaledPixels the minimum size to use for text in this view, 
* in scaled pixels. 
*/ 
public void setMinTextSize(int minTextSizeScaledPixels) { 
    mMinTextSizePixels = convertScaledPixelsToPixels(minTextSizeScaledPixels); 
    requestLayout(); 
} 

/** 
* @return lower text size limit, in pixels. 
*/ 
public float getMinTextSizePixels() { 
    return mMinTextSizePixels; 
} 

private void initialise() { 
    mMaxTextSizePixels = (int) getTextSize(); 
} 

/** 
* Resizes this view's text size with respect to its width and height 
* (minus padding). 
*/ 
private void resizeText() { 
    final int availableHeightPixels = getHeight() 
      - getCompoundPaddingBottom() - getCompoundPaddingTop(); 

    final int availableWidthPixels = getWidth() 
      - getCompoundPaddingLeft() - getCompoundPaddingRight(); 

    final CharSequence text = getText(); 

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

    int targetTextSizePixels = mMaxTextSizePixels; 
    int targetTextHeightPixels = getTextHeightPixels(
      text, 
      availableWidthPixels, 
      targetTextSizePixels); 

    // Until we either fit within our TextView 
    // or we have reached our minimum text size, 
    // incrementally try smaller sizes 
    while (targetTextHeightPixels > availableHeightPixels 
      && targetTextSizePixels > mMinTextSizePixels) { 
     targetTextSizePixels = Math.max(
       targetTextSizePixels - 2, 
       mMinTextSizePixels); 

     targetTextHeightPixels = getTextHeightPixels(
       text, 
       availableWidthPixels, 
       targetTextSizePixels); 
    } 

    // If we have reached our minimum text size and the text still doesn't fit, 
    // append an ellipsis 
    // (NOTE: Auto-ellipsize doesn't work hence why we have to do it here) 
    // (TODO: put ellipsis at the beginning, middle or end 
    // depending on the value of getEllipsize()) 
    if (getEllipsize() != null 
      && targetTextSizePixels == mMinTextSizePixels 
      && targetTextHeightPixels > availableHeightPixels) { 
     // Make a copy of the original TextPaint object for measuring 
     TextPaint textPaintCopy = new TextPaint(getPaint()); 
     textPaintCopy.setTextSize(targetTextSizePixels); 

     // Measure using a StaticLayout instance 
     StaticLayout staticLayout = new StaticLayout(
       text, 
       textPaintCopy, 
       availableWidthPixels, 
       Layout.Alignment.ALIGN_NORMAL, 
       mLineSpacingMultiplier, 
       mLineSpacingExtra, 
       false); 

     // Check that we have a least one line of rendered text 
     if (staticLayout.getLineCount() > 0) { 
      // Since the line at the specific vertical position would be cut off, 
      // we must trim up to the previous line and add an ellipsis 
      int lastLine = staticLayout.getLineForVertical(availableHeightPixels) - 1; 

      if (lastLine >= 0) { 
       int startOffset = staticLayout.getLineStart(lastLine); 
       int endOffset = staticLayout.getLineEnd(lastLine); 
       float lineWidthPixels = staticLayout.getLineWidth(lastLine); 
       float ellipseWidth = textPaintCopy.measureText(mEllipsis); 

       // Trim characters off until we have enough room to draw the ellipsis 
       while (availableWidthPixels < lineWidthPixels + ellipseWidth) { 
        endOffset--; 
        lineWidthPixels = textPaintCopy.measureText(
          text.subSequence(startOffset, endOffset + 1).toString()); 
       } 

       setText(text.subSequence(0, endOffset) + mEllipsis); 
      } 
     } 
    } 

    super.setTextSize(TypedValue.COMPLEX_UNIT_PX, targetTextSizePixels); 
    // Some devices try to auto adjust line spacing, so force default line spacing 
    super.setLineSpacing(mLineSpacingExtra, mLineSpacingMultiplier); 
} 

/** 
* Sets the text size of a clone of the view's {@link TextPaint} object 
* and uses a {@link StaticLayout} instance to measure the height of the text. 
* 
* @param source 
* @param availableWidthPixels 
* @param textSizePixels 
* @return the height of the text when placed in a view 
* with the specified width 
* and when the text has the specified size. 
*/ 
private int getTextHeightPixels(
     CharSequence source, 
     int availableWidthPixels, 
     float textSizePixels) { 
    // Make a copy of the original TextPaint object 
    // since the object gets modified while measuring 
    // (see also the docs for TextView.getPaint() 
    // which states to access it read-only) 
    TextPaint textPaintCopy = new TextPaint(getPaint()); 
    textPaintCopy.setTextSize(textSizePixels); 

    // Measure using a StaticLayout instance 
    StaticLayout staticLayout = new StaticLayout(
      source, 
      textPaintCopy, 
      availableWidthPixels, 
      Layout.Alignment.ALIGN_NORMAL, 
      mLineSpacingMultiplier, 
      mLineSpacingExtra, 
      true); 

    return staticLayout.getHeight(); 
} 

/** 
* @param scaledPixels 
* @return the number of pixels which 
scaledPixels 
corresponds to on the device. 
*/ 
private int convertScaledPixelsToPixels(int scaledPixels) { 
    float pixels = scaledPixels * getContext().getResources().getDisplayMetrics().scaledDensity; 
    return (int) pixels; 
} 
}