Android Text View 去掉默认的padding的实现方法

news2024/12/23 10:25:14

先看下最终实现效果,满意您在往下看:

TextView 绘制的时候自带一定的Padding值,要想实现去掉默认的padding值,xml文件可以设置一个属性值 :

android:includeFontPadding="false"

然后运行起来就会发现,并没有什么卵用,也不能说完全没有,但效果差点意思。我就不运行了,在编译器上也能大概看到效果,如下图,我们可以看到依然有着无法删除的padding值。

我们先来看一下绘制TextView时的几条基准线:

  • top:在给定文本大小下,字体中最高字形高于基线的最大距离,即能绘制的最高点
  • ascent:单倍行距文本的基线以上建议距离,即推荐的文字绘制上边缘线
  • base:文字绘制基准线,也就是坐标轴,X轴就是Baseline
  • decent:单间距文本低于基线的建议距离,即推荐的文字绘制下边缘线
  • bottom:能绘制的最低点

Textview绘制文字会在  ascentdecent 之间,外面的距离我们可以理解为 类似 padding一样的间隔,但又并不是我们设置的paddingTop,paddingBottom。

要解决这个这个问题,首先我们要知道textview的内容文字绘制的真实区域:

红色的区域就是内容的真实高度,蓝色的部分就是textview绘制的多余的部分,现在我们要去掉这一部分,首先可以通过 getTextBounds 来获取绘制的真实区域

textView.getPaint().getTextBounds(text, 0, text.length(), textRect);

获取到真实区域后,那么再来看textview绘制的几条基准线,你想到了什么,是的,我们只需要稍微移动一下这几条线把高度压缩到文字的展示绘制区域即可,实现用 SpannableString 来实现,SpannableString 是 android  里面专门用来实现设置 textview 文字样式的类,这个不清楚的自行查询一下,这里不赘述了,具体我们用的是  LineHeightSpan ,可以通过修改 textview 的行高来实现我们的目的。具体看下代码:

spannableString.setSpan(new LineHeightSpan() {
    @Override
    public void chooseHeight(CharSequence text, int start, int end, int spanstartv, int lineHeight, Paint.FontMetricsInt fm) {
        Rect textRect = new Rect();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            textView.getPaint().getTextBounds(text, 0, text.length(), textRect);
        } else {
            textView.getPaint().getTextBounds(text.toString(), 0, text.length(), textRect);
        }
        // 直接把 textview 绘制区域 缩小到 文字真实大小的区域
        // 这个是有一点问题的,看下图
        fm.top = textRect.top;
        fm.bottom = textRect.bottom;
        fm.ascent = fm.top;
        fm.descent = fm.bottom;
    }
}, 0, text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

 Paint.FontMetricsInt 里的 top bottom ascent descent 就是用来调整 绘制的时候的具体位置的,我们把textview的绘制区域直接设置到 FontMetricsInt 里面,实现的效果如下图:

看起来确实去掉了padding,而且去的干干净净,需要这种效果的可以停下来,施展CV大法。

这种实现方式原理也很简单,是使文字真实的绘制区域高度为所绘制内容中字符的最大高度,这样可能会造成排版问题,文字对不齐,那我们就需要统一下绘制内容的高度,我们知道TextView 有个属性TextSize ,它的值最终决定了Textview 的高度,然后我们略微修改一下代码:

