欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android富文本實現(xiàn)的幾種方式匯總

 更新時間:2022年05月26日 09:33:08   作者:newki  
由于項目中需要使用到富文本顯示和編輯,索性整理下,這篇文章主要給大家介紹了關(guān)于Android富文本實現(xiàn)的幾種方式,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下

Android富文本的實現(xiàn)的幾種方式

在Android開發(fā)過程中,最常見的富文本場景一般都是變色,點擊跳轉(zhuǎn),或者局部變大,而我們實現(xiàn)的方式通常分為兩種。

一種是Html的方式定義在string中,通過html標(biāo)簽變色,變大,通過占位符填充數(shù)據(jù)。一般常用于有國際化的需求。

另一種是CharSequence的setSpan設(shè)置自定義Span。功能更強大,細讀也更細,便于精準(zhǔn)操作。一般用于沒有國際化需求的地方。

為什么有國際化相關(guān)的要求,是因為一般setSpan的方式都是添加或者根據(jù)索引替換對應(yīng)的文本,如果國際化之后中英馬等語言的順序都變了,自然效果就不同了。當(dāng)然也可以通過判斷語言進行不同的操作。這是后話了。

一,Html的方式實現(xiàn)

1.1 占位符的處理

先看看string xml中如何處理占位符 %N代表第N個參數(shù),如%3代表的是第三個參數(shù); $是結(jié)束符;

<string name="string_test_1">學(xué)號:%1$d ;姓名:%2$s ;成績:%3$.2f</string>

使用的時候:

String testStr = getResources().getString(R.string.string_test_1);
String result = String.format(testStr,1001,"張三",9.235);
System.out.println(result);

1.2 Html的占位符

和上面的差不多:

    <string name="purchase_points"><![CDATA[ <font color="#767676">Purchase with</font> 
    <font color="#FF5E75">%s</font><font color="#767676"> points?</font>]]></string>

使用:

    String formatPoints = PointFormatUtils.formatPoints(points);
    String result = String.format(getResources().getString(R.string.purchase_points),formatPoints);
    tv_message.setText(Html.fromHtml(result));

注:Html.fromHtml還分Android N的兼容處理,需要傳入Model,不同的Model展示的效果有所不同,這里不做展開。其實效果大差不差。

實現(xiàn)效果:

結(jié)論:

能實現(xiàn)變色,簡單的變大等簡單功能,由于TextView不能解析更多的Html標(biāo)簽,由此還出現(xiàn)了一些庫,讓TextView支持更多標(biāo)簽,但是我們Android實現(xiàn)富文本本身就是小功能,還得依賴庫支持更多標(biāo)簽也都用不上,得不償失啊。

如果有一些自定義的需求,我們可以使用自定義標(biāo)簽+自定義標(biāo)簽的功能,例如Html中的自定義字體

1.3 自定義Html標(biāo)簽

先定義自定義字體的Span類

/**
 * 系統(tǒng)原生的TypefaceSpan只能使用原生的默認(rèn)字體
 * 如果使用自定義的字體,通過這個來實現(xiàn)
 */
public class MyTypefaceSpan extends MetricAffectingSpan {
    private final Typeface typeface;

    public MyTypefaceSpan(final Typeface typeface) {
        this.typeface = typeface;
    }

    @Override
    public void updateDrawState(final TextPaint drawState) {
        apply(drawState);
    }

    @Override
    public void updateMeasureState(final TextPaint paint) {
        apply(paint);
    }

    private void apply(final Paint paint) {
        final Typeface oldTypeface = paint.getTypeface();
        final int oldStyle = oldTypeface != null ? oldTypeface.getStyle() : 0;
        int fakeStyle = oldStyle & ~typeface.getStyle();
        if ((fakeStyle & Typeface.BOLD) != 0) {
            paint.setFakeBoldText(true);
        }
        if ((fakeStyle & Typeface.ITALIC) != 0) {
            paint.setTextSkewX(-0.25f);
        }
        paint.setTypeface(typeface);
    }
}

自定義標(biāo)簽:

/**
 * Html的TextView標(biāo)簽解釋
 * <face></face>
 */
public class TypeFaceLabel implements Html.TagHandler {
    private Typeface typeface;
    private int startIndex = 0;
    private int stopIndex = 0;

