ใช้แบบอักษรที่กำหนดเองใน Android


111

ฉันต้องการใช้แบบอักษรที่กำหนดเองสำหรับแอปพลิเคชัน Android ที่ฉันกำลังสร้างขึ้น
ฉันสามารถเปลี่ยนแบบอักษรของแต่ละออบเจ็กต์ทีละรายการจาก Code ได้ แต่ฉันมีหลายร้อยรายการ

ดังนั้น,

  • มีวิธีดำเนินการจาก XML หรือไม่? [การตั้งค่าแบบอักษรที่กำหนดเอง]
  • มีวิธีดำเนินการจากโค้ดในที่เดียวหรือไม่เพื่อบอกว่าแอปพลิเคชันทั้งหมดและส่วนประกอบทั้งหมดควรใช้แบบอักษรที่กำหนดเองแทนค่าเริ่มต้น

stackoverflow.com/questions/5541058/…ดูที่โพสต์นี้ฉันโพสต์รหัสทั้งหมดที่นี่เกี่ยวกับปัญหานี้
Manish Singla

คุณสามารถใช้ตัวแปรคงที่ในกิจกรรมหลักของคุณเพื่อเก็บข้อมูลอ้างอิงไปยังฟอนต์ที่ฝังไว้ นั่นจะทำให้มีชุดฟอนต์ถาวรหนึ่งชุดที่ GC จะไม่หยิบขึ้นมา
Jacksonkr

วิธีการโรงงาน หรือวิธีการที่ใช้มุมมองของคุณและตั้งค่าแบบอักษรและแบบอักษรทั้งหมด
mtmurdock

ไลบรารีการสนับสนุนใหม่ 26 ตอนนี้ช่วยให้คุณใช้ฟอนต์ใน XML ได้แล้ว นี่คือวิธีการใช้Fonts ใน XML
Vinicius Silva

คำตอบ:


80

มีวิธีดำเนินการจาก XML หรือไม่?

ไม่ล่ะขอบคุณ. คุณสามารถระบุแบบอักษรในตัวผ่าน XML เท่านั้น

มีวิธีดำเนินการจากโค้ดในที่เดียวหรือไม่เพื่อบอกว่าแอปพลิเคชันทั้งหมดและส่วนประกอบทั้งหมดควรใช้แบบอักษรที่กำหนดเองแทนค่าเริ่มต้น

ไม่ใช่ว่าฉันรู้ตัว

มีตัวเลือกมากมายสำหรับสิ่งเหล่านี้ในปัจจุบัน:

  • ทรัพยากรแบบอักษรและแบ็คพอร์ตใน Android SDK หากคุณใช้ appcompat

  • ไลบรารีของบุคคลที่สามสำหรับผู้ที่ไม่ได้ใช้appcompatแม้ว่าจะไม่ใช่ทั้งหมดที่รองรับการกำหนดฟอนต์ในเลย์เอาต์รีซอร์ส


4
@Codevalley: ในหลาย ๆ ด้านคำตอบที่ดีกว่าคืออย่าเอะอะกับฟอนต์ที่กำหนดเอง พวกเขามักจะมีขนาดใหญ่ทำให้การดาวน์โหลดของคุณใหญ่ขึ้นและลดจำนวนผู้ที่จะดาวน์โหลดและใช้แอปของคุณต่อไป
CommonsWare

22
@CommonsWare: ฉันไม่จำเป็นต้องเห็นด้วย Typeface เป็นส่วนหนึ่งของ UI Styling เช่นเดียวกับที่คุณทำกับภาพพื้นหลังเป็นต้นและขนาดก็ไม่จำเป็นต้องใหญ่เสมอไป ตัวอย่างเช่นแบบอักษรในกรณีของฉันคือ 19.6KB :)
Codevalley

2
@Amit: ไม่มีการเปลี่ยนแปลงในประเด็นนี้ มีข้อมูลโค้ดมากมายที่ช่วยให้การใช้แบบอักษรกับ UI ของคุณใน Java ง่ายขึ้น แต่ไม่มีวิธีใดที่จะทำจาก XML ได้
CommonsWare

1
@Amit: "แต่นั่นหมายความว่าฉันจะต้องสร้างไฟล์. ttf หรือ. otf สำหรับฟอนต์ที่กำหนดเองหรือไม่" -- อืมไม่. คุณสามารถใช้แบบอักษร TTF / OTF ที่มีอยู่ได้แม้ว่าอาจไม่ได้ผลทั้งหมด ปัญหาของคำถามนี้คือวิธีใช้แบบอักษรเหล่านั้นกับทั้งแอปซึ่งยังไม่สามารถทำได้และนั่นคือสิ่งที่ฉันอ้างถึงในความคิดเห็นของฉัน
CommonsWare

1
คำตอบที่ได้รับการยอมรับนี้ล้าสมัยแล้วและจะเป็นการดีที่จะอัปเดตหรือลบออก
Sky Kelsey

