Gunaseelan
Gunaseelan

Reputation: 15535

Dynamically calculate text size to fill the space of rectangle

I have rectf which is drawn in canvas, based on this rectf I have drawn simple bubble alike. I want my text should be inside this bubble.

Here is what I have tried.

I have a textPaint which is initialized with textSize 30, and I'm building static layout like this.

staticLayout = StaticLayout.Builder.obtain(text, start, end, textPaint, width)
        .setAlignment(Layout.Alignment.ALIGN_NORMAL)
        .setTextDirection(TextDirectionHeuristics.LTR)
        .setLineSpacing(0, 1.0f)
        .setIncludePad(false)
        .setEllipsizedWidth(10)
        .setEllipsize(TextUtils.TruncateAt.START)
        .setMaxLines(Integer.MAX_VALUE)
        .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
        .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
        .setJustificationMode(Layout.JUSTIFICATION_MODE_NONE)
        .build()

From this static layout I'm getting my TextLayout's height as staticLayout.height

I want my text layout height should not exceed by 70% of my bounds height, so that my text will be always inside my bounds.

float maxTextLayoutHeight = bounds.height() * 0.70f;

And my actual static layout's height

height = staticLayout.height;

From these two values I recalculate text size and applied it to my text paint with following code.

annotationTextSize = (maxTextLayoutHeight / height) * 13;
annotationTextSize = annotationTextSize * 0.80f;
if (annotationTextSize < 25) annotationTextSize = 25; //Limited to min 25
if (annotationTextSize > 99) annotationTextSize = 99; //Limited to max 99
textPaint.setTextSize(annotationTextSize);

Here is some images

With small text size:

enter image description here

enter image description here

Text size looks perfect:

enter image description here

enter image description here

Text size looks bigger:

enter image description here

enter image description here

Any improved calculation will be much helpful.

Upvotes: 5

Views: 906

Answers (2)

Cheticamp
Cheticamp

Reputation: 62841

Unfortunately, I don't think that you will be able to determine the size of the laid out text until it is laid out. Internally, autosized TextViews do measurements of the text to find the best fit through a binary search that lays out the text for each candidate text size. (See AppCompatTextViewAutoSizeHelper here.

You can borrow some code from the AppCompatTextViewAutoSizeHelper or use something simpler as the following although it will not be as quick:

override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)
    var textSize = 99
    while (mStaticLayout == null) {
        var layout: StaticLayout
        textPaint.textSize = textSize.toFloat()
        layout = makeStaticLayout(mText, 0, mText.length, textPaint, width)
        if (layout.height < height || textSize == 25) {
            mStaticLayout = layout
        } else {
            textSize--
        }
    }
    mStaticLayout?.draw(canvas)
}

You could also use the internal workings of TextView to get a StaticLayout that can be drawn to a canvas.

private fun makeAutoSizeView(
    @StringRes textId: Int, width: Int, height: Int
): AppCompatTextView {
    return AppCompatTextView(this).apply {
        layoutParams = ConstraintLayout.LayoutParams(width, height)
        TextViewCompat.setAutoSizeTextTypeWithDefaults(
            this,
            TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM
        )
        text = resources.getString(textId)
    }
}

private fun getAutoSizeLayout(width: Int, height: Int): StaticLayout {
    val autoSizeTextView = makeAutoSizeView(R.string.good_to_see_you, width, height)
    val widthSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY)
    val heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)
    autoSizeTextView.measure(widthSpec, heightSpec)
    autoSizeTextView.layout(0, 0, width, height)
    return autoSizeTextView.layout as StaticLayout
}

Upvotes: 1

Ruthwik Warrier
Ruthwik Warrier

Reputation: 197

I calculated the height of text to be rendered using the following method. Might be helpful

private fun getMultilineTextHeight(text: CharSequence, textPaint: TextPaint, width: Int): Int {

        val alignment = Layout.Alignment.ALIGN_NORMAL
        val spacingMultiplier = 1f
        val spacingAddition = 0f
        val includePadding = false

        val staticLayout =
                StaticLayout(text, textPaint, width, alignment, spacingMultiplier, spacingAddition, includePadding)

        return staticLayout.height

    }

Upvotes: 0

Related Questions