    public TypeFaceLabel(Typeface typeface) {
        this.typeface = typeface;
    }

    @Override
    public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) {
        if (tag.toLowerCase().equals("face")) {
            if (opening) {
                startIndex = output.length();
            } else {
                stopIndex = output.length();
                //使用的是自定義的字體來實現(xiàn)
                output.setSpan(new MyTypefaceSpan(typeface), startIndex, stopIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            }
        }
    }
}

定義Xml并使用,注意自定義face標(biāo)簽

  String content = "<font color=\"#000000\">HR from </font>" +
                    "<face><font color=\"#0689FB\">" + item.employer_name + "</font></face>" +
                    "<font color=\"#000000\"> has viewed your resume.</font>";

    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
          tv_resume_log_content.setText(Html.fromHtml(content, Html.FROM_HTML_MODE_LEGACY, null, new TypeFaceLabel(TypefaceUtil.getSFSemobold(mContext))));
        } else {
          tv_resume_log_content.setText(Html.fromHtml(content, null, new TypeFaceLabel(TypefaceUtil.getSFSemobold(mContext))));
    }

效果如下:

如果想實現(xiàn)其他的變大 下劃線 中劃線等Span效果,都可以通過自定義的Html標(biāo)簽+自定義Span實現(xiàn)相應(yīng)的效果。

二,Span的幾種實現(xiàn)方式

雖然通過Html的方式可以實現(xiàn)各種效果,但是定義的時候也太過復(fù)雜,各種定義Span 定義標(biāo)簽之類的,有沒有更簡單和直接的?

有,我們直接封裝Span就行了。

2.1 java - SpanUtil

在Java中我們可以封裝工具類一個如下:

/**
 * String字符串通過區(qū)間來改變顏色,大小,字體,下劃線等
 */
public class SpanUtils {
    private static final SpanUtils ourInstance = new SpanUtils();
    public static SpanUtils getInstance() {
        return ourInstance;
    }

    private SpanUtils() {
    }

    /**
     * 變大變小
     */
    public CharSequence toSizeSpan(CharSequence charSequence, int start, int end, float scale) {

        SpannableString spannableString = new SpannableString(charSequence);

        spannableString.setSpan(
                new RelativeSizeSpan(scale),
                start,
                end,
                Spannable.SPAN_INCLUSIVE_EXCLUSIVE);

        return spannableString;
    }

    /**
     * 變色
     */
    public CharSequence toColorSpan(CharSequence charSequence, int start, int end, int color) {

        SpannableString spannableString = new SpannableString(charSequence);

        spannableString.setSpan(
                new ForegroundColorSpan(color),
                start,
                end,
                Spannable.SPAN_INCLUSIVE_EXCLUSIVE);

        return spannableString;
    }

    /**
     * 變背景色
     */
    public CharSequence toBackgroundColorSpan(CharSequence charSequence, int start, int end, int color) {

        SpannableString spannableString = new SpannableString(charSequence);

        spannableString.setSpan(
                new BackgroundColorSpan(color),
                start,
                end,
                Spannable.SPAN_INCLUSIVE_EXCLUSIVE);

        return spannableString;
    }

    private long mLastClickTime = 0;
    public static final int TIME_INTERVAL = 1000;

    /**
     * 可點擊-帶下劃線
     */
    public CharSequence toClickSpan(CharSequence charSequence, int start, int end, int color, boolean needUnderLine, OnSpanClickListener listener) {

        SpannableString spannableString = new SpannableString(charSequence);

        ClickableSpan clickableSpan = new ClickableSpan() {
            @Override
            public void onClick(@NonNull View widget) {
                if (listener != null) {
                    //防止重復(fù)點擊
                    if (System.currentTimeMillis() - mLastClickTime >= TIME_INTERVAL) {
                        //to do
                        listener.onClick(charSequence.subSequence(start, end));

                        mLastClickTime = System.currentTimeMillis();
                    }

                }
            }

            @Override
            public void updateDrawState(@NonNull TextPaint ds) {
                ds.setColor(color);
                ds.setUnderlineText(needUnderLine);
            }
        };

        spannableString.setSpan(
                clickableSpan,
                start,
                end,
                Spannable.SPAN_INCLUSIVE_EXCLUSIVE);

        return spannableString;
    }

