最近项目里遇到一些要完全去除TextView上下边距的场景。但是设置includeFontPadding为false之后,并不能完全去除上边距或者下边距。
于是想从绘制过程中就移除上线边距。于是从网上查资料结合源码分析问题。
第一步:弄清楚上下边距的原理。这个参考 这篇文章自定义控件之绘图篇( 五):drawText()详解_启舰-CSDN博客,详细写了内部的view五线谱的原理。
第二步:集成TextView,重新设置view的高度和边距。有了第一步的基础之后,就明白对于“上面的按钮”来说,上边距就是top-ascent,下边距就是bottom-descent。接下来有一件事是确定要做的,那就是重写onMeasure方法,想达到“下面的按钮”的效果,高度肯定减去上面算出来的上和下边距。但是,做完之后发现问题了,view中文字绘制时是通过paint.drawText实现的,方法里会有一个baseline的参数,那么Y轴上的绘制起始点,其实还是以整个view是包含上下边距来计算的,那么可以想一想会出现什么效果?对的,那就是文字的下半部分会被截断一些,而所谓的这些的距离其实就是上边距的空白的高度。于是乎,这里就出现了2个解决方案。
第一个方案:在ondraw里执行setScrollY方法,把view竖直上移,然后再执行super.ondraw(),但发现view会闪。说明view多刷新了一次,个人理解的意思是在,设置setScrollY的时候,控件已经绘制好了并显示了,再次移动view会让其再绘制一次。于是考虑将操作放在ondraw方法执行之前。那么再结合view绘制过程的三个方法的执行顺序,只要再onlayout或onMeasure里提前操作即可。
下面附上完整代码:
public class NoTopBottomSpaceTextView extends AppCompatTextView {
public NoTopBottomSpaceTextView(Context context) {
super(context);
init();
}
public NoTopBottomSpaceTextView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public NoTopBottomSpaceTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
setGravity(getGravity() | Gravity.CENTER_VERTICAL);
setIncludeFontPadding(false);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Paint.FontMetrics fm = getPaint().getFontMetrics();
if (fm != null) {
int padding = (int) Math.abs(fm.bottom - fm.descent);
int width = getMeasuredWidth();
int height = getMeasuredHeight();
if (getText().toString().contains("g")
|| getText().toString().contains("y")
|| getText().toString().contains("p")) {
setMeasuredDimension(width, height - padding);
if (getScrollY() != (int)(fm.ascent-fm.top)) {
setScrollY((int) (fm.ascent - fm.top));
}
setScrollY((int) Math.abs(fm.top - fm.ascent));
} else {
setMeasuredDimension(width, height - padding - (int) (getTextSize() * 0.1));
setScrollY((int) Math.abs(fm.top - fm.ascent) - (int) (getTextSize() * 0.1));
}
}
}
}