「这是我参与11月更文挑战的第16天,活动详情查看:2021最后一次更文挑战」
Android TextView去除自身的边距
TextView是大家常用的组件,但有个简单的功能,实现起来却很是复杂,那就是边距问题,我们无法通过简单的配置去更改它的默认边距,我也不清楚是为什么,可能是给一些特殊的字符预留的位置吧。
尝试
网上流行了很多种方案,无论是哪种搜索引擎,出现的千篇一律都是一个情况,先来尝试分析一下几种常见的解决方案。
1. 修改padding
直接padding设置为0dp,无效。
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/purple_200"
android:text="测试"
android:textSize="50sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:background="@color/purple_200"
android:text="测试"
android:padding="0dp"
android:textSize="50sp" />
2. 设置includeFontPadding
includeFontPadding 的作用是什么呢?
API是这样给出的解释:给上升者和下降者留出足够的空间,而不是严格使用字体上升和下降
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/purple_200"
android:text="测试fy"
android:textSize="50sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:background="@color/purple_200"
android:text="测试fy"
android:includeFontPadding="false"
android:textSize="50sp" />
它的作用是什么呢?
它去除的是top到ascent之间,bottom到descent之间的区域
确实有一些作用,在绝大部分情况,使用它就够了,它是原生的方法,兼容性和稳定性都不用过多考虑。
但可能还会遇到更奇葩的场景,需要完全的去除padding,也就是需要更贴近边框。
解决方案
由于原生的方法不再提供进一步减少间距的方法,所以只能依靠自定义视图实现了了。 看下面方法前需要了解以下知识点,在Android中测量文字尺寸的高度有几种方法
- FontMetrics 上篇文章讲过FontMetrics.bottom-FontMetrics.top为文字的高度
- measureText 测量文本的宽高
- getTextBounds 将TextView 的文本放入一个矩形中, 测量TextView的高度和宽度,注意获取到的是包裹文字的最小宽度和高度。
方法一
还记得这张图吗,记住baseLine,记住canvas.drawText中的坐标是BaseLine,这个巧妙的方法就是通过得出BaseLine,然后通过drawText实现。
思想和源代码来自:来源
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);
}
}
方法二
与方法一的思想类似,方法二中多了一层容器去包含TextView,同时支持TextView的原生属性,同时也支持了padding属性,方法一不支持padding了。
另外一种方法 来源