1.网上方法的效果
在xml文件中对TextView设置: includeFontPadding=false
加上以上属性之后,文本的高度明显变小了,但是没有达到我们预想的效果(文字紧贴边框),而是在上下留了一部分距离。
具体效果可以查看第二部分中的对比图
2.失效的原因
在了解这个原因前需要对TextView中文本的绘制有一点了解,具体查看文末参考文章中启航大佬的介绍文章。
对应下图,top和bottom之间的高度,就是默认的TextView的高度。
includeFontPadding=false属性取消的是top和ascent之间,bottom和descent之间的两个空白区域。
但是也看的出来,在ascent和descent中间的文字,距离两条线还是有一定距离,这也就是在设置includeFontPadding=false属性后继续留着的空白部分。
(图来自启航大佬的文章)
具体从源码中可以看出这个过程
查看TextView的源码中查找mIncludePad属性,发现都是进到一个BoringLayout中
在其中查找includepad,在其init()方法中找到以下部分。
其中metrics为系统提供的用于查询top,ascent,descent,bottom四个属性的一个对象
1.最终效果对比图
中间的为普通的TextView
2 自定义的思路
首先系统提供了一个用于检测包裹住的文字所需最小的矩形的方法。
/**
* 获取指定字符串所对应的最小矩形,以(0,0)点所在位置为基线
* @param text 要测量最小矩形的字符串
* @param start 要测量起始字符在字符串中的索引
* @param end 所要测量的字符的长度
* @param bounds 接收测量结果
*/
public void getTextBounds(String text, int start, int end, Rect bounds);
自定义View
onMeasure
中把宽高设置为上述最小矩形的宽高
onDraw
由于宽高被重新设置了,所以canvas中原点要进行位移,才能使得文本正常显示。
3 具体代码
public class MyNoPaddingTextView extends android.support.v7.widget.AppCompatTextView {
private Rect minRect;
public MyNoPaddingTextView(Context context) {
super(context);
}
public MyNoPaddingTextView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public MyNoPaddingTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (minRect == null) {
//minRect用来获取文字显示所需要最小区域的左上角和右下角 坐标
//该坐标是以(0,0)为基准的矩形坐标
minRect = new Rect();
}
getPaint().getTextBounds(getText().toString(), 0, getText().length(), minRect);
final int width = minRect.width();
final int height = minRect.height();
setMeasuredDimension(width, height);
}
@Override
protected void onDraw(Canvas canvas) {
final String text = getText().toString();
final int left = minRect.left;
final int top = minRect.top;
Paint paint = getPaint();
paint.setColor(getCurrentTextColor());
/*此时文字的基线在(0,0),要达到刚好包裹文字的效果,相当于把以(0,0)为基线的minRect 移动到合适的位置
x轴上由于左边内边距的存在,所以需要左移minRect.left距离
y轴上相当于把mingRect的顶点向下移动minRect.top距离
*/
canvas.drawText(text, -left, -top, paint);
}
}
4.补充
(1)如果需要修改或者了解,一定要看参考文献中启航大佬的文章,介绍的很详细,避免走弯路。
(2)对于wrap_content的文本效果是可以满足的,如果需要指定文本大小,则需要在onMeasure中重写一下。
(3)使用本文中的代码时,xml中的padding会失效,如果有左右padding的需求,可以通过onMeasure和onDraw中getPaddingLeft()等方法判断一下,测试过是可以实现padding效果的。
(4)只测试了wrap_content的效果(效果有用了),要是有别的问题大家可以一起讨论解决。
参考文章:
启航 drawText()详解: https://blog.csdn.net/harvic880925/article/details/50423762
另一种自定义view实现效果 :https://blog.csdn.net/qq1282675628/article/details/79146859
去掉内边距(但是只去掉了上面部分):https://blog.csdn.net/Ab0510/article/details/52219464