Android Study 之真正解決TextView字間距,那些扯淡的邊兒去吧

LZ-Says:從個(gè)人的角度來說為什么要寫博客,一方面可以拓展知識(shí),通過分享去了解更多的知識(shí);二方面可以通過優(yōu)質(zhì)的博文去推廣自己。

寫博客,就好像工作一樣,要認(rèn)真,要對(duì)自己寫的東西負(fù)責(zé),更要對(duì)別人看的負(fù)責(zé)。

雖然目前技術(shù)不是很牛,但我相信,一點(diǎn)一滴積累,總會(huì)有起來的那天。

我們一起加油笑看工作中不順心

前言

說說今天遇到的問題吧。

老大說,搞個(gè)類似身份證原樣的布局。其中一些TextView需要設(shè)置相對(duì)應(yīng)的字間距,網(wǎng)上搜了n個(gè),郁悶的我,真想罵娘,都是什么啊。不過,民間總有大神在,好歹解決了我的問題。下面給大家簡單介紹下今天遇到的坑。

坑的多了,經(jīng)驗(yàn)也就多了,莫怕

關(guān)于TextView設(shè)置字間距,有的人就說了,那不是so esay嗎,直接一個(gè)屬性搞定。

他們說:<font color=#FF0000>可以利用 TextView 的 setTextScaleX() 方法設(shè)置字間距

LZ滿懷信心的去驗(yàn)證了,嘿,還真可以。美了沒一會(huì)兒,老大說,貌似不對(duì)。瞬間凌亂了,讓我們一起看看這到底什么情況?

驗(yàn)證之路

首先打開一個(gè)布局,整倆個(gè)TextView,網(wǎng)上隨便找段話復(fù)制上去,方便我們查看效果。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="15dp"
    android:orientation="vertical"
    tools:context="cn.hlq.textviewsetwordspace.MainActivity">

    <View
        android:layout_width="match_parent"
        android:layout_height="2dp"
        android:layout_marginBottom="5dp"
        android:background="#CCCCCC"/>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="我是準(zhǔn)備測(cè)試?yán)樱阂粋€(gè)人,只要堅(jiān)持不懈,就能在別人失敗的地方獲得成功。對(duì)于那些深思熟慮穩(wěn)步向前的人,道路并不漫長;對(duì)于那些臥薪嘗膽堅(jiān)韌不拔的人,榮譽(yù)并不遙遠(yuǎn)。"/>

    <View
        android:layout_width="match_parent"
        android:layout_height="2dp"
        android:layout_marginBottom="5dp"
        android:layout_marginTop="5dp"
        android:background="#CCCCCC"/>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="我是設(shè)置textScaleX例子:一個(gè)人,只要堅(jiān)持不懈,就能在別人失敗的地方獲得成功。對(duì)于那些深思熟慮穩(wěn)步向前的人,道路并不漫長;對(duì)于那些臥薪嘗膽堅(jiān)韌不拔的人,榮譽(yù)并不遙遠(yuǎn)。"
        android:textScaleX="1.5"/>
</LinearLayout>

布局文件很簡單,一個(gè)是測(cè)試的原文,一個(gè)是設(shè)置了android:textScaleX,那么接下來,看看他們分別展示的效果,到底有何區(qū)別。

這里寫圖片描述

按照網(wǎng)上某些教程所述,設(shè)置這個(gè)屬性即可實(shí)現(xiàn)我們所要效果,but,事實(shí)擺在我們眼前,一目了然。

按照谷歌提供方法,我們一般都可以從方法名稱簡介得出此方法或者屬性名稱代碼什么含義。基于這個(gè),LZ百度了下,如下:

這里寫圖片描述

按照字面理解,結(jié)合效果,我們可以得出以下結(jié)論:

android:textScaleX="比例":<font color=#FF0000> 即水平按照比例進(jìn)行縮放

so 各位,這個(gè)所謂的能實(shí)現(xiàn),壓根的就是忽悠。那么問題來了,那么,如果原型圖中,給定了某個(gè)TextView的字間距,那么這時(shí)候,我們?cè)撛趺崔k呢?

表急,<font color=#FF0000>民間自有大神在!

經(jīng)過搜索,排除,F(xiàn)uck之后,找到一位高手提供解決的方案 ,經(jīng)過測(cè)試后,發(fā)現(xiàn)能如愿實(shí)現(xiàn)我們想要的效果。

那么,接下來,讓我們看看高手是如何解決這個(gè)問題的吧~

下面引用高手說的一些話:

I built a custom class that <font color=#FF0000>extends TextView and adds a method "setSpacing"</font>. The workaround is similar to what @Noah said. The method adds a space between each letter of the String and with SpannedString changes the TextScaleX of the spaces, allowing positive and negative spacing.

<font color=#FF0000>Hope that helps someone ^^

LZ英文不是很6,大體猜測(cè)如下:

創(chuàng)建一個(gè)類,繼承TextView,添加一個(gè)setSpacing方法。這個(gè)方法在字符串中的每個(gè)字符之間添加了一個(gè)空間,并通過TextScaleX以X軸等比例進(jìn)行縮放,允許其在前后字符間添加空間。

PS:LZ英文不是很6,如有不正之處,歡迎大家指正~

接下來,我們一塊瞅瞅高人是如何實(shí)現(xiàn)的。

民間自有高人在

廢話不多說直接先給大家瞅瞅效果,免得說LZ坑人。

啦啦啦,德瑪西亞萬歲~

