0

I need two TextViews to have same baseline, ascent, bottom, descent values. Is it possible to change the FontMetrics of TextView after text has been drawn?

Siva
  • 355
  • 1
  • 6
  • 19
  • did you check `TextView` docs? is there anything related to "size" ? – pskink Aug 16 '16 at 06:33
  • @pskink I did check. There wasn't any public api related to that. However getPaint() provided the fontmetrics values. I was wondering if we could change it inside the paint object. – Siva Aug 16 '16 at 06:36
  • nothing related to size in `TextView` api? – pskink Aug 16 '16 at 06:37
  • There are apis related to textSize. But there is no api that could allow us to change Fontmetrics. For instance, if i want to draw text with 5 pixels above baseline, i cannot achieve it with textSize apis. – Siva Aug 16 '16 at 06:40
  • so use `android.text.Spannable` and "spans" concept for such things – pskink Aug 16 '16 at 06:56

2 Answers2

2

It is so late, but I also confronted with this issue, and I solve with some tricks. (just modify baseline, not ascent, descent or something)


  1. note that fontMetrics' values(ascent, descent, bottom, top) are depend on textSize.

    so we must use fontMetrics' values after view is drawn. (if it is autoSizing or something)

    to do something after view drawn, using Sa Qada's answer

  2. baseline is always 0. descent, ascent, top, bottom is relative values.

    refer this Suragch's answer.

  3. change fontMetrics' values does not affect to UI.

    to set custom baseline(text position of textview), just use padding. below code is align to baseline with two TextView.

    // this is kotlin code, but I'm sure you can understand
    // align textview2 to textview1's baseline
    
    val tv1Baseline = tv1.paddingBottom + tv1.paint.fontMetrics.bottom
    val tv2Baseline = tv2.paddingBottom + tv2.paint.fontMetrics.bottom
    val newPaddingOffset = tv1Baseline - tv2Baseline
    
    tv2.setPadding(tv2.paddingLeft, tv2.paddingTop, tv2.paddingRight,
                   tv2.paddingBottom + newPaddingOffset)
    
mgcation
  • 517
  • 6
  • 17
  • Thanks for answering, much appreciated. I will try this out. – Siva Jan 11 '18 at 13:58
  • @Siva but this may not really good idea. because you must consider text's alignment case by case. – mgcation Jan 12 '18 at 14:22
  • Does this code actually aligns bottoms, not baselines? – sandrstar Jun 03 '18 at 02:41
  • @sandrstar As I know, baselines are always 0. so I just add bottom padding to align two textviews. So it aligns bottoms as you said. – mgcation Jun 03 '18 at 09:14
  • Looks like it's not, I've just checked and at least in my case (code in my answer) return value from getBaseline() is different from 0, since it's 'Return the offset of the widget's text baseline from the widget's top boundary' (https://developer.android.com/reference/android/widget/TextView) – sandrstar Jun 03 '18 at 11:04
  • @sandrstar Thanks for your explanation. I think you're right. My second section(baseline always 0) is just for fontMetrics(textPaint), not textview. by the way, can I ask 1 question? Is your code also work at when layout does not support baseline alignment? – mgcation Jun 03 '18 at 17:45
  • 1
    I think so. I'm using it in my custom layout in order to align two textviews baselines. It does use TextView baseline calculations, but not align_baseline parameter. Obviously, if layout does support baseline alignment, you don't need to use such tricks as in my (or initially yours) answer. – sandrstar Jun 04 '18 at 04:24
1

Here's solution to align baselines (note the difference from bottom in Suragch answer and definition of return value of TextView.getBaseline()) which alignes tv2 baseline to tv1s':

private void alignWeatherTextBaseline(View tv1, View tv2) {
    int tv1Baseline = tv1.getTop() + tv1.getBaseline();
    int tv2Baseline = tv2.getTop() + tv2.getBaseline();
    int newPaddingOffset = tv1Baseline - tv2Baseline;

    if (newPaddingOffset != 0) {
        tv2.setPadding(tv2.getPaddingLeft(),
                tv2.getPaddingTop(), tv2.getPaddingRight(),
                tv2.getPaddingBottom() - newPaddingOffset);
    }
}

Be aware of the following:

  • if multiline text is possible, you need to write proper getLastLineBaseline() and use it instead of standard getBaseline()
  • you can get invalid value (0 or -1) from getBaseline() if a view hasn't been layout yet
sandrstar
  • 12,503
  • 8
  • 58
  • 65