    public interface OnSpanClickListener {
        void onClick(CharSequence charSequence);
    }


    /**
     * 變成自定義的字體
     */
    public CharSequence toCustomTypeFaceSpan(CharSequence charSequence, int start, int end, Typeface typeface) {

        SpannableString spannableString = new SpannableString(charSequence);

        spannableString.setSpan(
                new MyTypefaceSpan(typeface),
                start,
                end,
                Spannable.SPAN_INCLUSIVE_EXCLUSIVE);

        return spannableString;
    }
}

2.2 kotlin擴展

/**
 * 將一段文字中指定range的文字改變大小
 * @param range 要改變大小的文字的范圍
 * @param scale 縮放值,大于1,則比其他文字大;小于1,則比其他文字小;默認(rèn)是1.5
 */
fun CharSequence.toSizeSpan(range: IntRange, scale: Float = 1.5f): CharSequence {
    return SpannableString(this).apply {
        setSpan(
            RelativeSizeSpan(scale),
            range.start,
            range.endInclusive,
            Spannable.SPAN_INCLUSIVE_EXCLUSIVE
        )
    }
}

/**
 * 將一段文字中指定range的文字改變前景色
 * @param range 要改變前景色的文字的范圍
 * @param color 要改變的顏色,默認(rèn)是紅色
 */
fun CharSequence.toColorSpan(range: IntRange, color: Int = Color.RED): CharSequence {
    return SpannableString(this).apply {
        setSpan(
            ForegroundColorSpan(color),
            range.start,
            range.endInclusive,
            Spannable.SPAN_INCLUSIVE_EXCLUSIVE
        )
    }
}

/**
 * 將一段文字中指定range的文字改變背景色
 * @param range 要改變背景色的文字的范圍
 * @param color 要改變的顏色,默認(rèn)是紅色
 */
fun CharSequence.toBackgroundColorSpan(range: IntRange, color: Int = Color.RED): CharSequence {
    return SpannableString(this).apply {
        setSpan(
            BackgroundColorSpan(color),
            range.start,
            range.endInclusive,
            Spannable.SPAN_INCLUSIVE_EXCLUSIVE
        )
    }
}

/**
 * 將一段文字中指定range的文字添加刪除線
 * @param range 要添加刪除線的文字的范圍
 */
fun CharSequence.toStrikeThrougthSpan(range: IntRange): CharSequence {
    return SpannableString(this).apply {
        setSpan(
            StrikethroughSpan(),
            range.start,
            range.endInclusive,
            Spannable.SPAN_INCLUSIVE_EXCLUSIVE
        )
    }
}

/**
 * 將一段文字中指定range的文字添加顏色和點擊事件
 * @param range 目標(biāo)文字的范圍
 */
fun CharSequence.toClickSpan(
    range: IntRange,
    color: Int = Color.RED,
    isUnderlineText: Boolean = false,
    clickAction: (() -> Unit)?
): CharSequence {
    return SpannableString(this).apply {
        val clickableSpan = object : ClickableSpan() {
            override fun onClick(widget: View) {
                clickAction?.invoke()
            }

            override fun updateDrawState(ds: TextPaint) {
                ds.color = color
                ds.isUnderlineText = isUnderlineText
            }
        }
        setSpan(clickableSpan, range.start, range.endInclusive, Spannable.SPAN_INCLUSIVE_EXCLUSIVE)
    }
}

/**
 * 將一段文字中指定range的文字添加style效果
 * @param range 要添加刪除線的文字的范圍
 */
fun CharSequence.toStyleSpan(style: Int = Typeface.BOLD, range: IntRange): CharSequence {
    return SpannableString(this).apply {
        setSpan(
            StyleSpan(style),
            range.start,
            range.endInclusive,
            Spannable.SPAN_INCLUSIVE_EXCLUSIVE
        )
    }
}

/**
 * 將一段文字中指定range的文字添加自定義效果
 * @param range 要添加刪除線的文字的范圍
 */
fun CharSequence.toCustomTypeFaceSpan(typeface: Typeface, range: IntRange): CharSequence {
    return SpannableString(this).apply {
        setSpan(
            CustomTypefaceSpan(typeface),
            range.start,
            range.endInclusive,
            Spannable.SPAN_INCLUSIVE_EXCLUSIVE
        )
    }
}