spannableString.setSpan(new LineHeightSpan() {
    @Override
    public void chooseHeight(CharSequence text, int start, int end, int spanstartv, int lineHeight, Paint.FontMetricsInt fm) {
        Rect textRect = new Rect();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            textView.getPaint().getTextBounds(text, 0, text.length(), textRect);
        } else {
            textView.getPaint().getTextBounds(text.toString(), 0, text.length(), textRect);
        }

        Log.e("NoPaddingText", "修改之前 " + fm.toString());
        Log.e("NoPaddingText", textRect.toString());
        Log.e("NoPaddingText", "textSize: " + textView.getTextSize());

        if (textRect.bottom - textRect.top < textView.getTextSize()) {
            // 一般我们认为字体的textview的textsize为textview的高度,当然有一定的误差
            // 当字体的高度没有字体的textsize大时,我们把大小设置成textsize,这样就可以解决文字的排版问题了
            float tempPadding = (textView.getTextSize() - (textRect.bottom - textRect.top)) / 2f;
            fm.top = (int) (textRect.top - tempPadding);
            fm.bottom = (int) (textRect.bottom + tempPadding);
        } else {
            fm.top = textRect.top;
            fm.bottom = textRect.bottom;
        }
        fm.ascent = fm.top;
        fm.descent = fm.bottom;
        Log.e("NoPaddingText", "修改之后 " + fm.toString());
    }
}, 0, text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

我们用TextView的TextSize来约束统一绘制高度,不足TextSize 的,补充添加一个padding值补齐正常的高度,实现效果如下:

这样看着顺眼多了,继续CV大法。

完整代码如下:(两种实现形式:1、工具类; 2、自定义View形式实现)

点这里跳转项目源码地址

import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Build;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.style.LineHeightSpan;
import android.util.Log;
import android.widget.TextView;

// 工具类 实现
public class TextUtil {

    // 设置上下取消绘制的padding值 考虑textsize
    public static void setNoVerticalPaddingText(TextView textView, CharSequence text) {
        if (textView == null || text == null)
            return;
        // 如果原先上下有padding,重置为 0
        textView.setPadding(textView.getPaddingLeft(), 0, textView.getPaddingRight(), 0);
        // 利用 LineHeightSpan 快速实现去除 padding 的效果
        SpannableString spannableString = new SpannableString(text);
        spannableString.setSpan(new LineHeightSpan() {
            @Override
            public void chooseHeight(CharSequence text, int start, int end, int spanstartv, int lineHeight, Paint.FontMetricsInt fm) {
                Rect textRect = new Rect();
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                    textView.getPaint().getTextBounds(text, 0, text.length(), textRect);
                } else {
                    textView.getPaint().getTextBounds(text.toString(), 0, text.length(), textRect);
                }

                Log.e("NoPaddingText", "修改之前 " + fm.toString());
                Log.e("NoPaddingText", textRect.toString());
                Log.e("NoPaddingText", "textSize: " + textView.getTextSize());

                if (textRect.bottom - textRect.top < textView.getTextSize()) {
                    // 一般我们认为字体的textview的textsize为textview的高度,当然有一定的误差 当然也可以自定义View 形式
                    // 当字体的高度没有字体的textsize大时,我们把大小设置成textsize,这样就可以解决文字的排版问题了
                    float tempPadding = (textView.getTextSize() - (textRect.bottom - textRect.top)) / 2f;
                    fm.top = (int) (textRect.top - tempPadding);
                    fm.bottom = (int) (textRect.bottom + tempPadding);
                } else {
                    // 这么设置可以完全消除padding,但是会有问题,
                    // 同样textsize的Textview 会因为设置的内容不一样而高度不一样
                    // 如果有什么特殊需求可以考虑用一下
                    fm.top = textRect.top;
                    fm.bottom = textRect.bottom;
                }
                fm.ascent = fm.top;
                fm.descent = fm.bottom;
                Log.e("NoPaddingText", "修改之后 " + fm.toString());
            }
        }, 0, text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        textView.setText(spannableString);
    }


