Làm cách nào để sử dụng TypefaceSpan hoặc StyleSpan với một Typeface tùy chỉnh?


Câu trả lời:


149

Chà, tôi không thể tìm ra cách làm điều đó với các lớp học có sẵn nên tôi đã tự mình mở rộng lớp học TypefaceSpanbây giờ nó phù hợp với tôi. Đây là những gì tôi đã làm:

package de.myproject.text.style;

import android.graphics.Paint;
import android.graphics.Typeface;
import android.text.TextPaint;
import android.text.style.TypefaceSpan;

public class CustomTypefaceSpan extends TypefaceSpan {
    private final Typeface newType;

    public CustomTypefaceSpan(String family, Typeface type) {
        super(family);
        newType = type;
    }

    @Override
    public void updateDrawState(TextPaint ds) {
        applyCustomTypeFace(ds, newType);
    }

    @Override
    public void updateMeasureState(TextPaint paint) {
        applyCustomTypeFace(paint, newType);
    }

    private static void applyCustomTypeFace(Paint paint, Typeface tf) {
        int oldStyle;
        Typeface old = paint.getTypeface();
        if (old == null) {
            oldStyle = 0;
        } else {
            oldStyle = old.getStyle();
        }

        int fake = oldStyle & ~tf.getStyle();
        if ((fake & Typeface.BOLD) != 0) {
            paint.setFakeBoldText(true);
        }

        if ((fake & Typeface.ITALIC) != 0) {
            paint.setTextSkewX(-0.25f);
        }

        paint.setTypeface(tf);
    }
}

1
@notme tôi nên chuyển cái gì cho họ biến chuỗi trong hàm tạo này CustomTypefaceSpan (Họ chuỗi, kiểu Kiểu chữ) {} ???
KJEjava48

102

Trong khi notme về cơ bản có ý tưởng đúng, giải pháp được đưa ra hơi khó hiểu vì "gia đình" trở nên thừa. Nó cũng hơi không chính xác vì TypefaceSpan là một trong những nhịp đặc biệt mà Android biết đến và mong đợi một số hành vi nhất định đối với giao diện ParcelableSpan (mà lớp con của notme không đúng, cũng như không thể thực hiện).

Một giải pháp đơn giản và chính xác hơn sẽ là:

public class CustomTypefaceSpan extends MetricAffectingSpan
{
    private final Typeface typeface;

    public CustomTypefaceSpan(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;
        final 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);
    }
}

1
+1 Cảm ơn bạn! Và đây là một ví dụ sử dụng thích hợp.
caw

@MarcoW và @Benjamin .... Benjamin nói rằng bạn không thể sử dụng TypefaceSpannhưng sau đó Marco đưa ra một ví dụ hoạt động chỉ sử dụng nó. Cái nào là đúng? Benjamin bạn đã thử nghiệm ví dụ của mình chưa?
Jayson Minard

@JaysonMinard Tôi nghĩ @MarcoW đã nhận xét về câu trả lời sai. Tôi cho rằng anh ấy định nhận xét về câu trả lời của @ notme, vì đó là lớp học mà anh ấy đã sử dụng. Nói rõ hơn, tôi không nói rằng bạn không thể sub-class TypefaceSpan. Tôi chỉ thực sự khuyên bạn không nên . Làm như vậy vi phạm nguyên tắc thay thế của Liscov và là một hành vi cực kỳ xấu. Bạn đã phân loại phụ một lớp chỉ định phông chữ thông qua familytên có thể phân loại được; sau đó bạn đã ghi đè hoàn toàn hành vi đó và làm cho familytham số trở nên khó hiểu và vô nghĩa, và Spancũng không phải là giá trị.
Benjamin Dobell

Tôi thực sự không biết gì về chủ đề này @BenjaminDobell ... chỉ cố gắng tìm ra điều gì đang xảy ra trong câu hỏi khác này về cơ bản đã chuyển câu trả lời ở đây cho Kotlin và không làm cho nó hoạt động. Sự khác biệt Kotlin là không phù hợp bởi vì nó là khái niệm tương tự, và sẽ là các mã byte tương tự như Java, cái gì khác phải sai với: stackoverflow.com/questions/35039686/...
Jayson Minard

@JaysonMinard Cũng xem xét câu trả lời này đã có 42 phiếu ủng hộ, hỏi tôi có thử nghiệm ví dụ của mình hơi kỳ dị không. Tuy nhiên, có, tôi đã thử nghiệm mã này. Đây là mã chính xác mà tôi đã sử dụng trong nhiều ứng dụng Android đã xuất bản.
Benjamin Dobell,