/**
 * 將一段文字中指定range的文字添加自定義效果,可以設(shè)置對齊方式,可以設(shè)置margin
 * @param range
 */
fun CharSequence.toImageSpan(
    imageRes: Int,
    range: IntRange,
    verticalAlignment: Int = 0,  //默認(rèn)底部  4是垂直居中
    maginLeft: Int = 0,
    marginRight: Int = 0,
    width: Int = 0,
    height: Int = 0
): CharSequence {
    return SpannableString(this).apply {
        setSpan(
            MiddleIMarginImageSpan(
                CommUtils.getDrawable(imageRes)
                    .apply {
                        setBounds(0, 0, if (width == 0) getIntrinsicWidth() else width, if (height == 0) getIntrinsicHeight() else height)
                    },
                verticalAlignment,
                maginLeft,
                marginRight
            ),
            range.start,
            range.endInclusive,
            Spannable.SPAN_INCLUSIVE_EXCLUSIVE
        )
    }
}

擴展方法的使用

  mBinding.tvTextSpan1.text = "演示一下appendXX方法的用法\n"
        mBinding.tvTextSpan1.appendSizeSpan("變大變大", 1.5f)
            .appendColorSpan("我要變色", color = Color.parseColor("#f0aafc"))
            .appendBackgroundColorSpan("我是有底色的", color = Color.parseColor("#cacee0"))
            .appendStrikeThrougthSpan("添加刪除線哦哦哦哦")
            .appendClickSpan("來點我一下試試啊", isUnderlineText = true, clickAction = {
                toast("哎呀,您點到我了呢,嘿嘿")
            })
            .appendImageSpan(R.mipmap.ic_launcher)  //默認(rèn)的大圖什么都不加 默認(rèn)在底部對齊
            .appendStyleSpan("我是粗體的") //可以是默認(rèn)粗體 斜體等
            .appendImageSpan(R.mipmap.ic_launcher_round, 4, width = dp2px(35f), height = dp2px(35f))//4是居中的,限制Drawable
            .appendCustomTypeFaceSpan("Xiao mi Hua wei", TypefaceUtil.getSFFlower(mActivity))  //自定義字體文件
            //默認(rèn)底部對齊,加左右margin
            .appendImageSpan(R.mipmap.iv_me_red_packet, maginLeft = dp2px(10f), marginRight = dp2px(10f))
            //添加刪除線
            .appendStrikeThrougthSpan("添加刪除線哦哦哦哦添加刪除線哦哦哦哦")

效果:

2.3 kotlin DSL方式

如果是使用Kotlin的語言開發(fā),那么還有更簡單的DSL封裝方式:

第一層的DSL接口

interface DslSpannableStringBuilder {
    //增加一段文字
    fun addText(text: String, method: (DslSpanBuilder.() -> Unit)? = null)

    //添加一個圖標(biāo)
    fun addImage(imageRes: Int, verticalAlignment: Int = 0, maginLeft: Int = 0, marginRight: Int = 0, width: Int = 0, height: Int = 0)
}

第一層的DSL接口實現(xiàn)

class DslSpannableStringBuilderImpl : DslSpannableStringBuilder {
    private val builder = SpannableStringBuilder()
    //添加文本
    override fun addText(text: String, method: (DslSpanBuilder.() -> Unit)?) {
        val spanBuilder = DslSpanBuilderImpl()
        method?.let { spanBuilder.it() }

        var charSeq: CharSequence = text

        spanBuilder.apply {
            if (issetColor) {
                charSeq = charSeq.toColorSpan(0..text.length, textColor)
            }
            if (issetBackground) {
                charSeq = charSeq.toBackgroundColorSpan(0..text.length, textBackgroundColor)
            }
            if (issetScale) {
                charSeq = charSeq.toSizeSpan(0..text.length, scaleSize)
            }
            if (isonClick) {
                charSeq = charSeq.toClickSpan(0..text.length, textColor, isuseUnderLine, onClick)
            }
            if (issetTypeface) {
                charSeq = charSeq.toCustomTypeFaceSpan(typefaces, 0..text.length)
            }
            if (issetStrikethrough) {
                charSeq = charSeq.toStrikeThrougthSpan(0..text.length)
            }

            builder.append(charSeq)
        }
    }