109

ใช่มันเป็นไปได้

คุณต้องสร้างมุมมองแบบกำหนดเองซึ่งจะขยายมุมมองข้อความ

ในattrs.xmlในvaluesโฟลเดอร์:

<resources>
    <declare-styleable name="MyTextView">
        <attr name="first_name" format="string"/>
        <attr name="last_name" format="string"/>
        <attr name="ttf_name" format="string"/>
    </declare-styleable>
</resources>

ในmain.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:lht="http://schemas.android.com/apk/res/com.lht"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <TextView  android:layout_width="fill_parent" 
        android:layout_height="wrap_content"
        android:text="Hello"/>
    <com.lht.ui.MyTextView  
        android:id="@+id/MyTextView"
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content"
        android:text="Hello friends"
        lht:ttf_name="ITCBLKAD.TTF"
        />   
</LinearLayout>

ในMyTextView.java:

package com.lht.ui;

import android.content.Context;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.TextView;

public class MyTextView extends TextView {

    Context context;
    String ttfName;

    String TAG = getClass().getName();

    public MyTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;

        for (int i = 0; i < attrs.getAttributeCount(); i++) {
            Log.i(TAG, attrs.getAttributeName(i));
            /*
             * Read value of custom attributes
             */

            this.ttfName = attrs.getAttributeValue(
                    "http://schemas.android.com/apk/res/com.lht", "ttf_name");
            Log.i(TAG, "firstText " + firstText);
            // Log.i(TAG, "lastText "+ lastText);

            init();
        }

    }

    private void init() {
        Typeface font = Typeface.createFromAsset(context.getAssets(), ttfName);
        setTypeface(font);
    }

    @Override
    public void setTypeface(Typeface tf) {

        // TODO Auto-generated method stub
        super.setTypeface(tf);
    }

}

4
สิ่งนี้จะใช้ได้ แต่สำหรับTextView. จะต้องมีคลาสย่อยที่กำหนดเองสำหรับทุกคลาสวิดเจ็ตที่สืบทอดมาจากTextViewที่ที่ต้องการความสามารถเดียวกัน
CommonsWare

สวัสดี Manish ขอบคุณสำหรับการแก้ปัญหา แม้ว่าฉันจะประสบปัญหาในการใช้สิ่งนี้ ฉันได้รับ 'ไม่พบตัวระบุทรัพยากรสำหรับแอตทริบิวต์ ttf_name ในแพ็คเกจ com.lht.android' เรื่อย ๆ
Kshitij Aggarwal

17
วิธีนี้จะใช้งานได้ แต่ก่อน ICS จะจัดสรรหน่วยความจำสำหรับแบบอักษรสำหรับแต่ละมุมมองที่คุณสร้างอินสแตนซ์: code.google.com/p/android/issues/detail?id=9904วิธีแก้ไขปัญหานี้คือสร้างที่เข้าถึงได้ทั่วโลก แฮชแมปแบบคงที่ของฟอนต์ที่สร้างอินสแตนซ์ทั้งหมด: code.google.com/p/android/issues/detail?id=9904#c7
Ken Van Hoeylandt

ทำไมเราต้องวนซ้ำไม่ควรใช้กับการโทรเพียงครั้งเดียว?
Guillermo Tobar

1
@AlaksiejN ในกรณีที่คุณต้องตั้งค่าแบบอักษรที่แตกต่างกันไปยัง TextView ที่แตกต่างกัน ...
Nick

49

ฉันทำสิ่งนี้ด้วยวิธี "กำลังดุร้าย" ที่ไม่ต้องการการเปลี่ยนแปลงเลย์เอาต์ xml หรือกิจกรรม

ทดสอบบน Android เวอร์ชัน 2.1 ถึง 4.4 เรียกใช้สิ่งนี้เมื่อเริ่มต้นแอปในคลาสแอปพลิเคชันของคุณ:

private void setDefaultFont() {

    try {
        final Typeface bold = Typeface.createFromAsset(getAssets(), DEFAULT_BOLD_FONT_FILENAME);
        final Typeface italic = Typeface.createFromAsset(getAssets(), DEFAULT_ITALIC_FONT_FILENAME);
        final Typeface boldItalic = Typeface.createFromAsset(getAssets(), DEFAULT_BOLD_ITALIC_FONT_FILENAME);
        final Typeface regular = Typeface.createFromAsset(getAssets(),DEFAULT_NORMAL_FONT_FILENAME);

        Field DEFAULT = Typeface.class.getDeclaredField("DEFAULT");
        DEFAULT.setAccessible(true);
        DEFAULT.set(null, regular);

        Field DEFAULT_BOLD = Typeface.class.getDeclaredField("DEFAULT_BOLD");
        DEFAULT_BOLD.setAccessible(true);
        DEFAULT_BOLD.set(null, bold);

        Field sDefaults = Typeface.class.getDeclaredField("sDefaults");
        sDefaults.setAccessible(true);
        sDefaults.set(null, new Typeface[]{
                regular, bold, italic, boldItalic
        });

    } catch (NoSuchFieldException e) {
        logFontError(e);
    } catch (IllegalAccessException e) {
        logFontError(e);
    } catch (Throwable e) {
        //cannot crash app if there is a failure with overriding the default font!
        logFontError(e);
    }
}