    // 设置上下取消绘制的padding值 不考虑textsize
    public static void setNoVerticalPaddingText2(TextView textView, CharSequence text) {
        if (textView == null || text == null)
            return;
        // 如果原先上下有padding,重置为 0
        textView.setPadding(textView.getPaddingLeft(), 0, textView.getPaddingRight(), 0);
        // 利用 LineHeightSpan 快速实现去除 padding 的效果
        SpannableString spannableString = new SpannableString(text);
        spannableString.setSpan(new LineHeightSpan() {
            @Override
            public void chooseHeight(CharSequence text, int start, int end, int spanstartv, int lineHeight, Paint.FontMetricsInt fm) {
                Rect textRect = new Rect();
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                    textView.getPaint().getTextBounds(text, 0, text.length(), textRect);
                } else {
                    textView.getPaint().getTextBounds(text.toString(), 0, text.length(), textRect);
                }

                Log.e("NoPaddingText", "修改之前 " + fm.toString());
                Log.e("NoPaddingText", textRect.toString());
                Log.e("NoPaddingText", "textSize: " + textView.getTextSize());

                // 这么设置可以完全消除padding,但是会有问题,
                // 同样textsize的Textview 会因为设置的内容不一样而高度不一样
                // 如果有什么特殊需求可以考虑用一下
                fm.top = textRect.top;
                fm.bottom = textRect.bottom;
                fm.ascent = fm.top;
                fm.descent = fm.bottom;
                Log.e("NoPaddingText", "修改之后 " + fm.toString());
            }
        }, 0, text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        textView.setText(spannableString);
    }

}
import android.content.Context;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Build;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.style.LineHeightSpan;
import android.util.AttributeSet;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatTextView;

/**
 * 自定义view 形式 实现 NoPaddingTextView 布局可以预览
 */
public class NoPaddingTextView extends AppCompatTextView {


    public NoPaddingTextView(@NonNull Context context) {
        this(context, null);
    }

