androidfontmetricsstaticlayoutdynamiclayout

Android Layout: meaning of getLineBaseline, getLineDescent, getLineAscent, getLineBottom, getLineTop


The documentation for the following methods of Layout (including StaticLayout, DynamicLayout, and BoringLayout) are very sparce.

Exactly what are the numbers that these methods are returning? Are they the normal font metrics values or are they the locations on the layout?

I made a test project to find out so I am posting my answer below Q&A style.


Solution

  • I have previously described the meaning of top, ascent, baseline, descent, bottom, and leading in Android's FontMetrics.

    Because the Layout methods getLineBaseline, getLineDescent, getLineAscent, getLineBottom, and getLineTop sound so similar to the FontMetrics names, it is easy to get them confused. However, they report two different types of things:

    These methods return their vertical positions on the layout, which is different for every line.

    However, the following two methods return the value for the particular line they are on, regardless of where the line is in the layout. So unless there are special spans that affect the size, they will be the same for every line.

    Demo

    I made a simple project to demonstrate that the imformation above. There are six lines of text in an EditText. Clicking the button logs the info for each line.

    enter image description here

    Results

    Here is the logged result:

    line 0 baseline: 67
    line 1 baseline: 140
    line 2 baseline: 213
    line 3 baseline: 286
    line 4 baseline: 359
    line 5 baseline: 432
    
    line 0 descent: 15
    line 1 descent: 15
    line 2 descent: 15
    line 3 descent: 15
    line 4 descent: 15
    line 5 descent: 18
    
    line 0 ascent: -67
    line 1 ascent: -58
    line 2 ascent: -58
    line 3 ascent: -58
    line 4 ascent: -58
    line 5 ascent: -58
    
    line 0 top: 0
    line 1 top: 82
    line 2 top: 155
    line 3 top: 228
    line 4 top: 301
    line 5 top: 374
    
    line 0 bottom: 82
    line 1 bottom: 155
    line 2 bottom: 228
    line 3 bottom: 301
    line 4 bottom: 374
    line 5 bottom: 450
    
    FontMetrics top: -67
    FontMetrics bottom: 18
    FontMetrics ascent: -58
    FontMetrics descent: 15
    

    As you can see, top, bottom, and baseline are cumulative based on the line. Ascent and descent mainly stay the same for each line. Ascent is equal to FontMetrics.ascent for all lines except the first line, where it equals FontMetrics.top. And descent is equal to FontMetrics.descent for all lines except the last line, where it equals FontMetrics.bottom.

    So top, bottom, baseline, ascent, and descent for a line should not be considered to be equal to the FontMetrics values of the same names. On a line ascent is the distance from the baseline to the bottom of the line above it. Descent is the distance from the baseline to the top of the next line.

    In the source code, only top and descent are saved for every line. The other values are calculated from them:

    Project code:

    public class MainActivity extends AppCompatActivity {
    
        EditText editText;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            editText = (EditText) findViewById(R.id.editText);
        }
    
        public void buttonClick(View view) {
    
            Layout layout = editText.getLayout();
            for (int i = 0; i < layout.getLineCount(); i++) {
                int baseline = layout.getLineBaseline(i);
                Log.i("TAG", "line " + i + " baseline: " + baseline);
            }
            for (int i = 0; i < layout.getLineCount(); i++) {
                int descent = layout.getLineDescent(i);
                Log.i("TAG", "line " + i + " descent: " + descent);
            }
            for (int i = 0; i < layout.getLineCount(); i++) {
                int ascent = layout.getLineAscent(i);
                Log.i("TAG", "line " + i + " ascent: " + ascent);
            }
            for (int i = 0; i < layout.getLineCount(); i++) {
                int top = layout.getLineTop(i);
                Log.i("TAG", "line " + i + " top: " + top);
            }
            for (int i = 0; i < layout.getLineCount(); i++) {
                int bottom = layout.getLineBottom(i);
                Log.i("TAG", "line " + i + " bottom: " + bottom);
            }
    
            Paint.FontMetricsInt fm = editText.getLayout().getPaint().getFontMetricsInt();
            Log.i("TAG", "fm top: " + fm.top);
            Log.i("TAG", "fm bottom: " + fm.bottom);
            Log.i("TAG", "fm ascent: " + fm.ascent);
            Log.i("TAG", "fm descent: " + fm.descent);
        }
    }
    

    See also