先看下最終實(shí)現(xiàn)效果,滿意您在往下看:
TextView 繪制的時(shí)候自帶一定的Padding值,要想實(shí)現(xiàn)去掉默認(rèn)的padding值,xml文件可以設(shè)置一個(gè)屬性值 :
android:includeFontPadding="false"
然后運(yùn)行起來就會(huì)發(fā)現(xiàn),并沒有什么卵用,也不能說完全沒有,但效果差點(diǎn)意思。我就不運(yùn)行了,在編譯器上也能大概看到效果,如下圖,我們可以看到依然有著無法刪除的padding值。
我們先來看一下繪制TextView時(shí)的幾條基準(zhǔn)線:
-
top
:在給定文本大小下,字體中最高字形高于基線的最大距離,即能繪制的最高點(diǎn) -
ascent
:單倍行距文本的基線以上建議距離,即推薦的文字繪制上邊緣線 -
base
:文字繪制基準(zhǔn)線,也就是坐標(biāo)軸,X軸就是Baseline -
decent
:單間距文本低于基線的建議距離,即推薦的文字繪制下邊緣線 -
bottom
:能繪制的最低點(diǎn)
Textview繪制文字會(huì)在??ascent 和 decent?
之間,外面的距離我們可以理解為 類似?padding一樣的間隔,但又并不是我們?cè)O(shè)置的paddingTop,paddingBottom。
要解決這個(gè)這個(gè)問題,首先我們要知道textview的內(nèi)容文字繪制的真實(shí)區(qū)域:
紅色的區(qū)域就是內(nèi)容的真實(shí)高度,藍(lán)色的部分就是textview繪制的多余的部分,現(xiàn)在我們要去掉這一部分,首先可以通過?getTextBounds 來獲取繪制的真實(shí)區(qū)域
textView.getPaint().getTextBounds(text, 0, text.length(), textRect);
獲取到真實(shí)區(qū)域后,那么再來看textview繪制的幾條基準(zhǔn)線,你想到了什么,是的,我們只需要稍微移動(dòng)一下這幾條線把高度壓縮到文字的展示繪制區(qū)域即可,實(shí)現(xiàn)用 SpannableString 來實(shí)現(xiàn),SpannableString 是 android? 里面專門用來實(shí)現(xiàn)設(shè)置 textview 文字樣式的類,這個(gè)不清楚的自行查詢一下,這里不贅述了,具體我們用的是??LineHeightSpan ,可以通過修改 textview 的行高來實(shí)現(xiàn)我們的目的。具體看下代碼:
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 繪制區(qū)域 縮小到 文字真實(shí)大小的區(qū)域
// 這個(gè)是有一點(diǎn)問題的,看下圖
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 就是用來調(diào)整 繪制的時(shí)候的具體位置的,我們把textview的繪制區(qū)域直接設(shè)置到?FontMetricsInt 里面,實(shí)現(xiàn)的效果如下圖:
看起來確實(shí)去掉了padding,而且去的干干凈凈,需要這種效果的可以停下來,施展CV大法。
這種實(shí)現(xiàn)方式原理也很簡單,是使文字真實(shí)的繪制區(qū)域高度為所繪制內(nèi)容中字符的最大高度,這樣可能會(huì)造成排版問題,文字對(duì)不齊,那我們就需要統(tǒng)一下繪制內(nèi)容的高度,我們知道TextView 有個(gè)屬性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()) {
// 一般我們認(rèn)為字體的textview的textsize為textview的高度,當(dāng)然有一定的誤差
// 當(dāng)字體的高度沒有字體的textsize大時(shí),我們把大小設(shè)置成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來約束統(tǒng)一繪制高度,不足TextSize 的,補(bǔ)充添加一個(gè)padding值補(bǔ)齊正常的高度,實(shí)現(xiàn)效果如下:
這樣看著順眼多了,繼續(xù)CV大法。
完整代碼如下:(兩種實(shí)現(xiàn)形式:1、工具類; 2、自定義View形式實(shí)現(xiàn))文章來源:http://www.zghlxwxcb.cn/news/detail-805446.html
點(diǎn)這里跳轉(zhuǎn)項(xiàng)目源碼地址文章來源地址http://www.zghlxwxcb.cn/news/detail-805446.html
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;
// 工具類 實(shí)現(xiàn)
public class TextUtil {
// 設(shè)置上下取消繪制的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 快速實(shí)現(xiàn)去除 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()) {
// 一般我們認(rèn)為字體的textview的textsize為textview的高度,當(dāng)然有一定的誤差 當(dāng)然也可以自定義View 形式
// 當(dāng)字體的高度沒有字體的textsize大時(shí),我們把大小設(shè)置成textsize,這樣就可以解決文字的排版問題了
float tempPadding = (textView.getTextSize() - (textRect.bottom - textRect.top)) / 2f;
fm.top = (int) (textRect.top - tempPadding);
fm.bottom = (int) (textRect.bottom + tempPadding);
} else {
// 這么設(shè)置可以完全消除padding,但是會(huì)有問題,
// 同樣textsize的Textview 會(huì)因?yàn)樵O(shè)置的內(nèi)容不一樣而高度不一樣
// 如果有什么特殊需求可以考慮用一下
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);
}
// 設(shè)置上下取消繪制的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 快速實(shí)現(xiàn)去除 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());
// 這么設(shè)置可以完全消除padding,但是會(huì)有問題,
// 同樣textsize的Textview 會(huì)因?yàn)樵O(shè)置的內(nèi)容不一樣而高度不一樣
// 如果有什么特殊需求可以考慮用一下
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 形式 實(shí)現(xiàn) NoPaddingTextView 布局可以預(yù)覽
*/
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);
}
// 設(shè)置上下取消繪制的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 快速實(shí)現(xiàn)去除 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()) {
// 一般我們認(rèn)為字體的textview的textsize為textview的高度,當(dāng)然有一定的誤差 當(dāng)然也可以自定義View 形式
// 當(dāng)字體的高度沒有字體的textsize大時(shí),我們把大小設(shè)置成textsize,這樣就可以解決文字的排版問題了
float tempPadding = (textView.getTextSize() - (textRect.bottom - textRect.top)) / 2f;
fm.top = (int) (textRect.top - tempPadding);
fm.bottom = (int) (textRect.bottom + tempPadding);
} else {
// 這么設(shè)置可以完全消除padding,但是會(huì)有問題,
// 同樣textsize的Textview 會(huì)因?yàn)樵O(shè)置的內(nèi)容不一樣而高度不一樣
// 如果有什么特殊需求可以考慮用一下
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;
}
}
到了這里,關(guān)于Android Text View 去掉默認(rèn)的padding的實(shí)現(xiàn)方法的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!