    //添加圖標(biāo)
    override fun addImage(imageRes: Int, verticalAlignment: Int, maginLeft: Int, marginRight: Int, width: Int, height: Int) {
        var charSeq: CharSequence = "1"
        charSeq = charSeq.toImageSpan(imageRes, 0..1, verticalAlignment, maginLeft, marginRight, width, height)
        builder.append(charSeq)
    }

    fun build(): SpannableStringBuilder {
        return builder
    }
}

第二層Text的DSL接口

interface DslSpanBuilder {
    //設(shè)置文字顏色
    fun setColor(color: Int = 0)

    //設(shè)置點擊事件
    fun setClick(useUnderLine: Boolean = true, onClick: (() -> Unit)?)

    //設(shè)置縮放大小
    fun setScale(scale: Float = 1.0f)

    //設(shè)置自定義字體
    fun setTypeface(typeface: Typeface)

    //是否需要中劃線
    fun setStrikethrough(isStrikethrough: Boolean = false)

    //設(shè)置背景
    fun setBackground(color: Int = Color.TRANSPARENT)
}

第二層Text的DSL接口實現(xiàn)

class DslSpanBuilderImpl : DslSpanBuilder {
    var issetColor = false
    var textColor: Int = Color.BLACK

    var isonClick = false
    var isuseUnderLine = false
    var onClick: (() -> Unit)? = null

    var issetScale = false
    var scaleSize = 1.0f

    var issetTypeface = false
    var typefaces: Typeface = Typeface.DEFAULT

    var issetStrikethrough = false

    var issetBackground = false
    var textBackgroundColor = 0

    override fun setColor(color: Int) {
        issetColor = true
        textColor = color
    }

    override fun setClick(useUnderLine: Boolean, onClick: (() -> Unit)?) {
        isonClick = true
        isuseUnderLine = useUnderLine
        this.onClick = onClick
    }

    override fun setScale(scale: Float) {
        issetScale = true
        scaleSize = scale
    }

    override fun setTypeface(typeface: Typeface) {
        issetTypeface = true
        typefaces = typeface
    }

    override fun setStrikethrough(isStrikethrough: Boolean) {
        issetStrikethrough = isStrikethrough
    }

    override fun setBackground(color: Int) {
        issetBackground = true
        textBackgroundColor = color
    }
}

創(chuàng)建TextVuew的擴展入口

//為 TextView 創(chuàng)建擴展函數(shù),其參數(shù)為接口的擴展函數(shù)
fun TextView.buildSpannableString(init: DslSpannableStringBuilder.() -> Unit) {
    //具體實現(xiàn)類
    val spanStringBuilderImpl = DslSpannableStringBuilderImpl()
    spanStringBuilderImpl.init()
    movementMethod = LinkMovementMethod.getInstance()
    //通過實現(xiàn)類返回SpannableStringBuilder
    text = spanStringBuilderImpl.build()
}

使用:

    mBinding.tvTextSpan4.buildSpannableString {
            addText("我已詳細閱讀并同意")
            addText("測試紅色的文字顏色") {
                setColor(Color.RED)
            }
            addText("測試白色文字加上灰色背景") {
                setColor(Color.WHITE)
                setBackground(Color.GRAY)
            }
            addText("測試文本變大了") {
                setColor(Color.DKGRAY)
                setScale(1.5f)
            }
            addImage(R.mipmap.ic_launcher)
            addText("測試可以點擊的文本") {
                setClick(true) {
                    toast("點擊文本拉啦啦")
                }
            }
            addImage(R.mipmap.ic_launcher_round, 5, dp2px(10f), dp2px(10f), dp2px(35f), dp2px(35f))
            addText("Test Custom Typeface Font is't Success?") {
                setTypeface(TypefaceUtil.getSFFlower(mActivity))
            }
            addText("測試中劃線是否生效") {
                setStrikethrough(true)
            }
        }

效果:

總結(jié)

如果是順序固定,效果復(fù)雜,那么可以用Span的方式。

如果順序不固定(如國際化)那么可以使用Html的方式。

總的來說,兩種方式都不算太難,都是些固定的代碼。如果需求可以看源碼。

到此這篇關(guān)于Android富文本實現(xiàn)的幾種方式的文章就介紹到這了,更多相關(guān)Android富文本實現(xiàn)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論