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

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

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大法。

我这边是写了一个工具类,当然也可以自定义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);
    }

}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【总结】Dinky学习笔记

概述 Dinky 是一个开箱即用、易扩展&#xff0c;以 Apache Flink 为基础&#xff0c;连接 OLAP 和数据湖等众多框架的一站式实时计算平台&#xff0c;致力于流批一体和湖仓一体的探索与实践 官网&#xff1a;Dinky 核心特性 沉浸式&#xff1a;提供专业的 DataStudio 功能&a…

喜好儿AI周报Weekly(第9期)CES2024 AI产业大爆发 | Rabbit R1 | 3D-Fauna | OLED屏幕 | Genie | MagicVideoV2 | Magnific

各位观众朋友们大家好&#xff01;我是被老板派去出差逛CES2024 拉斯维加斯消费电子展差点迷路回不来的阿喜。一起去看看这一周有什么新鲜事吧。 本期导读&#xff1a; 逛逛CES 2024消费电子展Rabbit R1人工智能设备三星AI机器人BallieLG无线透明OLED屏幕Portalgraph VR空间投…

【Linux】初识Linux及几个基本指令

Hello everybody!算算时间我已经有一个多月没有更新啦&#xff01;因为本专业是纺织工程&#xff0c;所以一直在复习应付期末考试\(0^◇^0)/。那好&#xff0c;废话不多说。让我们进入今天的主题&#xff01; 关于Linux系统可能很多同学不是很熟悉&#xff0c;有的人可能听过&…

4、C语言:指针与数组

数组与指针 指针与地址指针与函数参数指针与数组地址算数运算字符指针与函数指针数组以及指向指针的指针多维数组命令行参数指向函数的指针复杂声明 指针是一种保存变量地址的变量。C语言中&#xff0c;指针的使用非常广泛&#xff0c;原因之一是&#xff0c;指针常常是表达某个…

紫光展锐T610安卓核心板_虎贲T610安卓核心板参数

紫光展锐T610核心板是一款结构紧凑的4G智能模块&#xff0c;尺寸为52.5nm*38.5nm*2.9nm&#xff0c;适用于对产品结构尺寸要求较高的场合。该核心板搭载Android 11操作系统&#xff0c;采用12nm制程工艺&#xff0c;配备八核1.8GHz的CPU&#xff0c;包括2 x A751.8GHz 6 x A55…

20240115在ubuntu20.04.6下查看显卡信息

20240115在ubuntu20.04.6下查看显卡信息 2024/1/15 17:33 百度&#xff1a;ubuntu查看显卡型号命令 https://linux.xiaosiseo.com/post/6037.html#id4 Ubuntu查看显卡信息命令 小四LINUX7个月前 (05-22)Ubuntu3230 小四LINUX&#xff0c;是小四运营旗下网站&#xff0c;专注LIN…

excel管理接口测试用例

闲话休扯&#xff0c;上需求&#xff1a;自动读取、执行excel里面的接口测试用例&#xff0c;测试完成后&#xff0c;返回错误结果并发送邮件通知。 分析&#xff1a; 1、设计excel表格 2、读取excel表格 3、拼接url&#xff0c;发送请求 4、汇总错误结果、发送邮件 开始实现…

短视频IP运营流程架构SOP模板PPT

【干货资料持续更新&#xff0c;以防走丢】 短视频IP运营流程架构SOP模板PPT 部分资料预览 资料部分是网络整理&#xff0c;仅供学习参考。 抖音运营资料合集&#xff08;完整资料包含以下内容&#xff09; 目录 抖音15秒短视频剧本创作公式 在抖音这个短视频平台上&#…

QT报错记录

Ubuntu22.04安装Qt之后启动Qt Creator报错&#xff1a; Fron 6.5.0, xcb-cursor0 or libxcb-cursor0 is needed to load the Qt xcb platforn plugin. Could not load. This application failed to start because no Qt platforn plugin could be initialized. Reinstalling t…

OpenCV-Python(41):背景减除

