Last active
December 4, 2015 14:50
-
-
Save descorp/b17789d50434e5d626b6 to your computer and use it in GitHub Desktop.
Converted code of AutoResizeTextView from http://stackoverflow.com/questions/5033012/auto-scale-textview-text-to-fit-within-bounds/17782522#17782522
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using Android.Widget; | |
| using Android.Graphics; | |
| using Android.Util; | |
| using Android.Text; | |
| using Android.Content; | |
| using Android.Runtime; | |
| using Java.Lang; | |
| using Android.Content.Res; | |
| namespace Timebook.Droid.Controls | |
| { | |
| [Register("com.timebook.android.controls.AutoResizeTextView")] | |
| public class AutoResizeTextView : TextView | |
| { | |
| private RectF mTextRect = new RectF(); | |
| private RectF mAvailableSpaceRect; | |
| private SparseIntArray mTextCachedSizes; | |
| internal TextPaint mPaint; | |
| private float mMaxTextSize; | |
| private float mSpacingMult = 1.0f; | |
| private float mSpacingAdd = 0.0f; | |
| private float mMinTextSize = 20; | |
| private int mWidthLimit; | |
| private static int NO_LINE_LIMIT = -1; | |
| private int mMaxLines; | |
| private bool mEnableSizeCache = true; | |
| private bool mInitiallized; | |
| public AutoResizeTextView(IntPtr javaReference, JniHandleOwnership transfer) | |
| : base(javaReference, transfer) | |
| { | |
| Initialize(); | |
| } | |
| public AutoResizeTextView(Context context) | |
| : base(context) | |
| { | |
| Initialize(); | |
| } | |
| public AutoResizeTextView(Context context, IAttributeSet attrs) | |
| : base(context, attrs) | |
| { | |
| Initialize(); | |
| } | |
| public AutoResizeTextView(Context context, IAttributeSet attrs, int defStyle) | |
| : base(context, attrs, defStyle) | |
| { | |
| Initialize(); | |
| } | |
| private void Initialize() | |
| { | |
| mPaint = new TextPaint(this.Paint); | |
| this.mMaxTextSize = this.TextSize; | |
| this.mAvailableSpaceRect = new RectF(); | |
| this.mTextCachedSizes = new SparseIntArray(); | |
| if (mMaxLines == 0) | |
| { | |
| // no value was assigned during construction | |
| mMaxLines = NO_LINE_LIMIT; | |
| } | |
| this.mSizeTester = new SizeTester(this); | |
| mInitiallized = true; | |
| } | |
| public override void SetText(ICharSequence text, Android.Widget.TextView.BufferType type) | |
| { | |
| base.SetText(text, type); | |
| AdjustTextSize(this.Text); | |
| } | |
| public override float TextSize | |
| { | |
| get | |
| { | |
| return base.TextSize; | |
| } | |
| set | |
| { | |
| base.TextSize = value; | |
| mMaxTextSize = value; | |
| mTextCachedSizes.Clear(); | |
| AdjustTextSize(this.Text); | |
| } | |
| } | |
| public override void SetMaxLines(int maxlines) | |
| { | |
| base.SetMaxLines(maxlines); | |
| mMaxLines = maxlines; | |
| ReAdjust(); | |
| } | |
| public override int MaxLines | |
| { | |
| get | |
| { | |
| return mMaxLines; | |
| } | |
| } | |
| public override void SetSingleLine() | |
| { | |
| base.SetSingleLine(); | |
| mMaxLines = 1; | |
| ReAdjust(); | |
| } | |
| public override void SetSingleLine(bool singleLine) | |
| { | |
| base.SetSingleLine(singleLine); | |
| if (singleLine) | |
| { | |
| mMaxLines = 1; | |
| } | |
| else | |
| { | |
| mMaxLines = NO_LINE_LIMIT; | |
| } | |
| ReAdjust(); | |
| } | |
| public override void SetLines(int lines) | |
| { | |
| base.SetLines(lines); | |
| mMaxLines = lines; | |
| ReAdjust(); | |
| } | |
| public override void SetTextSize(ComplexUnitType unit, float size) | |
| { | |
| Context c = this.Context; | |
| Resources r; | |
| if (c == null) | |
| { | |
| r = Resources.System; | |
| } | |
| else | |
| { | |
| r = c.Resources; | |
| } | |
| mMaxTextSize = TypedValue.ApplyDimension(unit, size, r.DisplayMetrics); | |
| mTextCachedSizes.Clear(); | |
| AdjustTextSize(this.Text); | |
| } | |
| public override void SetLineSpacing(float add, float mult) | |
| { | |
| base.SetLineSpacing(add, mult); | |
| mSpacingMult = mult; | |
| mSpacingAdd = add; | |
| } | |
| public float MinTextSize | |
| { | |
| set | |
| { | |
| mMinTextSize = value; | |
| ReAdjust(); | |
| } | |
| } | |
| private void ReAdjust() | |
| { | |
| AdjustTextSize(this.Text); | |
| } | |
| private void AdjustTextSize(string text) | |
| { | |
| if (!mInitiallized) | |
| { | |
| return; | |
| } | |
| int startSize = (int)mMinTextSize; | |
| int heightLimit = this.MeasuredHeight - this.CompoundPaddingBottom - this.CompoundPaddingTop; | |
| mWidthLimit = MeasuredWidth - CompoundPaddingLeft - CompoundPaddingRight; | |
| mAvailableSpaceRect.Right = mWidthLimit; | |
| mAvailableSpaceRect.Bottom = heightLimit; | |
| base.SetTextSize(ComplexUnitType.Px, EfficientTextSizeSearch(startSize, (int)mMaxTextSize, mSizeTester, mAvailableSpaceRect)); | |
| } | |
| public void EnableSizeCache(bool enable) | |
| { | |
| mEnableSizeCache = enable; | |
| mTextCachedSizes.Clear(); | |
| AdjustTextSize(this.Text); | |
| } | |
| private int EfficientTextSizeSearch(int start, int end, SizeTester sizeTester, RectF availableSpace) | |
| { | |
| if (!mEnableSizeCache) | |
| { | |
| return BinarySearch(start, end, sizeTester, availableSpace); | |
| } | |
| var text = this.Text; | |
| 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; | |
| } | |
| protected override void OnTextChanged(ICharSequence text, int start, int before, int after) | |
| { | |
| base.OnTextChanged(text, start, before, after); | |
| ReAdjust(); | |
| } | |
| protected override void OnSizeChanged(int width, int height, int oldwidth, int oldheight) | |
| { | |
| mTextCachedSizes.Clear(); | |
| base.OnSizeChanged(width, height, oldwidth, oldheight); | |
| if (width != oldwidth || height != oldheight) | |
| { | |
| ReAdjust(); | |
| } | |
| } | |
| private SizeTester mSizeTester; | |
| internal class SizeTester | |
| { | |
| AutoResizeTextView parent; | |
| public SizeTester(AutoResizeTextView parent) | |
| { | |
| this.parent = parent; | |
| } | |
| public int OnTestSize(int suggestedSize, RectF availableSpace) | |
| { | |
| this.parent.mPaint.TextSize = suggestedSize; | |
| var text = this.parent.Text; | |
| var singleline = this.parent.MaxLines == 1; | |
| if (singleline) | |
| { | |
| this.parent.mTextRect.Bottom = this.parent.mPaint.FontSpacing; | |
| this.parent.mTextRect.Right = this.parent.mPaint.MeasureText(text); | |
| } | |
| else | |
| { | |
| var layout = new StaticLayout(text, this.parent.mPaint, this.parent.mWidthLimit, Layout.Alignment.AlignNormal, this.parent.mSpacingMult, this.parent.mSpacingAdd, true); | |
| // return early if we have more lines | |
| if (this.parent.MaxLines != NO_LINE_LIMIT && layout.LineCount > this.parent.MaxLines) | |
| { | |
| return 1; | |
| } | |
| this.parent.Bottom = layout.Height; | |
| int maxWidth = -1; | |
| for (int i = 0; i < layout.LineCount; i++) | |
| { | |
| if (maxWidth < layout.GetLineWidth(i)) | |
| { | |
| maxWidth = (int)layout.GetLineWidth(i); | |
| } | |
| } | |
| this.parent.mTextRect.Right = maxWidth; | |
| } | |
| this.parent.mTextRect.OffsetTo(0, 0); | |
| if (availableSpace.Contains(this.parent.mTextRect)) | |
| { | |
| // may be too small, don't worry we will find the best match | |
| return -1; | |
| } | |
| else | |
| { | |
| // too big | |
| return 1; | |
| } | |
| } | |
| } | |
| } | |
| } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment