Quantcast
Channel: Recent Threads — Xamarin Community Forums
Viewing all articles
Browse latest Browse all 204402

AutoResizeTextView

$
0
0

Hi,

I have just finished translating the AutoResizeTextView class found here http://stackoverflow.com/questions/5033012/auto-scale-textview-text-to-fit-within-bounds

And thought I'd share the code:

using System; using Android.Content; using Android.Runtime; using Android.Text; using Android.Widget; using Android.Util; using Android.Graphics; using Java.Lang; using Math = System.Math; using String = System.String;

namespace AndroClient.CustomViews { /// /// TextView that automatically resizes it's content to fit the layout dimensions /// public class AutoResizeTextView : TextView {

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

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

    #region Fields
    private static readonly Canvas TextResizeCanvas = new Canvas();

    // Our ellipse string
    private const String Ellipsis = "...";

    // Registered resize listener
    private IOnTextResizeListener _textResizeListener;

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

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

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

    // Lower bounds for text size
    private float _minTextSize = MinTextSize;

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

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

    // Add ellipsis to text that overflows at the smallest text size
    private bool _addEllipsis = true;
    #endregion

    #region Constructors
    public AutoResizeTextView(IntPtr a, JniHandleOwnership b) : base(a, b) { }

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

    }

    // Default constructor when inflating from XML file
    public AutoResizeTextView(Context context, IAttributeSet attrs)
        : this(context, attrs, 0)
    {

    }

    // Default constructor override
    public AutoResizeTextView(Context context, IAttributeSet attrs, int defStyle)
        : base(context, attrs, defStyle)
    {
        _textSize = TextSize;
    }
    #endregion

    #region Public Methods
    //When text changes, set the force resize flag to true and reset the text size.
    protected override void OnTextChanged(ICharSequence text, int start, int before, int after)
    {
        _needsResize = 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
    protected override void OnSizeChanged(int w, int h, int oldw, int oldh)
    {
        if (w != oldw || h != oldh)
        {
            _needsResize = true;
        }
    }

    //Register listener to receive resize notifications
    public void SetOnResizeListener(IOnTextResizeListener listener)
    {
        _textResizeListener = listener;
    }


    public override void SetTextSize(ComplexUnitType unitType, float size)
    {
        base.SetTextSize(unitType, size);
        _textSize = TextSize;
    }

    /**
     * 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
    public override void SetLineSpacing(float add, float mult)
    {
        base.SetLineSpacing(add, mult);
        _spacingMult = mult;
        _spacingAdd = add;
    }

    //Set the upper text size limit and invalidate the view
    public void SetMaxTextSize(float maxTextSize)
    {
        _maxTextSize = maxTextSize;
        RequestLayout();
        Invalidate();
    }

    //Return upper text size limit
    public float GetMaxTextSize()
    {
        return _maxTextSize;
    }

    //Set the lower text size limit and invalidate the view
    public void SetMinTextSize(float minTextSize)
    {
        _minTextSize = minTextSize;
        RequestLayout();
        Invalidate();
    }

    //Return lower text size limit
    public float SetMinTextSize()
    {
        return _minTextSize;
    }

    //Set flag to add ellipsis to text that overflows at the smallest text size
    public void SetAddEllipsis(bool addEllipsis)
    {
        _addEllipsis = addEllipsis;
    }

    //Return flag to add ellipsis to text that overflows at the smallest text size
    public bool GetAddEllipsis()
    {
        return _addEllipsis;
    }

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

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

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


    // Resize the text size with specified width and height
    public void ResizeText(int width, int height)
    {
        string text = Text;
        // Do not resize if the view does not have dimensions or there is no text
        if (string.IsNullOrEmpty(text) || height <= 0 || width <= 0 || _textSize == 0)
            return;
        // Get the text view's paint object
        TextPaint textPaint = Paint;
        // Store the current text size
        float oldTextSize = textPaint.TextSize;
        // If there is a max text size set, use the lesser of that and the default text size
        float targetTextSize = _maxTextSize > 0 ? Math.Min(_textSize, _maxTextSize) : _textSize;

        // 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 > _minTextSize)
        {
            targetTextSize = Math.Max(targetTextSize - 2, _minTextSize);
            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 (_addEllipsis && targetTextSize == _minTextSize && textHeight > height)
        {
            // Draw using a static layout
            StaticLayout layout = new StaticLayout(text, textPaint, 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
                int lastLine = layout.GetLineForVertical(height) - 1;
                // If the text would not even fit on a single line, clear it
                if (lastLine < 0)
                {
                    Text = "";
                }
                // 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(Ellipsis);

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

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

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

        // Reset force resize flag
        _needsResize = false;
    }

    #endregion

    #region Private methods

    // Set the text size of the text paint object and use a static layout to render text off screen before measuring
    private int GetTextHeight(string source, TextPaint paint, int width, float textSize)
    {
        // Update the text paint object
        paint.TextSize = textSize;
        // Measure using a static layout
        StaticLayout layout = new StaticLayout(source, paint, width, Layout.Alignment.AlignNormal, _spacingMult, _spacingAdd, true);
        return layout.Height;
    }

    // Set the text size of the text paint object and use a static layout to render text off screen before measuring
    private int GetTextWidth(string source, TextPaint paint, int width, float textSize)
    {
        // Update the text paint object
        paint.TextSize = textSize;
        // Draw using a static layout
        StaticLayout layout = new StaticLayout(source, paint, width, Layout.Alignment.AlignNormal, _spacingMult, _spacingAdd, true);
        layout.Draw(TextResizeCanvas);
        return layout.Width;
    }
    #endregion
}

}


Viewing all articles
Browse latest Browse all 204402

Trending Articles