สำหรับตัวอย่างที่สมบูรณ์ยิ่งขึ้นโปรดดูที่http://github.com/perchrh/FontOverrideExample


นี่คือทางออกที่ดีที่สุดสำหรับฉัน
Igor K

2
ค่าเริ่มต้นไม่ทำงานสำหรับฉัน ถ้าฉันใช้โมโนสเปซแล้วตั้งค่าcode<style name = "AppTheme" parent = "AppBaseTheme"> <item name = "android: typeface"> monospace </item> </style> codeก็ใช้ได้ แต่ไม่ใช่ตัวหนา ฉันเพิ่มรหัสนี้ในคลาสที่ขยายแอปพลิเคชัน นั่นคือสถานที่ที่ถูกต้องหรือไม่? @ P-chan
Christopher Rivera

2
@ChristopherRivera ใช่เพิ่มลงในคลาส Application ของแอปตรวจสอบให้แน่ใจว่าทำงานบน onCreate ดูที่grepcode.com/file/repository.grepcode.com/java/ext/…ฉันขอแนะนำให้คุณแทนที่ฟิลด์เพิ่มเติมสำหรับโมโนสเปซและฟิลด์ในโค้ดตัวอย่างของฉันด้านบน!
ต่อ Christian Henden

2
โอเคฉันเปลี่ยนสำหรับฟิลด์ SERIF และมันใช้งานได้ :)
Abdullah Umer

3
คำตอบนี้อ้างอิงคำตอบนี้และให้แนวทางที่สะอาดกว่า imho
adamdport

36

แม้ว่าฉันจะยกระดับคำตอบของ Manish ว่าเป็นวิธีที่เร็วและตรงเป้าหมายที่สุด แต่ฉันก็ยังได้เห็นวิธีแก้ปัญหาที่ไร้เดียงสาซึ่งจะวนซ้ำซ้ำ ๆ ผ่านลำดับชั้นของมุมมองและอัปเดตแบบอักษรขององค์ประกอบทั้งหมดในทางกลับกัน สิ่งนี้:

public static void applyFonts(final View v, Typeface fontToSet)
{
    try {
        if (v instanceof ViewGroup) {
            ViewGroup vg = (ViewGroup) v;
            for (int i = 0; i < vg.getChildCount(); i++) {
                View child = vg.getChildAt(i);
                applyFonts(child, fontToSet);
            }
        } else if (v instanceof TextView) {
            ((TextView)v).setTypeface(fontToSet);
        }
    } catch (Exception e) {
        e.printStackTrace();
        // ignore
    }
}

คุณจะต้องเรียกใช้ฟังก์ชันนี้ในมุมมองของคุณทั้งหลังจากขยายรูปแบบและonContentChanged()วิธีการของกิจกรรม


สวยมาก. ในขณะนั้นเป็นวิธีเดียวที่จะทำได้ภายในกรอบเวลาของโครงการ ทางลัดที่สะดวกหากจำเป็น (:
pospi

23

ฉันสามารถทำได้แบบรวมศูนย์นี่คือผลลัพธ์:

ใส่คำอธิบายภาพที่นี่

ฉันได้ติดตามActivityและขยายจากมันหากฉันต้องการแบบอักษรที่กำหนดเอง:

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.LayoutInflater.Factory;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;

public class CustomFontActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
    getLayoutInflater().setFactory(new Factory() {

        @Override
        public View onCreateView(String name, Context context,
                AttributeSet attrs) {
            View v = tryInflate(name, context, attrs);
            if (v instanceof TextView) {
                setTypeFace((TextView) v);
            }
            return v;
        }
    });
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
}

private View tryInflate(String name, Context context, AttributeSet attrs) {
    LayoutInflater li = LayoutInflater.from(context);
    View v = null;
    try {
        v = li.createView(name, null, attrs); 
    } catch (Exception e) {
        try {
            v = li.createView("android.widget." + name, null, attrs);
        } catch (Exception e1) {
        }
    }
    return v;
}

private void setTypeFace(TextView tv) {
    tv.setTypeface(FontUtils.getFonts(this, "MTCORSVA.TTF"));
}
}

แต่ถ้าฉันใช้กิจกรรมจากแพ็คเกจการสนับสนุนเช่นFragmentActivityฉันจะใช้สิ่งนี้Activity:

import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;

public class CustomFontFragmentActivity extends FragmentActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
}