這里寫圖片描述

為了給大家看的直觀方便,特意設(shè)置了大一點(diǎn),現(xiàn)在是不是很清楚的看到了我們要的效果擼碼,擼碼

package cn.hlq.textviewsetwordspace;

import android.content.Context;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ScaleXSpan;
import android.util.AttributeSet;
import android.widget.TextView;

/**
 * Created by HLQ on 2017/5/12
 */
public class LetterSpacingTextView extends TextView {

    private float spacing = Spacing.NORMAL;
    private CharSequence originalText = "";

    public LetterSpacingTextView(Context context) {
        super(context);
    }

    public LetterSpacingTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public LetterSpacingTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    /**
     * 獲取字間距
     *
     * @return
     */
    public float getSpacing() {
        return this.spacing;
    }

    /**
     * 設(shè)置間距
     *
     * @param spacing
     */
    public void setSpacing(float spacing) {
        this.spacing = spacing;
        applySpacing();
    }

    @Override
    public void setText(CharSequence text, BufferType type) {
        originalText = text;
        applySpacing();
    }

    @Override
    public CharSequence getText() {
        return originalText;
    }

    /**
     * 添加應(yīng)用空間
     */
    private void applySpacing() {
        if (this == null || this.originalText == null) return;
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < originalText.length(); i++) {
            builder.append(originalText.charAt(i));
            if (i + 1 < originalText.length()) {
                // \u00A0 不間斷空格
                // 追加空格
                builder.append("\u00A0");
            }
        }
        // TextView通常用來顯示普通文本,但是有時(shí)候需要對(duì)其中某些文本進(jìn)行樣式、事件方面的設(shè)置。Android系統(tǒng)通過SpannableString類來對(duì)指定文本進(jìn)行相關(guān)處理,具體有以下功能:
        // 1、BackgroundColorSpan 背景色
        // 2、ClickableSpan 文本可點(diǎn)擊,有點(diǎn)擊事件
        // 3、ForegroundColorSpan 文本顏色(前景色)
        // 4、MaskFilterSpan 修飾效果,如模糊(BlurMaskFilter)、浮雕(EmbossMaskFilter)
        // 5、MetricAffectingSpan 父類,一般不用
        // 6、RasterizerSpan 光柵效果
        // 7、StrikethroughSpan 刪除線(中劃線)
        // 8、SuggestionSpan 相當(dāng)于占位符
        // 9、UnderlineSpan 下劃線
        // 10、AbsoluteSizeSpan 絕對(duì)大?。ㄎ谋咀煮w)
        // 11、DynamicDrawableSpan 設(shè)置圖片,基于文本基線或底部對(duì)齊。
        // 12、ImageSpan 圖片
        // 13、RelativeSizeSpan 相對(duì)大小(文本字體)
        // 14、ReplacementSpan 父類,一般不用
        // 15、ScaleXSpan 基于x軸縮放
        // 16、StyleSpan 字體樣式:粗體、斜體等
        // 17、SubscriptSpan 下標(biāo)(數(shù)學(xué)公式會(huì)用到)
        // 18、SuperscriptSpan 上標(biāo)(數(shù)學(xué)公式會(huì)用到)
        // 19、TextAppearanceSpan 文本外貌(包括字體、大小、樣式和顏色)
        // 20、TypefaceSpan 文本字體
        // 21、URLSpan 文本超鏈接
        // 我們也是通過這個(gè),去設(shè)置空格
        SpannableString finalText = new SpannableString(builder.toString());
        if (builder.toString().length() > 1) { // 如果當(dāng)前TextView內(nèi)容長度大于1,則進(jìn)行空格添加
            for (int i = 1; i < builder.toString().length(); i += 2) { // 小demo:100  1 0 0
                // 按照x軸等比例進(jìn)行縮放 通過我們?cè)O(shè)置的字間距+1除以10進(jìn)行等比縮放
                finalText.setSpan(new ScaleXSpan((spacing + 1) / 10), i, i + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            }
        }
        super.setText(finalText, TextView.BufferType.SPANNABLE);
    }

    public class Spacing {
        public final static float NORMAL = 0;
    }
}

看完代碼,突然貌似想明白了一些,高手也是曲線救國,不知道大家有沒有注意到下面這幾行代碼:

    if (builder.toString().length() > 1) { // 如果當(dāng)前TextView內(nèi)容長度大于1,則進(jìn)行空格添加
            for (int i = 1; i < builder.toString().length(); i += 2) { // 小demo:100  1 0 0
                // 按照x軸等比例進(jìn)行縮放 通過我們?cè)O(shè)置的字間距+1除以10進(jìn)行等比縮放
                finalText.setSpan(new ScaleXSpan((spacing + 1) / 10), i, i + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            }
        }

同樣是通過ScaleXSpan來實(shí)現(xiàn)我們的要求,哈哈~

結(jié)束

經(jīng)過這個(gè)博文,LZ明白了,有時(shí)候解決問題不一定要正面干,曲線救國也不乏是種可靠的辦法~

感謝大家查閱~

下面為大家附上GitHub地址:

https://github.com/HLQ-Struggle/TextViewSetWordSpace

特別喜歡CSDN的一句話,在此分享給大家,共勉~

這里寫圖片描述

參考資料

民間自有高人在!感謝高手奉獻(xiàn)~

下面為大家附上參考地址:

http://stackoverflow.com/questions/1640659/how-to-adjust-text-kerning-in-android-textview/1644061#1644061

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容