    public NoPaddingTextView(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public NoPaddingTextView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public void setText(CharSequence text, BufferType type) {
        super.setText(getNoVerticalPaddingText(this,text), type);
    }

    // 设置上下取消绘制的padding值 考虑textsize
    public final SpannableString getNoVerticalPaddingText(TextView textView, CharSequence text) {
        if (textView == null || text == null)
            return new SpannableString("");
        // 如果原先上下有padding,重置为 0
        textView.setPadding(textView.getPaddingLeft(), 0, textView.getPaddingRight(), 0);
        // 利用 LineHeightSpan 快速实现去除 padding 的效果
        SpannableString spannableString = new SpannableString(text);
        spannableString.setSpan(new LineHeightSpan() {
            @Override
            public void chooseHeight(CharSequence text, int start, int end, int spanstartv, int lineHeight, Paint.FontMetricsInt fm) {
                Rect textRect = new Rect();
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                    textView.getPaint().getTextBounds(text, 0, text.length(), textRect);
                } else {
                    textView.getPaint().getTextBounds(text.toString(), 0, text.length(), textRect);
                }

                //Log.e("NoPaddingText", "修改之前 " + fm.toString());
                //Log.e("NoPaddingText", textRect.toString());
                //Log.e("NoPaddingText", "textSize: " + textView.getTextSize());

                if (textRect.bottom - textRect.top < textView.getTextSize()) {
                    // 一般我们认为字体的textview的textsize为textview的高度,当然有一定的误差 当然也可以自定义View 形式
                    // 当字体的高度没有字体的textsize大时,我们把大小设置成textsize,这样就可以解决文字的排版问题了
                    float tempPadding = (textView.getTextSize() - (textRect.bottom - textRect.top)) / 2f;
                    fm.top = (int) (textRect.top - tempPadding);
                    fm.bottom = (int) (textRect.bottom + tempPadding);
                } else {
                    // 这么设置可以完全消除padding,但是会有问题,
                    // 同样textsize的Textview 会因为设置的内容不一样而高度不一样
                    // 如果有什么特殊需求可以考虑用一下
                    fm.top = textRect.top;
                    fm.bottom = textRect.bottom;
                }
                fm.ascent = fm.top;
                fm.descent = fm.bottom;
                //Log.e("NoPaddingText", "修改之后 " + fm.toString());
            }
        }, 0, text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        return spannableString;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1394378.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++干货铺】红黑树 (Red Black Tree)

个人主页点击直达&#xff1a;小白不是程序媛 C系列专栏&#xff1a;C干货铺 代码仓库&#xff1a;Gitee 目录 前言 红黑树的概念 红黑树的性质 红黑树结点的定义 红黑树的插入操作 插入新的结点 检查规则进行改色 情况一 情况二 情况三 插入完整代码 红黑树的验…

ArcGIS Pro 如何新建布局

你是否已经习惯了在ArcGIS中数据视图和布局视图之间来回切换&#xff0c;到了ArcGIS Pro中却找不到二者之间切换的按钮&#xff0c;即使新建布局后却发现地图怎么却是一片空白。 这一切的一切都是因为ArcGIS Pro的功能框架完全不同&#xff0c;这里为大家介绍一下在ArcGIS Pro…

纸黄金实战投资技巧:避免亏损的有效策略

在纸黄金交易的实战中&#xff0c;避免亏损是每位投资者都追求的目标。虽然任何投资都存在一定的风险&#xff0c;但采取一些有效的策略可以帮助投资者最大限度地减少亏损的可能性。以下是一些在纸黄金交易中避免亏损的实战技巧&#xff1a; 一、设定止损点是避免亏损的关键 止…

【Android】自定义View onDraw()方法会调用两次

问题 自定义了View后&#xff0c;在构造函数中设置画笔颜色&#xff0c;发现它没起效&#xff0c;但是在onDraw()里设置颜色就会起效&#xff0c;出问题的代码如下&#xff1a; public RoundSeekbarView(Context context, Nullable AttributeSet attrs) {super(context, attrs…

dubbo入门案例!!!

入门案例之前我们先介绍一下&#xff1a;zookeeper。 Zookeeper是Apacahe Hadoop的子项目&#xff0c;可以为分布式应用程序协调服务&#xff0c;适合作为Dubbo服务的注册中心&#xff0c;负责服务地址的注册与查找&#xff0c;相当于目录服务&#xff0c;服务提供者和消费者只…

考下初级会计证书,好处竟有这么多!柯桥学会计去哪里?零基础入门手把手教学

初级会计证书有什么用&#xff1f; 初级会计证书有什么用&#xff1f;往下看&#xff0c;看完还没报名的建议大家赶紧报名&#xff0c;今年拿下&#xff01;因为初级会计证书真的很有用&#xff01; 01 求职刚需 初级会计是会计职业的起点&#xff0c;很多会计基础岗位&#x…

rust跟我学六:虚拟机检测

图为RUST吉祥物 大家好,我是get_local_info作者带剑书生,这里用一篇文章讲解get_local_info是怎么检测是否在虚拟机里运行的。 首先,先要了解get_local_info是什么? get_local_info是一个获取linux系统信息的rust三方库,并提供一些常用功能,目前版本0.2.4。详细介绍地址:…

关于Jenkins安装后,插件管理中插件版本依赖报错问题的解决方法

我们在初次安装完Jenkins后&#xff0c;通常会去下载要使用的插件&#xff0c;但是在插件管理中通常会出现插件版本问题的提示&#xff0c;例如&#xff1a; 此类问题一般可通过升级Jenkins到最新版本来解决问题。但是Jenkins从旧版本升级到最新版本&#xff0c;望望可能会连…

redis安装-Linux为例

可以下载一个Shell或者MobaXterm工具&#xff0c;便于操作 在redis官网下载压缩包 开始安装 安装依赖 yum install -y gcc tcl切换目录 切换目录后直接把redis安装包拖到/user/local/src/下 cd /user/local/src/解压然后安装 #解压 tar -zxvf redis-7.2.4.tar.gz #安装 …

并发编程之MESI缓存一致性协议

目录 CPU缓存架构 CPU多核缓存架构 CPU缓存架构缓存一致性的解决方案 缓存一致性协议实现原理 总线窥探 工作原理 窥探协议类型 缓存一致性协议 MESI协议 伪共享问题 CPU缓存架构 CPU缓存即高速缓冲存储器&#xff0c;是位于CPU与主内存间的一种容量较小但速度很高的…

如何在MinIO存储服务中通过Buckets实现远程访问管理界面上传文件

文章目录 前言1. 创建Buckets和Access Keys2. Linux 安装Cpolar3. 创建连接MinIO服务公网地址4. 远程调用MinIO服务小结5. 固定连接TCP公网地址6. 固定地址连接测试 前言 MinIO是一款高性能、分布式的对象存储系统&#xff0c;它可以100%的运行在标准硬件上&#xff0c;即X86等…

Halcon基于相关性的模板匹配

Halcon基于相关性的模板匹配 基于相关性的模板匹配其实是另一种基于灰度值的匹配&#xff0c;不过它的特点是使用一种归一化的互相关匹配&#xff08;Normalized Cross Correlation&#xff0c;NCC&#xff09;来衡量模板图像和检测图像之间的关系&#xff0c;因此&#xff0c…

前端实现轮训和长连接

简介 轮训和长连接相关内容可以参考之前的文章消息推送系统。消息推送系统-CSDN博客文章浏览阅读106次。在餐饮行业中&#xff0c;店内应用有pos、厨显屏等&#xff0c;云端应用为对应数据中心。为了实现云端数据和操作指令下发到店内应用&#xff0c;需要有一个系统实现这个功…

“重大利好”!以太坊坎昆升级临近!Layer2新玩家Blast不断蚕食市场份额,令竞品汗流浃背?

1月17日&#xff0c;坎昆升级&#xff08;Dencun&#xff09;率先在以太坊Goerli测试网启动&#xff0c;由于Goerli是参与者数量以及网络负载程度最高的测试网&#xff0c;仅次于以太坊主网&#xff0c;因此如果没什么问题&#xff0c;预示着主网升级已经不远。 而现在&#xf…

「alias」Linux 给命令起别名,自定义bash命令

0. 背景 Arch 系统没有 ll命令,在其他发行版用惯了一时间没有真不习惯,来配置一下吧! 1. 全局配置 我希望 ll 命令可以被所有人使用,所以应该配置在全局的bash配置文件中,一般这个全局bash配置文件在: /etc/bash.bashrc 切好管理员权限后,命令如下 echo “alias ll‘ls -l -…

QuestDB时序数据库快速入门

简介 QuestDB是一个开源的高性能时序数据库&#xff0c;专门用于处理时间序列相关的数据存储与查询&#xff1b; QuestDB使用列式存储模型。数据存储在表中&#xff0c;每列存储在其自己的文件和其自己的本机格式中。新数据被附加到每列的底部&#xff0c;以便能够按照与摄取…

【音视频】基于NGINX如何播放rtmp视频流

背景 现阶段直播越来越流行&#xff0c;直播技术发展也越来越快。Webrtc、rtmp、rtsp是比较火热的技术&#xff0c;而且应用也比较广泛。本文通过实践来展开介绍关于rtmp如何播放。 概要 本文重点介绍基于NGINX如何播放rtmp视频流 正文 1、构造rtsp视频流 可以参考上一篇…

BigeMap在Unity3d中的应用,助力数字孪生

1. 首先需要用到3个软件&#xff0c;unity&#xff0c;gis office 和 bigemap离线服务器 Unity下载地址:点击前往下载页面(Unity需要 Unity 2021.3.2f1之后的版本) Gis office下载地址:点击前往下载页面 Bigemap离线服务器 下载地址: 点击前往下载页面 Unity用于数字孪生项…

F-Droid:开源Android应用的宝库

F-Droid&#xff1a;开源Android应用的宝库 引言 F-Droid是一个开源应用程序存储库&#xff0c;旨在为安卓用户提供自由、隐私和安全的应用程序。它最初于2010年由Ciaran Gultnieks创建&#xff0c;因为他认为Google Play Store上的应用程序不够透明和安全。F-Droid的目标是为…

FTP文件传输与vsftpd配置

一 存储类型 直连式存储DAS 适用于那些数据量不大&#xff0c;对磁盘访问速度要求较高的中小企业 存储区域网络SAN 用来存储非结构化数据&#xff0c;虽然受限于以太网的速度&#xff0c;但是部署灵活&#xff0c;成本低 网络附加存储NAS 适用于大型应用或数据库系统&…