// we can't setLayout Factory as its already set by FragmentActivity so we
// use this approach
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
    View v = super.onCreateView(name, context, attrs);
    if (v == null) {
        v = tryInflate(name, context, attrs);
        if (v instanceof TextView) {
            setTypeFace((TextView) v);
        }
    }
    return v;
}

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
public View onCreateView(View parent, String name, Context context,
        AttributeSet attrs) {
    View v = super.onCreateView(parent, name, context, attrs);
    if (v == null) {
        v = tryInflate(name, context, attrs);
        if (v instanceof TextView) {
            setTypeFace((TextView) v);
        }
    }
    return v;
}

private View tryInflate(String name, Context context, AttributeSet attrs) {
    LayoutInflater li = LayoutInflater.from(context);
    View v = null;
    try {
        v = li.createView(name, null, attrs);
    } catch (Exception e) {
        try {
            v = li.createView("android.widget." + name, null, attrs);
        } catch (Exception e1) {
        }
    }
    return v;
}

private void setTypeFace(TextView tv) {
    tv.setTypeface(FontUtils.getFonts(this, "MTCORSVA.TTF"));
}
}

ฉันยังไม่ได้ทดสอบโค้ดนี้กับFragments แต่หวังว่าจะใช้งานได้

ของฉันFontUtilsเป็นเรื่องง่ายซึ่งช่วยแก้ปัญหา pre-ICS ที่กล่าวถึงที่นี่https://code.google.com/p/android/issues/detail?id=9904 :

import java.util.HashMap;
import java.util.Map;

import android.content.Context;
import android.graphics.Typeface;

public class FontUtils {

private static Map<String, Typeface> TYPEFACE = new HashMap<String, Typeface>();

public static Typeface getFonts(Context context, String name) { 
    Typeface typeface = TYPEFACE.get(name);
    if (typeface == null) {
        typeface = Typeface.createFromAsset(context.getAssets(), "fonts/"
                + name);
        TYPEFACE.put(name, typeface);
    }
    return typeface;
}
}

2
สิ่งนี้ควรได้รับการโหวตมากกว่านี้ ใช้งานได้และมีภาพหน้าจอสำหรับการสาธิต
ericn

10

เฮ้ฉันต้องการแบบอักษรที่แตกต่างกัน 2 แบบในแอพของฉันสำหรับวิดเจ็ตที่แตกต่างกัน! ฉันใช้วิธีนี้:

ในคลาสแอปพลิเคชันของฉันฉันสร้างวิธีการแบบคงที่:

public static Typeface getTypeface(Context context, String typeface) {
    if (mFont == null) {
        mFont = Typeface.createFromAsset(context.getAssets(), typeface);
    }
    return mFont;
}

แบบอักษร String แสดงถึง xyz.ttf ในโฟลเดอร์ asset (ฉันสร้างคลาสค่าคงที่) ตอนนี้คุณสามารถใช้สิ่งนี้ได้ทุกที่ในแอพของคุณ:

mTextView = (TextView) findViewById(R.id.text_view);
mTextView.setTypeface(MyApplication.getTypeface(this, Constants.TYPEFACE_XY));

ปัญหาเดียวคือคุณต้องใช้สิ่งนี้สำหรับทุกวิดเจ็ตที่คุณต้องการใช้ฟอนต์! แต่ฉันคิดว่านี่เป็นวิธีที่ดีที่สุด


4

การใช้คำแนะนำของ pospiและการทำงานกับคุณสมบัติ 'แท็ก' เหมือนที่Richardทำฉันได้สร้างคลาสที่กำหนดเองที่โหลดแบบอักษรที่กำหนดเองของฉันและนำไปใช้กับมุมมองตามแท็กของพวกเขา

โดยพื้นฐานแล้วแทนที่จะตั้งค่า TypeFace ในแอตทริบิวต์ android: fontFamily คุณกำลังใช้ android: tag attritube และตั้งค่าเป็น enums ที่กำหนดไว้

public class Fonts {
    private AssetManager mngr;

    public Fonts(Context context) {
        mngr = context.getAssets();
    }
    private enum AssetTypefaces {
        RobotoLight,
        RobotoThin,
        RobotoCondensedBold,
        RobotoCondensedLight,
        RobotoCondensedRegular
    }

