4

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.

Gunaseelan
  • 14,415
  • 11
  • 80
  • 128
  • Is the text always going to be what you show in the images? – Helder Sepulveda Dec 04 '20 at 18:28
  • Have you read the comments in [this answer to Measuring text height to be drawn on Canvas ( Android )](https://stackoverflow.com/a/42091739/295004) – Morrison Chang Dec 05 '20 at 01:37
  • @HelderSepulveda, No it will change... – Gunaseelan Dec 05 '20 at 05:29
  • If the text could be other than what you show, you need to include the text in your calculations, measure the text with an average size if is bigger reduce size, if smaller increase and loop until you get something that fits – Helder Sepulveda Dec 06 '20 at 13:26
  • Have you thought about using an [Autosizing TextView](https://developer.android.com/guide/topics/ui/look-and-feel/autosizing-textview) and grabbing the _StaticLayout_ from the _TextView_? You would set up the bounds and let the _TextView_ do the work. – Cheticamp Dec 06 '20 at 14:54

2 Answers2

1

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
}
Cheticamp
  • 61,413
  • 10
  • 78
  • 131
0

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

    }
Ruthwik Warrier
  • 187
  • 2
  • 14
  • 1
    But I want recommended text size based on static layout height and rectf height... When apply that recommended text size to text paint, the text will occupy max space from that rect. – Gunaseelan Dec 03 '20 at 06:41