彻底解决Android TextView文本上下留白问题

背景:在android开发中,经常会遇到icon 和 textview 在同一行。这种情况下产品和UI设计师会告诉你,要求icon图标和textview垂直方向上中心线对齐,即在同一行中 icon距离上下的距离是相等的,textview亦然。或者还有更常见的情况,当我们android程序猿根据UI设计师的设计图,一通疯狂操作之后,自信满满的给到UI设计师的时候,当我们正在洋洋得意的认为小case的时候,UI会找到你,说控件之间的上下间距不对,和UI设计稿上的距离对不上。What! 仔细看看代码,对的呀,设计稿上是10dp,代码里面写的也是10dp,为什么不对呢。这就是这篇文章要解决的问题了。

根据上面的背景描述,我相信每一个android程序猿都遇到过这个问题,因为这是原生的TextView与生俱来的小问题,UI设计师给到的设计稿上的控件之间的间距,都是贴着文本内容测量出来的距离,但是android TextView 会在文本的上下分别加上一点空白区域。这就会导致我们的实际效果和UI设计稿有了出入。要解决这个问题,怎么办呢!下面根据我自己的经验和网上查找的资料,列出大概的几种方案。如果有不错误的地方请大家斧正。

解决方案:

1、这个问题不解决,每次有问题,通过和UI设计师不断的上下微调(不推荐)

这个方案比较费时,而且不一定在每个机型上都能调到正合适。

2、android:includeFontPadding=“false”

这个是网上看到的最多的,百度出来的基本都是说设置这个属性,这个属性确实能减少一点上下的留白,但是做不到完全去除内容以外的上下留白。

3、自定义View,在onDraw()方法中使用drawText()重新绘制,使其不留上下空白

这个方案看起来可以解决文字上下留白的问题,但是当你使用之后会发现,这种办法自定义的View无法使用TextView的属性和方法,让你没有办法像使用原生TextView一样使用。功能比较单一,只是纯粹的只能基本的文字处理,如果想做一些其他的处理,需要挨个在onDraw()里面一一实现,事倍功半,而且比较容易出错。

4、我的方案:能不能不破坏TextView的原生用法,在原有TextView的基础上消除掉上下留白,让我可以像使用原生TextView一样使用

网上找了很多资料,我们先看一张图,帮助我们理解,然后我们看看通过什么办法可以做的消除上下留白。

image

为了简单这个图是我从网上copy过来的这样懒得再去作图了😃,这种图网上很多,这里我做个简单的介绍吧。

top: baseline到顶部的最大距离。

ascent: 上坡度,baseline到顶部的推荐距离。

baseline: 基线。

descent: 下坡度,baseline到底部的推荐距离。

bottom: baseline到底部的最大距离。

我的做法思路是:我们要消除的是:

       1.Top到文字顶部的距离。

       2.bottom到文字底部的距离。

这两个距离如果消除了,就不存在上下留白的问题了。

有了这个思路之后,我们知道 xml里面可以使用 paddingTop = "-10dp"这种办法可以让文字向TextView的顶部移动对应的像素,那么我们就可以在代码里面给它加上一个向上的 负的 paddingTop 让文字紧贴着TextView的顶部消除上面的留白。那底部的留白是不是可以用同样的办法设置一个负数 的PaddingBottom来解决呢?答案是否定的,我们总不能在两个相反的方向上同时让文字向上向下吧,就想拔河一样,只能走向一边。所以,底部的留白我们就换一种思路,我们顶部的留白已经消除了,那么我们只需要能够计算出的TextView的高度是不是就解决了呢。答案是肯定的,我们在得到TextView的测量高度后,再减去bottom到文字底部的距离剩下的就是整个内容的高度了,并且没有了上下留白。

好了有了思路,就可以付诸实践敲敲代码试试看了。

 /**

* @author luowang8

* @date 2020-01-17 16:36

*/

@SuppressLint("AppCompatCustomView")

public class NoSpaceTextView extends TextView {

/**

* 控制measure()方法 刷新测量

*/

  private boolean refreshMeasure =false;

public NoSpaceTextView(Context context) {

super(context);

}

public NoSpaceTextView(Context context,@Nullable AttributeSet attrs) {

super(context,attrs);

}

public NoSpaceTextView(Context context,@Nullable AttributeSet attrs,int defStyleAttr) {

super(context,attrs,defStyleAttr);

}

@Override

  protected void onMeasure(int widthMeasureSpec,int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec,heightMeasureSpec);

removeSpace(widthMeasureSpec,heightMeasureSpec);

}

@Override

  public void setText(CharSequence text,BufferType type) {

super.setText(text,type);

// 每次文本内容改变时,需要测量两次,确保计算的高度没有问题

      refreshMeasure =true;

}

/**

* 这里处理文本的上下留白问题

*/

  private void removeSpace(int widthspc,int heightspc) {

int      paddingTop =0;

String[]  linesText  =getLinesText();

TextPaint paint      =getPaint();

Rect      rect      =new Rect();

String    text      = linesText[0];

paint.getTextBounds(text,0, text.length(), rect);

Paint.FontMetricsInt fontMetricsInt =new Paint.FontMetricsInt();

paint.getFontMetricsInt(fontMetricsInt);

paddingTop = (fontMetricsInt.top - rect.top);

// 设置TextView向上的padding (小于0, 即把TextView文本内容向上移动)

      setPadding(getLeftPaddingOffset()

, paddingTop +getTopPaddingOffset()

,getRightPaddingOffset()

,getBottomPaddingOffset());

String endText = linesText[linesText.length -1];

paint.getTextBounds(endText,0, endText.length(), rect);

// 再减去最后一行文本的底部空白,得到的就是TextView内容上线贴边的的高度,到达消除文本上下留白的问题

      setMeasuredDimension(getMeasuredWidth()

,getMeasuredHeight() - (fontMetricsInt.bottom - rect.bottom));

if (refreshMeasure) {

refreshMeasure =false;

measure(widthspc,heightspc);

}

}

/**

* 获取每一行的文本内容

*/

  private String[]getLinesText() {

int start =0;

int end  =0;

String[] texts =new String[getLineCount()];

String text =getText().toString();

Layout layout =getLayout();

for (int i =0; i

end = layout.getLineEnd(i);

String line = text.substring(start, end);//指定行的内容

        start = end;

texts[i] = line;

}

return texts;

}

}

image

因为文字可能有多行,所以需要获取第一行的文字内容来做计算,fontMetricsInt.top 即我们上面说的最顶部的位置,rect.top即是文字的顶部,这两的差值就是顶部的留白部分,然后我们设置一个顶部的padding 就可以消除掉顶部留白了。

image

同理文字有多行,底部的留白,我们需要以最后一行的文字为准,先得到原始的TextView高度,然后再减去 最后一行底部的留白,剩下的就是我们的实际要的高度。

到此我们就基本完成了一个没有上下留白的TextView了,并且这个TextView和以前的用法一模一样,我们也不用担心,TextView的其他功能用不了。机智的童鞋可能要问了,我们可以设置负的paddingTop 这种方式来达到我们的目的,那我们可不可以使用marginTop呢?其实这两种方式均是可以实现消除上下留白的,原理其实都一样,我这里就提供padding这一种代码方案,margin的原理是一样的,有兴趣的童鞋不妨自己写写试试。

效果图:
image.png

image.png

Demo代码直通车:https://github.com/wwluo14/NoSpaceTextView

  • 7
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值