    private Typeface getTypeface(AssetTypefaces font) {
        Typeface tf = null;
        switch (font) {
            case RobotoLight:
                tf = Typeface.createFromAsset(mngr,"fonts/Roboto-Light.ttf");
                break;
            case RobotoThin:
                tf = Typeface.createFromAsset(mngr,"fonts/Roboto-Thin.ttf");
                break;
            case RobotoCondensedBold:
                tf = Typeface.createFromAsset(mngr,"fonts/RobotoCondensed-Bold.ttf");
                break;
            case RobotoCondensedLight:
                tf = Typeface.createFromAsset(mngr,"fonts/RobotoCondensed-Light.ttf");
                break;
            case RobotoCondensedRegular:
                tf = Typeface.createFromAsset(mngr,"fonts/RobotoCondensed-Regular.ttf");
                break;
            default:
                tf = Typeface.DEFAULT;
                break;
        }
        return tf;
    }
    public void setupLayoutTypefaces(View v) {
        try {
            if (v instanceof ViewGroup) {
                ViewGroup vg = (ViewGroup) v;
                for (int i = 0; i < vg.getChildCount(); i++) {
                    View child = vg.getChildAt(i);
                    setupLayoutTypefaces(child);
                }
            } else if (v instanceof TextView) {
                if (v.getTag().toString().equals(AssetTypefaces.RobotoLight.toString())){
                    ((TextView)v).setTypeface(getTypeface(AssetTypefaces.RobotoLight));
                }else if (v.getTag().toString().equals(AssetTypefaces.RobotoCondensedRegular.toString())) {
                    ((TextView)v).setTypeface(getTypeface(AssetTypefaces.RobotoCondensedRegular));
                }else if (v.getTag().toString().equals(AssetTypefaces.RobotoCondensedBold.toString())) {
                    ((TextView)v).setTypeface(getTypeface(AssetTypefaces.RobotoCondensedBold));
                }else if (v.getTag().toString().equals(AssetTypefaces.RobotoCondensedLight.toString())) {
                    ((TextView)v).setTypeface(getTypeface(AssetTypefaces.RobotoCondensedLight));
                }else if (v.getTag().toString().equals(AssetTypefaces.RobotoThin.toString())) {
                    ((TextView)v).setTypeface(getTypeface(AssetTypefaces.RobotoThin));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            // ignore
        }
    }
}

ในกิจกรรมหรือส่วนย่อยของคุณคุณเพียงแค่โทรหา

Fonts fonts = new Fonts(getActivity());
fonts.setupLayoutTypefaces(mainLayout);

4

ผมพบว่าวิธีการแก้ปัญหาที่ดีในบล็อกของลิซาเรย์ ด้วยการผูกข้อมูลใหม่คุณสามารถตั้งค่าแบบอักษรในไฟล์ XML ของคุณได้

@BindingAdapter({"bind:font"})
public static void setFont(TextView textView, String fontName){
    textView.setTypeface(Typeface.createFromAsset(textView.getContext().getAssets(), "fonts/" + fontName));
}

ใน XML:

<TextView
app:font="@{`Source-Sans-Pro-Regular.ttf`}"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>

คุณสามารถแนะนำตัวอย่างโดยใช้การผูกข้อมูลได้หรือไม่? ที่จะได้รับการชื่นชมมาก
Sri Krishna

3

ฉันคิดว่ามีวิธีที่ง่ายกว่านี้ในการทำ ชั้นเรียนต่อไปนี้จะตั้งค่าประเภทใบหน้าที่กำหนดเองสำหรับส่วนประกอบทั้งหมดของแอปพลิเคชันของคุณ (พร้อมการตั้งค่าต่อชั้นเรียน)

/**
 * Base Activity of our app hierarchy.
 * @author SNI
 */
public class BaseActivity extends Activity {

    private static final String FONT_LOG_CAT_TAG = "FONT";
    private static final boolean ENABLE_FONT_LOGGING = false;

    private Typeface helloTypeface;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        helloTypeface = Typeface.createFromAsset(getAssets(), "fonts/<your type face in assets/fonts folder>.ttf");
    }

    @Override
    public View onCreateView(String name, Context context, AttributeSet attrs) {
        View view = super.onCreateView(name, context, attrs);
        return setCustomTypeFaceIfNeeded(name, attrs, view);
    }

    @Override
    public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
        View view = super.onCreateView(parent, name, context, attrs);
        return setCustomTypeFaceIfNeeded(name, attrs, view);
    }

    protected View setCustomTypeFaceIfNeeded(String name, AttributeSet attrs, View view) {
        View result = null;
        if ("TextView".equals(name)) {
            result = new TextView(this, attrs);
            ((TextView) result).setTypeface(helloTypeface);
        }

        if ("EditText".equals(name)) {
            result = new EditText(this, attrs);
            ((EditText) result).setTypeface(helloTypeface);
        }

        if ("Button".equals(name)) {
            result = new Button(this, attrs);
            ((Button) result).setTypeface(helloTypeface);
        }

        if (result == null) {
            return view;
        } else {
            if (ENABLE_FONT_LOGGING) {
                Log.v(FONT_LOG_CAT_TAG, "A type face was set on " + result.getId());
            }
            return result;
        }
    }

}

2

การใช้งานเริ่มต้นของ LayoutInflater ไม่สนับสนุนการระบุแบบอักษรจาก xml อย่างไรก็ตามฉันได้เห็นมันทำใน xml โดยจัดเตรียมโรงงานที่กำหนดเองสำหรับ LayoutInflater ที่จะแยกวิเคราะห์คุณสมบัติดังกล่าวจากแท็ก xml