4

Trên Android P, bạn có thể sử dụng cùng lớp TypefaceSpan mà bạn biết, như được hiển thị ở đây .

Nhưng trên các phiên bản cũ hơn, bạn có thể sử dụng những gì chúng đã hiển thị sau trong video mà tôi đã viết ở đây .


0

Nếu ai đó quan tâm, đây là phiên bản C # Xamarin của mã Benjamin:

using System;
using Android.Graphics;
using Android.Text;
using Android.Text.Style;

namespace Utils
{
    //https://stackoverflow.com/a/17961854/1996780
    /// <summary>A text span which applies <see cref="Android.Graphics.Typeface"/> on text</summary>
    internal class CustomFontSpan : MetricAffectingSpan
    {
        /// <summary>The typeface to apply</summary>
        public Typeface Typeface { get; }

        /// <summary>CTor - creates a new instance of the <see cref="CustomFontSpan"/> class</summary>
        /// <param name="typeface">Typeface to apply</param>
        /// <exception cref="ArgumentNullException"><paramref name="typeface"/> is null</exception>
        public CustomFontSpan(Typeface typeface) =>
            Typeface = typeface ?? throw new ArgumentNullException(nameof(typeface));


        public override void UpdateDrawState(TextPaint drawState) => Apply(drawState);

        public override void UpdateMeasureState(TextPaint paint) => Apply(paint);

        /// <summary>Applies <see cref="Typeface"/></summary>
        /// <param name="paint"><see cref="Paint"/> to apply <see cref="Typeface"/> on</param>
        private void Apply(Paint paint)
        {
            Typeface oldTypeface = paint.Typeface;
            var oldStyle = oldTypeface != null ? oldTypeface.Style : 0;
            var fakeStyle = oldStyle & Typeface.Style;

            if (fakeStyle.HasFlag(TypefaceStyle.Bold))
                paint.FakeBoldText = true;

            if (fakeStyle.HasFlag(TypefaceStyle.Italic))
                paint.TextSkewX = -0.25f;

            paint.SetTypeface(Typeface);
        }
    }
}

Và cách sử dụng: (trong hoạt động OnCreate)

var txwLogo = FindViewById<TextView>(Resource.Id.logo);
var font = Resources.GetFont(Resource.Font.myFont);

var wordtoSpan = new SpannableString(txwLogo.Text);
wordtoSpan.SetSpan(new CustomFontSpan(font), 6, 7, SpanTypes.InclusiveInclusive); //One caracter
txwLogo.TextFormatted = wordtoSpan;  

0

Kiểu chữ có thể mở rộng: Để đặt kiểu chữ phông chữ khác cho một số phần của văn bản, có thể sử dụng TypefaceSpan tùy chỉnh, như được hiển thị trong ví dụ sau:

spannable.setSpan( new CustomTypefaceSpan("SFUIText-Bold.otf",fontBold), 0,
firstWord.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
spannable.setSpan( new CustomTypefaceSpan("SFUIText-Regular.otf",fontRegular),
firstWord.length(), firstWord.length() + lastWord.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
text.setText( spannable );

Tuy nhiên, để làm cho đoạn mã trên hoạt động, lớp CustomTypefaceSpan phải có nguồn gốc từ lớp TypefaceSpan. Điều này có thể được thực hiện như sau:

public class CustomTypefaceSpan extends TypefaceSpan {
    private final Typeface newType;

    public CustomTypefaceSpan(String family, Typeface type) {
        super(family);
        newType = type;
    }

    @Override
    public void updateDrawState(TextPaint ds) {
        applyCustomTypeFace(ds, newType);
    }

    @Override
    public void updateMeasureState(TextPaint paint) {
        applyCustomTypeFace(paint, newType);
    }

    private static void applyCustomTypeFace(Paint paint, Typeface tf) {
        int oldStyle;
        Typeface old = paint.getTypeface();
        if (old == null) {
            oldStyle = 0;
        } else {
            oldStyle = old.getStyle();
        }
        int fake = oldStyle & ~tf.getStyle();
        if ((fake & Typeface.BOLD) != 0) {
            paint.setFakeBoldText(true);
        }
        if ((fake & Typeface.ITALIC) != 0) {
            paint.setTextSkewX(-0.25f);
        }
        paint.setTypeface(tf);
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.