目标 学习并掌握OpenCV中的背景减除方法 背景说明 在很多基础应用中背景检出都是一个非常重要的步骤。例如&#xff1a;顾客统计&#xff0c;使用一个静态摄像头来记录进入和离开房间的人数&#xff0c;或者是交通摄像头&#xff0c;需要提取交通工具的信息等。在所有的这些例…

QT day6

目录 思维导图 学生管理系统 思维导图 学生管理系统 ui界面 头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QSqlDatabase> //数据库管理类 #include <QSqlQuery> //执行sql语句类 #include <QSqlRecord> //数据库记录类 …

【大模型 + 网络安全 】炒作内卷 or 革新升级?

一年前&#xff0c;ChatGPT问世&#xff0c;以强大的信息整合推理和语言对话能力惊艳全球&#xff0c;随后&#xff0c;以大语言模型LLM&#xff08;以下简称“大模型”&#xff09;为代表的AI技术应用全面席卷&#xff0c;赋能千行百业&#xff0c;重构业务流程&#xff0c;加…

Qt点击按钮在其附近弹出一个窗口

效果 FS_PopupWidget.h #ifndef FS_POPUPWIDGET_H #define FS_POPUPWIDGET_H#pragma once#include <QToolButton> #include <QWidgetAction> #include <QPointer>class QMenu;class FS_PopupWidget : public QToolButton {Q_OBJECTpublic:FS_PopupWidget(QW…

Android的setContentView流程

一.Activity里面的mWindow是啥 在ActivityThread的performLaunchActivity方法里面&#xff1a; private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {ActivityInfo aInfo r.activityInfo;if (r.packageInfo null) {r.packageInfo getP…

2024年甘肃省职业院校技能大赛信息安全管理与评估 样题一 模块二

竞赛需要完成三个阶段的任务&#xff0c;分别完成三个模块&#xff0c;总分共计 1000分。三个模块内容和分值分别是&#xff1a; 1.第一阶段&#xff1a;模块一 网络平台搭建与设备安全防护&#xff08;180 分钟&#xff0c;300 分&#xff09;。 2.第二阶段&#xff1a;模块二…

如何分析测试任务及需求(附分析流程)

测试分析 确认测试范围 根据测试项目的不同需求&#xff0c;有大致几类测试项目类型&#xff1a;商户/平台功能测试、支付方式接入测试、架构调整类测试、后台优化测试、性能测试、基本功能自动化测试。 测试项目需要按照文档要求进行测试需求分析&#xff0c;并给出对应的输出…

数据结构学习 jz66 构建乘积数组

关键词&#xff1a;数学 双指针 方法一&#xff1a;这个题目我一开始做不知道不能用除法。我做的&#xff1a;[ 用时: 12 m 12 s ] 用了除法 分类讨论 方法二&#xff1a;后来看了提示&#xff0c;双指针&#xff0c;两边各开始乘。 方法三&#xff1a;然后又看了答案可以节…

几款提高开发效率的Idea 插件

1、ignore 开发代码过程中经常会有一些需要提交到代码仓库的文件&#xff0c;比如java文件生成的.class、.jar 等&#xff0c;如果将编译后的文件都提交到代码库那么代码库会很大&#xff0c;关键是没有必要。 这款插件就可以很方便的解决某类文件或者某个文件夹不需要提交到…

OS进程管理

进程 文章目录 进程概念组成特征状态与转换组织方式链接方式索引方式 进程控制实现进程控制如何实现原语的“原子性” 进程通信(IPC)共享存储基于存储区共享基于数据结构的共享 消息传递直接通信方式间接通信方式 管道通信 线程实现方式用户级线程内核级线程 多线程模式状态与转…

文件销毁的方法与安全操作守则, 淼一护航文件安全最后一公里

文件销毁的目前大概分为三种&#xff0c;分别是&#xff1a; 一、做成纸浆填埋。把需要销毁处理的过期涉密文件放到工业浸泡池里面浸泡&#xff0c;放入自来水和一定比例的化学药物&#xff0c;文件经过5-8个小时的浸泡后变成了纸浆&#xff0c;上面记录的信息也随之被销毁。最…