โครงสร้างพื้นฐานก็อยากได้แบบนี้

public class TypefaceInflaterFactory implements LayoutInflater.Factory {

    @Override
    public View onCreateView(String name, Context context, AttributeSet attrs) {
        // CUSTOM CODE TO CREATE VIEW WITH TYPEFACE HERE
        // RETURNING NULL HERE WILL TELL THE INFLATER TO USE THE
        // DEFAULT MECHANISMS FOR INFLATING THE VIEW FROM THE XML
    }

}

public class BaseActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        LayoutInflater.from(this).setFactory(new TypefaceInflaterFactory());
    }
}

บทความนี้ให้คำอธิบายเชิงลึกเพิ่มเติมเกี่ยวกับกลไกเหล่านี้และวิธีที่ผู้เขียนพยายามให้การสนับสนุนเค้าโครง xml สำหรับแบบอักษรด้วยวิธีนี้ รหัสสำหรับการดำเนินงานของผู้เขียนที่สามารถพบได้ที่นี่


1

การตั้งค่าแบบอักษรที่กำหนดเองเป็น ProgressDialog / AlertDialog ปกติ:

font=Typeface.createFromAsset(getAssets(),"DroidSans.ttf");

ProgressDialog dialog = ProgressDialog.show(this, "titleText", "messageText", true);
((TextView)dialog.findViewById(Resources.getSystem().getIdentifier("message", "id", "android"))).setTypeface(font);
((TextView)dialog.findViewById(Resources.getSystem().getIdentifier("alertTitle", "id", "android"))).setTypeface(font);

1

ใช่เป็นไปได้โดยการแทนที่แบบอักษรเริ่มต้น ฉันติดตามโซลูชันนี้และมันก็ใช้งานได้ดีสำหรับ TextViews และข้อความ ActionBar ทั้งหมดด้วยการเปลี่ยนแปลงเพียงครั้งเดียว

public class MyApp extends Application {

  @Override
  public void onCreate() {
    TypefaceUtil.overrideFont(getApplicationContext(), "SERIF", "fonts/Roboto-Regular.ttf"); // font from assets: "assets/fonts/Roboto-Regular.ttf
  }
}

styles.xml

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/pantone</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
    <item name="android:windowTranslucentStatus" tools:targetApi="kitkat">true</item>
    <item name="android:windowDisablePreview">true</item>
    <item name="android:typeface">serif</item>
</style>

แทนที่จะเป็น themes.xml ตามที่กล่าวไว้ในลิงก์ด้านบนฉันพูดถึงฟอนต์เริ่มต้นที่จะแทนที่ใน styles.xml ของฉันในแท็กธีมแอปเริ่มต้นของฉัน แบบอักษรเริ่มต้นที่สามารถเขียนทับได้คือ serif, sans, monospace และ normal

TypefaceUtil.java

public class TypefaceUtil {

    /**
     * Using reflection to override default typeface
     * NOTICE: DO NOT FORGET TO SET TYPEFACE FOR APP THEME AS DEFAULT TYPEFACE WHICH WILL BE OVERRIDDEN
     * @param context to work with assets
     * @param defaultFontNameToOverride for example "monospace"
     * @param customFontFileNameInAssets file name of the font from assets
     */
    public static void overrideFont(Context context, String defaultFontNameToOverride, String customFontFileNameInAssets) {
        try {
            final Typeface customFontTypeface = Typeface.createFromAsset(context.getAssets(), customFontFileNameInAssets);

            final Field defaultFontTypefaceField = Typeface.class.getDeclaredField(defaultFontNameToOverride);
            defaultFontTypefaceField.setAccessible(true);
            defaultFontTypefaceField.set(null, customFontTypeface);
        } catch (Exception e) {
            Log.e("Can not set custom font " + customFontFileNameInAssets + " instead of " + defaultFontNameToOverride);
        }
    }
}

ในตอนแรกฉันไม่รู้ว่าแบบอักษรที่จะเขียนทับนั้นได้รับการแก้ไขและตั้งค่าที่กำหนดไว้แล้ว แต่ในที่สุดมันก็ช่วยให้ฉันเข้าใจว่า Android จัดการกับฟอนต์และฟอนต์และค่าดีฟอลต์อย่างไรซึ่งเป็นประเด็นที่แตกต่างกัน


0

ฉันไม่รู้ว่ามันเปลี่ยนทั้งแอพหรือเปล่า แต่ฉันได้จัดการเปลี่ยนส่วนประกอบบางอย่างที่ไม่สามารถเปลี่ยนแปลงได้โดยทำสิ่งนี้:

