Wednesday 15 August 2012

java - Truly top-aligning text in Android TextView -



java - Truly top-aligning text in Android TextView -

i trying display textview in android such text in view top-aligned:

@override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); // create container layout framelayout layout = new framelayout(this); // create text label textview label = new textview(this); label.settextsize(typedvalue.complex_unit_px, 25); // 25 pixels tall label.setgravity(gravity.top + gravity.center); // align text top-center label.setpadding(0, 0, 0, 0); // no padding rect bounds = new rect(); label.getpaint().gettextbounds("gdyl!", 0, 5, bounds); // measure height label.settext("good day, world! "+bounds.top+" "+bounds.bottom); label.settextcolor (0xff000000); // black text label.setbackgroundcolor(0xff00ffff); // bluish background // position text label framelayout.layoutparams layoutparams = new framelayout.layoutparams(300, 25, gravity.left + gravity.top); // 25 pixels tall layoutparams.setmargins(50, 50, 0, 0); label.setlayoutparams(layoutparams); // compose screen layout.addview(label); setcontentview(layout); }

this code outputs next image:

the things note:

the bluish box 25 pixels tall, requested the text bounds reported 25 pixels tall requested (6 - (-19) = 25) the text not start @ top of label, has padding above it, ignoring setpadding() this leads text beingness clipped @ bottom, though box technically tall enough

how tell textview start text @ top of box?

i have 2 restrictions possible answers:

i need maintain text top-aligned, though, if there trick bottom-aligning or centering vertically instead, can't utilize it, since have scenarios textview taller needs be. i'm bit of compatibility-freak, if possible i'd stick calls available in android apis (preferably 1, no higher 7).

textviews utilize abstract class android.text.layout draw text on canvas:

canvas.drawtext(buf, start, end, x, lbaseline, paint);

the vertical offset lbaseline calculated bottom of line minus font's descent:

int lbottom = getlinetop(i+1); int lbaseline = lbottom - getlinedescent(i);

the 2 called functions getlinetop , getlinedescent abstract, simple implementation can found in boringlayout (go figure... :), returns values mbottom , mdesc. these calculated in init method follows:

if (includepad) { spacing = metrics.bottom - metrics.top; } else { spacing = metrics.descent - metrics.ascent; } if (spacingmult != 1 || spacingadd != 0) { spacing = (int)(spacing * spacingmult + spacingadd + 0.5f); } mbottom = spacing; if (includepad) { mdesc = spacing + metrics.top; } else { mdesc = spacing + metrics.ascent; }

here, includepad boolean specifies whether text should include additional padding allow glyphs extend past specified ascent. can set (as @ggc pointed out) textview's setincludefontpadding method.

if includepad set true (the default value), text positioned baseline given top-field of font's metrics. otherwise text's baseline taken descent-field.

so, technically, should mean need turn off includefontpadding, unfortunately yields next result:

the reason font reports -23.2 ascent, while bounding box reports top-value of -19. don't know if "bug" in font or if it's supposed this. unfortunately fontmetrics not provide value matches 19 reported bounding box, if seek somehow incorporate reported screen resolution of 240dpi vs. definition of font points @ 72dpi, there no "official" way prepare this.

but, of course, available info can used hack solution. there 2 ways it:

with includefontpadding left alone, i.e. set true:

double top = label.getpaint().getfontmetrics().top; label.setpadding(0, (int) (top - bounds.top - 0.5), 0, 0);

i.e. vertical padding set compensate difference in y-value reported text bounds , font-metric's top-value. result:

with includefontpadding set false:

double ascent = label.getpaint().getfontmetrics().ascent; label.setpadding(0, (int) (ascent - bounds.top - 0.5), 0, 0); label.setincludefontpadding(false);

i.e. vertical padding set compensate difference in y-value reported text bounds , font-metric's ascent-value. result:

note there nil magical setting includefontpadding false. both version should work. reason yield different results different rounding errors when font-metric's floating-point values converted integers. happens in particular case looks improve includefontpadding set false, different fonts or font sizes may different. easy adjust calculation of top-padding yield same exact rounding errors calculation used boringlayout. haven't done yet since i'll rather utilize "bug-free" font instead, might add together later if find time. then, should irrelevant whether includefontpadding set false or true.

java android textview vertical-alignment

No comments:

Post a Comment