太激動!Android修改全域性字型樣式,替換整個APP字型
最近一直在如何全域性修改app字型上困惑著,今天終於有了突破。我將蒐集的資料進行了整理,現在提供給大家。
前面為分析,建議直接翻到最後看【個人中心設定】。
首先將專案需要的字型資源放置在app下:
這是我自己找的字型檔案,分別代表粗體,方正準圓,華文彩雲,華文行楷,華文新宋,華文新魏,幼圓。
注意,字型ttf檔案只能用英文字母,中文會報找不到檔案異常。
我們先看看未設定之前的佈局樣式:
字型檔案準備好後,我們就可以按需設定自己想要的字型樣式。下面提供了3種設定方法,這3種方法都可以改變可以顯示文字的控制元件字型樣式,如TextView,Button,EditText,CheckBox,RadioButton等等:
方法1:自定義控制元件 FontTextView
重寫TextView,重寫其中的setTypeface方法,設定自己的字型樣式
package com.laundrylang.laundrylangpda.view;
public class FontTextView extends TextView{
public FontTextView(Context context) {
super(context);
}
public FontTextView(Context context, AttributeSet attrs) {
super (context, attrs);
}
public FontTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private Typeface createTypeface(Context context, String fontPath) {
return Typeface.createFromAsset(context.getAssets(), fontPath);
}
@Override
public void setTypeface(Typeface tf, int style) {
super.setTypeface(createTypeface(getContext(),"fonts/fzzy.ttf"), style);
}
}
佈局中直接用自定義的FontTextView類即可。
<com.laundrylang.laundrylangpda.view.FontTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="這是一個TextView"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="這是一個Button"/>
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="這是一個EditText"/>
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="這是一個CheckBox"/>
<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="這是一個RadioButton"/>
前後對照佈局檔案,因為這裡只重寫了TextView,所以只有TextView有變化:
這種設定方式的優缺點:
優點:使用簡單方便,不需要額外的工作。
缺點:只能替換一類控制元件的字型,如果需要替換Button或EditText控制元件的字型,需要以相同的方式自定義這些控制元件,這樣工作量大。
方法2:遞迴批量替換某個View及其子View的字型
Android中可顯示文字的控制元件都直接或間接繼承自TextView,批量替換字型的原理就是從指定的View節點開始遞迴遍歷所有子View,如果子View型別是TextView型別或其子型別則替換字型,如果子View是ViewGroup型別則重複這一過程。我抽取了一個工具類,程式碼如下:
public class TypefaceUtil {
/**
* <p>Replace the font of specified view and it's children</p>
* @param root The root view.
* @param fontPath font file path relative to 'assets' directory.
*/
public static void replaceFont(@NonNull View root, String fontPath) {
if (root == null || TextUtils.isEmpty(fontPath)) {
return;
}
if (root instanceof TextView) { // If view is TextView or it's subclass, replace it's font
TextView textView = (TextView)root;
int style = Typeface.NORMAL;
if (textView.getTypeface() != null) {
style = textView.getTypeface().getStyle();
}
textView.setTypeface(createTypeface(root.getContext(), fontPath), style);
} else if (root instanceof ViewGroup) { // If view is ViewGroup, apply this method on it's child views
ViewGroup viewGroup = (ViewGroup) root;
for (int i = 0; i < viewGroup.getChildCount(); ++i) {
replaceFont(viewGroup.getChildAt(i), fontPath);
}
}
}
/**
* <p>Replace the font of specified view and it's children</p>
* @param context The view corresponding to the activity.
* @param fontPath font file path relative to 'assets' directory.
*/
public static void replaceFont(@NonNull Activity context, String fontPath) {
replaceFont(getRootView(context),fontPath);
}
/*
* Create a Typeface instance with your font file
*/
public static Typeface createTypeface(Context context, String fontPath) {
return Typeface.createFromAsset(context.getAssets(), fontPath);
}
/**
* 從Activity 獲取 rootView 根節點
* @param context
* @return 當前activity佈局的根節點
*/
public static View getRootView(Activity context)
{
return ((ViewGroup)context.findViewById(android.R.id.content)).getChildAt(0);
}
}
程式碼中呼叫,這裡建議最好放在BaseActivity中,因為this只能代表當前頁面,放在BaseActivity中讓所有Activity繼承自BaseActivity可以全域性的改變字型樣式:
TypefaceUtil.replaceFont(this, "fonts/fzzy.ttf");
前後對照佈局檔案,可以發現所有的控制元件字型都變了:
這種設定方式的優缺點:
優點:不需要修改XML佈局檔案,不需要重寫控制元件,可以批量替換所有繼承自TextView的控制元件的字型,適合需要批量替換字型的場合,如程式的預設字型。
缺點:如果要替換整個App的所有字型,需要在每個有介面的地方批量替換一次,頁面多了還是有些工作量的,不過可以在Activity和Fragment的基類中完成這個工作。其次,效能可能差一點,畢竟要遞迴遍歷所有子節點(不過實際使用中沒有明顯的效能下降程式依然流暢)。
方法3:通過反射替換預設字型
App中顯示的字型來自於Typeface中的預定義的字型,這種方式是通過改變系統字型樣式改變字型。
首先需要改變APP的BaseTheme
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<!-- Set system default typeface -->
<item name="android:typeface">monospace</item>
</style>
再然後我將需要的方法又抽取了一下,和之前的TypefaceUtil形成了一個完整的工具類,程式碼如下:
package com.laundrylang.laundrylangpda.util;
import android.app.Activity;
import android.content.Context;
import android.graphics.Typeface;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.lang.reflect.Field;
/*
* Copyright (C) 2013 Peng fei Pan <[email protected]>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public class TypefaceUtil {
/**
* 為給定的字串新增HTML紅色標記,當使用Html.fromHtml()方式顯示到TextView 的時候其將是紅色的
*
* @param string 給定的字串
* @return
*/
public static String addHtmlRedFlag(String string) {
return "<font color=\"red\">" + string + "</font>";
}
/**
* 將給定的字串中所有給定的關鍵字標紅
*
* @param sourceString 給定的字串
* @param keyword 給定的關鍵字
* @return 返回的是帶Html標籤的字串,在使用時要通過Html.fromHtml()轉換為Spanned物件再傳遞給TextView物件
*/
public static String keywordMadeRed(String sourceString, String keyword) {
String result = "";
if (sourceString != null && !"".equals(sourceString.trim())) {
if (keyword != null && !"".equals(keyword.trim())) {
result = sourceString.replaceAll(keyword, "<font color=\"red\">" + keyword + "</font>");
} else {
result = sourceString;
}
}
return result;
}
/**
* <p>Replace the font of specified view and it's children</p>
* @param root The root view.
* @param fontPath font file path relative to 'assets' directory.
*/
public static void replaceFont(@NonNull View root, String fontPath) {
if (root == null || TextUtils.isEmpty(fontPath)) {
return;
}
if (root instanceof TextView) { // If view is TextView or it's subclass, replace it's font
TextView textView = (TextView)root;
int style = Typeface.NORMAL;
if (textView.getTypeface() != null) {
style = textView.getTypeface().getStyle();
}
textView.setTypeface(createTypeface(root.getContext(), fontPath), style);
} else if (root instanceof ViewGroup) { // If view is ViewGroup, apply this method on it's child views
ViewGroup viewGroup = (ViewGroup) root;
for (int i = 0; i < viewGroup.getChildCount(); ++i) {
replaceFont(viewGroup.getChildAt(i), fontPath);
}
}
}
/**
* <p>Replace the font of specified view and it's children</p>
* 通過遞迴批量替換某個View及其子View的字型改變Activity內部控制元件的字型(TextView,Button,EditText,CheckBox,RadioButton等)
* @param context The view corresponding to the activity.
* @param fontPath font file path relative to 'assets' directory.
*/
public static void replaceFont(@NonNull Activity context, String fontPath) {
replaceFont(getRootView(context),fontPath);
}
/*
* Create a Typeface instance with your font file
*/
public static Typeface createTypeface(Context context, String fontPath) {
return Typeface.createFromAsset(context.getAssets(), fontPath);
}
/**
* 從Activity 獲取 rootView 根節點
* @param context
* @return 當前activity佈局的根節點
*/
public static View getRootView(Activity context)
{
return ((ViewGroup)context.findViewById(android.R.id.content)).getChildAt(0);
}
/**
* 通過改變App的系統字型替換App內部所有控制元件的字型(TextView,Button,EditText,CheckBox,RadioButton等)
* @param context
* @param fontPath
* 需要修改style樣式為monospace:
*/
// <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
// <!-- Customize your theme here. -->
// <!-- Set system default typeface -->
// <item name="android:typeface">monospace</item>
// </style>
public static void replaceSystemDefaultFont(@NonNull Context context, @NonNull String fontPath) {
replaceTypefaceField("MONOSPACE", createTypeface(context, fontPath));
}
/**
* <p>Replace field in class Typeface with reflection.</p>
*/
private static void replaceTypefaceField(String fieldName, Object value) {
try {
Field defaultField = Typeface.class.getDeclaredField(fieldName);
defaultField.setAccessible(true);
defaultField.set(null, value);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
程式碼中引用只需要在BaseApplication的onCreate裡設定,為了增強效果,這裡我將顯示樣式變成粗體:
TypefaceUtil.replaceSystemDefaultFont(this,"fonts/bold.ttf");
前後對照佈局檔案,可以看到,雖然是粗體,但和方法2一樣,也是所有的字型都更換樣式了:
這種設定方式的優缺點:
優點:方式2的優點+更加簡潔
缺點:字型檔案一般比較大,載入時間長而且佔記憶體(不過實際使用中沒有明顯的效能下降程式依然流暢)。
個人中心設定
我一般都是用第2,3種,簡潔高效,現在說一下如何在個人設定裡邊改變你的app字型:
經實踐,第2種方法是最好的,可以實時更新頁面。而第三種需要返回重新進入到activity才會看到效果。
先在BaseActivity註冊一個字型改變監聽的廣播
package com.laundrylang.laundrylangpda.activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import com.laundrylang.laundrylangpda.constant.ConstantValue;
import com.laundrylang.laundrylangpda.util.PreferencesUtil;
import com.laundrylang.laundrylangpda.util.TypefaceUtil;
public abstract class BaseActivity extends FragmentActivity {
private TypefaceChangeReceiver typefaceChangeReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayout());
//改變新建立Activity的字型
onTypefaceChange(PreferencesUtil.getString(ConstantValue.AppSetting.SHPNAME,ConstantValue.AppSetting.typeface));
typefaceChangeReceiver = new TypefaceChangeReceiver();
IntentFilter typefaceFilter = new IntentFilter();
typefaceFilter.addAction(ConstantValue.ReceiverAction.TYPEFACE_ACTION);
registerReceiver(typefaceChangeReceiver,typefaceFilter);
}
@Override
protected void onDestroy() {
unregisterReceiver(typefaceChangeReceiver);
super.onDestroy();
}
/**
* 設定當前activity的layout
* @return 當前介面的佈局id
*/
protected abstract int getLayout();
/**
* 字型改變
*/
protected void onTypefaceChange(String typeface){
TypefaceUtil.replaceFont(this, typeface);
}
/**
* 字型改變監聽,用於改變整個APP字型
*/
public class TypefaceChangeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if(ConstantValue.ReceiverAction.TYPEFACE_ACTION.equals(intent.getAction())){
String typeface = intent.getStringExtra("typeface");
//改變未銷燬尚存在的Activity的字型
onTypefaceChange(typeface);
}
}
}
}
常量抽取出來,放到ConstantValue常量類中:
interface AppSetting{
String SHPNAME = "SETTING";
String typeface = "typeface";
}
interface ReceiverAction{
String TYPEFACE_ACTION = "font.TYPEFACE_CHANGE";
}
interface Typeface{
String BOLD = "fonts/bold.ttf";//粗體
String FZZY = "fonts/fzzy.ttf";//方正準圓
String HWCY = "fonts/hwcy.ttf";//華文彩雲
String HWXK = "fonts/hwxk.ttf";//華文行楷
String HWXS = "fonts/hwxs.ttf";//華文新宋
String HWXW = "fonts/hwxw.ttf";//華文新魏
String YY = "fonts/yy.ttf";//幼圓
}
在SettingActivity裡傳送特定的廣播即可:
@Override
public void onClick(View v) {
Intent intent = new Intent(ConstantValue.ReceiverAction.TYPEFACE_ACTION);
String typeface = null;
switch (v.getId()){
case R.id.bold:
typeface = ConstantValue.Typeface.BOLD;
break;
case R.id.hwcy:
typeface = ConstantValue.Typeface.HWCY;
break;
}
//儲存字型設定
PreferencesUtil.put(ConstantValue.AppSetting.SHPNAME, ConstantValue.AppSetting.typeface, typeface);
intent.putExtra("typeface", typeface);
sendBroadcast(intent);
}
這裡有兩個動作,
一個是傳送廣播,用於修改之前建立了但並未銷燬的Activity的字型;
另一個是儲存設定的字型,用於修改之後將建立的Activity的字型。
不過需要在BaseActivity裡setContentView之後新增程式碼:
onTypefaceChange(PreferencesUtil.getString(ConstantValue.AppSetting.SHPNAME,ConstantValue.AppSetting.typeface));
順便附上PreferencesUtil的一部分程式碼:
private static Context context = BaseApplication.getContext();
private final static String DEFAULT_STRING_VALUE = "";
public static void put(@NonNull String SHPNAME,@NonNull String key,Object value){
SharedPreferences shp = context.getSharedPreferences(SHPNAME, Context.MODE_PRIVATE);
SharedPreferences.Editor edit = shp.edit();
if(value instanceof String)
edit.putString(key,(String)value);
if(value instanceof Boolean)
edit.putBoolean(key, (Boolean) value);
if(value instanceof Float)
edit.putFloat(key, (Float) value);
if(value instanceof Long)
edit.putLong(key, (Long) value);
if(value instanceof Integer)
edit.putInt(key, (Integer) value);
if(value instanceof Set)
edit.putStringSet(key, (Set<String>) value);
edit.apply();
}
public static String getString(@NonNull String SHPNAME,@NonNull String key){
SharedPreferences shp = context.getSharedPreferences(SHPNAME, Context.MODE_PRIVATE);
return shp.getString(key, DEFAULT_STRING_VALUE);
}
這樣便實現了在一個SettingActivity裡改變全域性字型的功能。
使用注意:
1.如果字型檔案比較大,當設定後可能並不會立即生效,有1~2s的延遲,具體還依據類中控制元件的數量來定。
2.至關重要,所有的Activity請務必要繼承BaseActivity。
相關推薦
太激動!Android修改全域性字型樣式,替換整個APP字型
最近一直在如何全域性修改app字型上困惑著,今天終於有了突破。我將蒐集的資料進行了整理,現在提供給大家。 前面為分析,建議直接翻到最後看【個人中心設定】。 首先將專案需要的字型資源放置在app下: 這是我自己找的字型檔案,分別代表粗體,方正準圓,華
Android設定全域性字型大小,實現小中大字型功能
很多app有這種需求,實現字型小中大字型設定,仿照QQ的字型。經過測試,下面程式碼完美實現,需要重啟APP才能生效。 上核心程式碼 public class MainActivity extends Activity { @InjectView(R.id.btn
放心嗨!Android 9 Pie發布,網易雲易盾加固已第一時間適配
工作 工程師 ffffff 谷歌 時間 water 科技 RoCE image 自5月份谷歌在年度開發者大會上首次亮相Android 9 P後,其正式版Android 9 Pie昨天正式發布,網易雲易盾移動安全已在昨天第一時間裏進行了最終適配。 早在今年初,易盾就做好了相關
AI 程式設計師 80 萬太少!BAT 得不到他,就用錢搶他!
【CSDN編者按】最近一則熱搜把AI程式設計師又送到了臺前,AI程式設計師應屆生80萬了!就在大家議論紛紛改行業時,筆者瞭解到在國外隨著矽谷人工智慧人才的激烈爭奪,薪酬更貴! 據外媒Business Insider報道,為了招募人工智慧領域的頂尖人才,Oracle開出了高
AI程式設計師80萬太少!BAT得不到他,就用錢搶他!
點選上方“程式人生”,選擇“置頂公眾號” 第一時間關注程式猿(媛)身邊的故事 【CSDN 編者按】最近一則熱搜把AI程式設計師又送到了臺前,AI程式設計師應屆生80萬了!就在大家議論紛紛改行業時,筆者瞭解到在國外隨著矽谷人工智慧人才的激烈爭奪,薪酬更貴!據外媒 Busines
修改select預設樣式,相容IE9
前面有篇文章已經提供瞭如何修改select標籤的預設樣式,但是隻能相容到ie10,要相容ie9只能模擬一個類似的 html結構: <div class="select_diy"> <s
樣式問題-如何一次性設定網站英文字型樣式,中文字型等樣式
今天才發現,CSS 的 font-family 屬性 的基本能力之一就是依其列表內字型的排序(優先順序)來顯示文字。 如果設定為「font-family: "英文字型", "中文字型", generic-family;」,就用第一項 "Western Font" 顯示西文(英文字母、英文標點、阿拉伯數
CSS筆記(字型樣式,文字屬性和顏色樣式)
1 字型樣式 1.1 字體系列(font family) 在CSS中,字型劃分為 五組“字體系列”,分別為: sans-serif字體系列:沒有襯線的字型,最常用不為過,如 Arial、Verdana,Arial Black,Geneva等 seri
Android自定義SeekBar樣式,遇到的進度條高度問題
首先來看我的android:progressDrawable="@drawable/seek_progress_drawable",下面是seek_progress_drawable檔案: <
MFC下如何設定控制元件的字型樣式,視窗背景和控制元件底色透明
重寫OnPaint方法(WM_PAINT)和OnCtlColor的方法(WM_CTLCOLOR) void CPannelRecentUsed::OnPaint() { CPaintDC dc(this); // device context for painting
intelliJ IDEA (JetBrains PyCharm)中 3個地方設定字型大小,文字編輯的字型大小,介面字型大小,顯示log的字型大小
在使用這個intelliJ IDEA (JetBrains PyCharm)編輯器的時候,可能剛剛開始要設定合適的字型大小,但是除了設定,編輯程式碼時的文字的字型大小外。 在現實console,就是那個log框的字型也是很小。這個地方的設定,也不是一下兩下就能找到的。
℃江讓您從精通到入門:Android Studio 簡單實現ViewPager,可做APP操作提示
前期準備,如下圖: 第一步、先書寫佈局檔案:activity_main.xml檔案如下: <?xml version="1.0" encoding="utf-8"?> <Re
Android 切換系統語言後,重啟App
問題描述:App->改變系統語言->重進App後,最近的Activity會走onCreate()方法,然後App被殺掉;直到第二次進入App後,App會重啟 解決方案:App->改變系統語言->重進App後,最近的Activity走到onCreate
android:修改PagerTabStrip中的背景顏色,標題字型的樣式、顏色和圖示以及指示條的顏色
1.修改PagerTabStrip中的背景顏色 我們在佈局中直接設定background屬性即可: <android.support.v4.view.ViewPager android:id="@+id/pager" android:layout_width="fill_parent" andro
Android 修改源碼自定義SwipeRefreshLayout樣式——高仿微信朋友圈下拉刷新
樣式 post and 微信 修改 size roi 自定義 details 修改源碼自定義SwipeRefreshLayout樣式——高仿微信朋友圈下拉刷新Android 修改源碼自定義SwipeRefreshLayout樣式——高仿微信朋友圈下拉
Android左邊控制檯字型樣式設定
file->setting裡面:appearance,紅框裡面設定。(https://img-blog.csdn.net/20171027101134428?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbnp3MzE=/font/5a6L5
Latex修改全域性字型
如果使用的是XeTeX 或者 LuaTeX編譯器的話,可以直接使用fontspec巨集包。用\setmainfont引入就可以了。 但如果使用的是pdfLatex的話就不適用了,此時修改字型只能針對latex自帶的三個字型族,詳情可以看這裡. 引用原文如下: Thes
太無奈!Redis 作者被迫修改 master-slave 架構的描述
相信在座各位的開發者都不會對 Redis 的主從模式(master-slave)感到陌生。Redis 中的主從模式事實上也是源自 MySQL 中同名的一個概念,是同步資料的一種手段。在這樣的場景下,master-slave 本來是一個不帶任何感情色彩的詞語。然而有很多開發者卻
(五)微信小程式-文章列表-實現及在全域性樣式表新增頁面預設字型樣式
文章列表 每篇文章包含文章標題、文章頭圖、文章概要、評論數和閱讀數,基本上使用view, image, text 這三個元件就可以完成 先將準備好的圖片放在根目錄images檔案相應的路徑下,沒有建立,不過多解釋 我們在前序博文微信輪播圖實現專案下繼續操作操作 在post.wxm
修改 input中的placeholder的字型樣式和顏色
placeholder屬性是css3中新增加的屬性, 由於是新加入的屬性因此對各大瀏覽器都不相容: 因此在使用的時候要加相容性 火狐:-moz-placeholder { /* Mozilla Firefox 4 to&nb