Typeface tf = Typeface.createFromAsset(getAssets(), "fonts/Lucida Sans Unicode.ttf");
Typeface.class.getField("DEFAULT").setAccessible(true);
Typeface.class.getField("DEFAULT_BOLD").setAccessible(true);
Typeface.class.getField("DEFAULT").set(null, tf);
Typeface.class.getField("DEFAULT_BOLD").set(null, tf);

2
น่าเสียดายที่สิ่งนี้ใช้ไม่ได้อีกต่อไป: java.lang.IllegalAccessException: ฟิลด์ถูกทำเครื่องหมาย 'final' ที่ java.lang.reflect.Field.setField (Native Method) ที่ java.lang.reflect.Field.set (Field.java: 556)
BoD

0

ฉันชอบคำแนะนำของ pospi ทำไมไม่ลองใช้คุณสมบัติ "แท็ก" ของมุมมอง (ซึ่งคุณสามารถระบุได้ใน XML - "android: tag") เพื่อระบุสไตล์เพิ่มเติมที่คุณไม่สามารถทำได้ใน XML ฉันชอบ JSON ดังนั้นฉันจึงใช้สตริง JSON เพื่อระบุชุดคีย์ / ค่า ชั้นเรียนนี้ได้ผล - เพียงแค่โทรหาStyle.setContentView(this, [resource id])คุณในกิจกรรมของคุณ

public class Style {

  /**
   * Style a single view.
   */
  public static void apply(View v) {
    if (v.getTag() != null) {
      try {
        JSONObject json = new JSONObject((String)v.getTag());
        if (json.has("typeface") && v instanceof TextView) {
          ((TextView)v).setTypeface(Typeface.createFromAsset(v.getContext().getAssets(),
                                                             json.getString("typeface")));
        }
      }
      catch (JSONException e) {
        // Some views have a tag without it being explicitly set!
      }
    }
  }

  /**
   * Style the passed view hierarchy.
   */
  public static View applyTree(View v) {
    apply(v);
    if (v instanceof ViewGroup) {
      ViewGroup g = (ViewGroup)v;
      for (int i = 0; i < g.getChildCount(); i++) {
        applyTree(g.getChildAt(i));
      }
    }
    return v;
  }

  /**
   * Inflate, style, and set the content view for the passed activity.
   */
  public static void setContentView(Activity activity, int resource) {
    activity.setContentView(applyTree(activity.getLayoutInflater().inflate(resource, null)));
  }
}

เห็นได้ชัดว่าคุณต้องการจัดการมากกว่าแบบอักษรเพื่อให้ใช้ JSON อย่างคุ้มค่า

ประโยชน์ของคุณสมบัติ "แท็ก" คือคุณสามารถตั้งค่าเป็นรูปแบบพื้นฐานที่คุณใช้เป็นธีมและทำให้มันใช้กับมุมมองทั้งหมดของคุณโดยอัตโนมัติ แก้ไข: การทำเช่นนี้ส่งผลให้เกิดความผิดพลาดในช่วงเงินเฟ้อบน Android 4.0.3 คุณยังสามารถใช้สไตล์และนำไปใช้กับมุมมองข้อความทีละรายการได้

สิ่งหนึ่งที่คุณจะเห็นในโค้ด - มุมมองบางอย่างมีแท็กโดยไม่ได้ตั้งค่าไว้อย่างชัดเจน - แปลกประหลาดมากคือสตริง "Αποκοπή" ซึ่งเป็น "ตัด" ในภาษากรีกตาม Google Translate! ห่าอะไร ... ?


0

คำตอบของ @ majinboo ได้รับการแก้ไขสำหรับประสิทธิภาพและการจัดการหน่วยความจำ มากกว่าหนึ่งฟอนต์ที่ต้องการกิจกรรมที่เกี่ยวข้องสามารถใช้คลาสฟอนต์นี้ได้โดยให้คอนสตรัคเตอร์เป็นพารามิเตอร์

@Override
public void onCreate(Bundle savedInstanceState)
{
    Font font = new Font(this);
}

คลาสแบบอักษรที่แก้ไขมีดังต่อไปนี้:

public class Fonts
{
    private HashMap<AssetTypefaces, Typeface> hashMapFonts;

    private enum AssetTypefaces
    {
        RobotoLight,
        RobotoThin,
        RobotoCondensedBold,
        RobotoCondensedLight,
        RobotoCondensedRegular
    }

    public Fonts(Context context)
    {
        AssetManager mngr = context.getAssets();

        hashMapFonts = new HashMap<AssetTypefaces, Typeface>();
        hashMapFonts.put(AssetTypefaces.RobotoLight, Typeface.createFromAsset(mngr, "fonts/Roboto-Light.ttf"));
        hashMapFonts.put(AssetTypefaces.RobotoThin, Typeface.createFromAsset(mngr, "fonts/Roboto-Thin.ttf"));
        hashMapFonts.put(AssetTypefaces.RobotoCondensedBold, Typeface.createFromAsset(mngr, "fonts/RobotoCondensed-Bold.ttf"));
        hashMapFonts.put(AssetTypefaces.RobotoCondensedLight, Typeface.createFromAsset(mngr, "fonts/RobotoCondensed-Light.ttf"));
        hashMapFonts.put(AssetTypefaces.RobotoCondensedRegular, Typeface.createFromAsset(mngr, "fonts/RobotoCondensed-Regular.ttf"));
    }

    private Typeface getTypeface(String fontName)
    {
        try
        {
            AssetTypefaces typeface = AssetTypefaces.valueOf(fontName);
            return hashMapFonts.get(typeface);
        }
        catch (IllegalArgumentException e)
        {
            // e.printStackTrace();
            return Typeface.DEFAULT;
        }
    }

    public void setupLayoutTypefaces(View v)
    {
        try
        {
            if (v instanceof ViewGroup)
            {
                ViewGroup vg = (ViewGroup) v;
                for (int i = 0; i < vg.getChildCount(); i++)
                {
                    View child = vg.getChildAt(i);
                    setupLayoutTypefaces(child);
                }
            }
            else if (v instanceof TextView)
            {
                ((TextView) v).setTypeface(getTypeface(v.getTag().toString()));
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
            // ignore
        }
    }
}

0

ทำงานให้กับ Xamarin Android:

ประเภท:

public class FontsOverride
{
    public static void SetDefaultFont(Context context, string staticTypefaceFieldName, string fontAssetName)
    {
        Typeface regular = Typeface.CreateFromAsset(context.Assets, fontAssetName);
        ReplaceFont(staticTypefaceFieldName, regular);
    }

    protected static void ReplaceFont(string staticTypefaceFieldName, Typeface newTypeface)
    {
        try
        {
            Field staticField = ((Java.Lang.Object)(newTypeface)).Class.GetDeclaredField(staticTypefaceFieldName);
            staticField.Accessible = true;
            staticField.Set(null, newTypeface);
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
        }
    }
}

การใช้งานแอปพลิเคชัน:

namespace SomeAndroidApplication
{
    [Application]
    public class App : Application
    {
        public App()
        {

        }

        public App(IntPtr handle, JniHandleOwnership transfer)
            : base(handle, transfer)
        {

        }

        public override void OnCreate()
        {
            base.OnCreate();

            FontsOverride.SetDefaultFont(this, "MONOSPACE", "fonts/Roboto-Light.ttf");
        }
    }
}

สไตล์:

<style name="Theme.Storehouse" parent="Theme.Sherlock">
    <item name="android:typeface">monospace</item>
</style>

0

ดูเหมือนว่าการใช้ฟอนต์ที่กำหนดเองนั้นทำได้ง่ายด้วย Android O โดยทั่วไปแล้วคุณสามารถใช้ xml เพื่อบรรลุเป้าหมายนี้ได้ ฉันได้แนบลิงก์ไปยังเอกสารอย่างเป็นทางการของ Android เพื่อใช้อ้างอิงและหวังว่าสิ่งนี้จะช่วยให้ผู้ที่ยังต้องการโซลูชันนี้ การทำงานกับแบบอักษรที่กำหนดเองใน Android


0

อาจเป็นประโยชน์ที่จะทราบว่าตั้งแต่ Android 8.0 (API ระดับ 26) คุณสามารถใช้แบบอักษรที่กำหนดเองใน XMLได้

พูดง่ายๆคือคุณสามารถทำได้ด้วยวิธีต่อไปนี้

  1. res/fontใส่ตัวอักษรในโฟลเดอร์

  2. ใช้ในแอตทริบิวต์ของวิดเจ็ต

<Button android:fontFamily="@font/myfont"/>

หรือใส่เข้าไป res/values/styles.xml

<style name="MyButton" parent="android:Widget.Button">
    <item name="android:fontFamily">@font/myfont</item>
</style>

และใช้เป็นสไตล์

<Button style="@style/MyButton"/>

0

ใช้แอตทริบิวต์ "fontPath" โดยตรงในไฟล์ xml

สำหรับใช้ใน style.xml

<item name = "fontPath"> แบบอักษร / ProximaNovaSemibold.ttf </item>

สำหรับใช้ในไฟล์เลย์เอาต์โดยตรง

fontPath = "แบบอักษร / ProximaNovaBold.ttf"

(หมายเหตุ: ไม่จำเป็นต้องใช้แอตทริบิวต์ app / android ในคำนำหน้า)


-6

เป็นไปได้อย่างแน่นอน ทำได้หลายวิธี วิธีที่เร็วที่สุดสร้างเงื่อนไขด้วยวิธี try - catch .. ลองใช้เงื่อนไขรูปแบบฟอนต์ของคุณจับข้อผิดพลาดและกำหนดรูปแบบฟอนต์อื่น ๆ


7
คุณสามารถสาธิตเพื่อพิสูจน์ว่าคุณตอบได้หรือไม่?
มาร์ค
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.