2012-03-21 10 views
20

Tôi muốn sử dụng phông chữ Roboto trong ứng dụng Android của mình và đảm bảo nó hoạt động cho các phiên bản Android trước đó mà không cài đặt phông chữ. Tôi biết tôi có thể làm điều này bằng cách sử dụng Typeface.createFromAsset() và sau đó tự thiết lập phông chữ cho mỗi TextViews/Buttons/Other-Objects của tôi. Nó có vẻ như một nỗi đau lớn để làm điều này cho mọi đối tượng tôi hiển thị trên màn hình mặc dù.Sử dụng phông chữ Roboto cho các thiết bị cũ

Câu hỏi của tôi là, có cách nào tốt hơn để thực hiện việc này không? Một số lớp trợ giúp hoặc một cách để đặt phông chữ tùy chỉnh trong tệp chủ đề .xml? Mọi thứ tự động sẽ tốt hơn việc tự liệt kê mọi đối tượng trên mỗi màn hình và thay đổi phông chữ.

Cảm ơn!

+0

Bạn có thể đặt nó trong một [kiểu] (http://developer.android.com/guide/topics/ui/themes.html) có lẽ. Idk nếu đó làm việc với kiểu chữ từ tài sản mặc dù. – zapl

Trả lời

16

Truy xuất tất cả các chế độ xem bên trong hoạt động, kiểm tra loại của nó và áp dụng hành động thích hợp.

Typeface typeface = Typeface.createFromAsset(getAssets(), "fonts/Roboto/Roboto-Regular.ttf"); 
for (View view : allViews) 
{ 
if (view instanceof TextView) 
{ 
    TextView textView = (TextView) view; 
    textView.setTypeface(typeface); 
    } 
} 
30

Trên Câu trả lời được chấp nhận là đúng nhưng tôi chỉ muốn cung cấp thực hiện của tôi ở đây

lớp tiện ích của tôi:

package com.example.utils; 

import android.content.Context; 
import android.graphics.Typeface; 
import android.view.View; 
import android.view.ViewGroup; 
import android.widget.TextView; 

public class AndroidUtils 
{ 
    private static Typeface robotoTypeFace; 

    public static void setRobotoFont (Context context, View view) 
    { 
     if (robotoTypeFace == null) 
     { 
      robotoTypeFace = Typeface.createFromAsset(context.getAssets(), "fonts/Roboto/Roboto-Regular.ttf"); 
     } 
     setFont(view, robotoTypeFace); 
    } 

    private static void setFont (View view, Typeface robotoTypeFace) 
    { 
     if (view instanceof ViewGroup) 
     { 
      for (int i = 0; i < ((ViewGroup)view).getChildCount(); i++) 
      { 
       setFont(((ViewGroup)view).getChildAt(i), robotoTypeFace); 
      } 
     } 
     else if (view instanceof TextView) 
     { 
      ((TextView) view).setTypeface(robotoTypeFace); 
     } 
    } 
} 

Làm thế nào để sử dụng nó, giả sử this là một Hoạt động:

AndroidUtils.setRobotoFont(this, view); 

Để đặt cùng một phông chữ cho tất cả TextView, bạn có thể sử dụng corView hoạt động của bạn:

ViewGroup godfatherView = (ViewGroup)this.getWindow().getDecorView(); 
AndroidUtils.setRobotoFont(this, godfatherView); 

Nếu bạn có bộ điều hợp hoặc đoạn, đừng quên đặt phông chữ.

Xem here cũng.

3

Đây là phiên bản cải tiến của giải pháp này. Nó có thể lưu các phông chữ và xem xét các thiết lập tham số TextView.textStyle. Vì vậy, nó có thể thiết lập văn bản in đậm. http://anton.averin.pro/2012/09/12/how-to-use-android-roboto-font-in-honeycomb-and-earlier-versions/

+0

Rất tốt! Sử dụng với << ViewGroup godfatherView = (ViewGroup) this.getWindow(). GetDecorView(); AndroidUtils.setRobotoFont (this, godfatherView); >> là hoàn hảo. –

7

Nhờ @Jitsu, @Arnaud và @Pawan M, tôi đã thực hiện giải pháp của tôi, tốt hơn so với tất cả chúng mình:

/** 
* Adapted from http://stackoverflow.com/a/12387343/450148 
* 
* @author Anton Averin 
* @author Felipe Micaroni Lalli 
*/ 

package net.alouw.alouwCheckin.util; 

import android.content.Context; 
import android.graphics.Typeface; 
import android.view.View; 
import android.view.ViewGroup; 
import android.widget.TextView; 

import java.util.EnumMap; 
import java.util.Map; 

public final class FontUtils { 
    private FontUtils() { 
    } 

    private enum FontType { 
     BOLD("fonts/Roboto/Roboto-BoldCondensed.ttf"), 
     BOLD_ITALIC("fonts/Roboto/Roboto-BoldCondensedItalic.ttf"), 
     NORMAL("fonts/Roboto/Roboto-Condensed.ttf"), 
     ITALIC("fonts/Roboto/Roboto-CondensedItalic.ttf"); 

     private final String path; 

     FontType(String path) { 
      this.path = path; 
     } 

     public String getPath() { 
      return path; 
     } 
    } 

    /* cache for loaded Roboto typefaces*/ 
    private static Map<FontType, Typeface> typefaceCache = new EnumMap<FontType, Typeface>(FontType.class); 

    /** 
    * Creates Roboto typeface and puts it into cache 
    */ 
    private static Typeface getRobotoTypeface(Context context, FontType fontType) { 
     String fontPath = fontType.getPath(); 

     if (!typefaceCache.containsKey(fontType)) { 
      typefaceCache.put(fontType, Typeface.createFromAsset(context.getAssets(), fontPath)); 
     } 

     return typefaceCache.get(fontType); 
    } 

    /** 
    * Gets roboto typeface according to passed typeface style settings. 
    * <p/> 
    * Will get Roboto-Bold for Typeface.BOLD etc 
    */ 
    private static Typeface getRobotoTypeface(Context context, Typeface originalTypeface) { 
     FontType robotoFontType = null; 

     if (originalTypeface == null) { 
      robotoFontType = FontType.NORMAL; 
     } else { 
      int style = originalTypeface.getStyle(); 

      switch (style) { 
       case Typeface.BOLD: 
        robotoFontType = FontType.BOLD; 
        break; 

       case Typeface.BOLD_ITALIC: 
        robotoFontType = FontType.BOLD_ITALIC; 
        break; 

       case Typeface.ITALIC: 
        robotoFontType = FontType.ITALIC; 
        break; 

       case Typeface.NORMAL: 
        robotoFontType = FontType.NORMAL; 
        break; 
      } 
     } 

     return (robotoFontType == null) ? originalTypeface : getRobotoTypeface(context, robotoFontType); 
    } 

    /** 
    * Walks ViewGroups, finds TextViews and applies Typefaces taking styling in consideration 
    * 
    * @param context - to reach assets 
    * @param view - root view to apply typeface to 
    */ 
    public static void setRobotoFont(Context context, View view) { 
     if (view instanceof ViewGroup) { 
      for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) { 
       setRobotoFont(context, ((ViewGroup) view).getChildAt(i)); 
      } 
     } else if (view instanceof TextView) { 
      Typeface currentTypeface = ((TextView) view).getTypeface(); 
      ((TextView) view).setTypeface(getRobotoTypeface(context, currentTypeface)); 
     } 
    } 
} 

Và điều cuối cùng trong hoạt động chính onCreate của bạn:

if (Build.VERSION.SDK_INT < 11) { 
    ViewGroup godfatherView = (ViewGroup) this.getWindow().getDecorView(); 
    FontUtils.setRobotoFont(this, godfatherView); 
} 

Và trong danh sách của tôi với tùy chỉnh xem đoạn mã trên không hiệu quả, vì vậy tôi đã thực hiện điều này:

@Override 
public View getView(int position, View convertView, ViewGroup parent) { 
    // (...) 

    View view = // build your custom view here 

    if (Build.VERSION.SDK_INT < 11) { 
     FontUtils.setRobotoFont(activity, view); 
    } 

    return view; 
} 
+0

Hi nghĩ rằng đó là '(if (Build.VERSION.SDK_INT <= 11))' no? –

+0

Không chắc chắn. Vui lòng chỉnh sửa nếu là trường hợp. –

3

Tôi sử dụng một giải pháp khác. Tôi đặt tùy chỉnh LayoutInflater.Factory thành hoạt động. Vì vậy, tôi có toàn quyền truy cập để xem sau khi tạo. Tôi có thể cài đặt phông chữ tùy chỉnh cho mọi TextView mà không cần lặp lại trên hệ thống phân cấp khung nhìn. Một điều bạn nên làm để sử dụng phông chữ tùy chỉnh trong tất cả các ứng dụng của bạn là gọi new Font(...).install() trong hoạt động cơ sở của bạn. Xem exaple dưới đây.

Đây là giải pháp của tôi với mẫu sử dụng:

import java.util.Map; 

import com.google.common.collect.Maps; 

import static com.google.common.base.Preconditions.checkNotNull; 

import android.R; 
import android.app.Activity; 
import android.content.Context; 
import android.content.res.TypedArray; 
import android.graphics.Typeface; 
import android.support.v4.app.FragmentActivity; 
import android.util.AttributeSet; 
import android.util.Log; 
import android.view.InflateException; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.widget.TextView; 

/** 
* Provides an ability to apply custom font to all {@link TextView} and subclasses. 
* 
* To install custom font use method {@link #install(Activity)} in {@link Activity#onCreate(android.os.Bundle)} 
* <b>before</b> calling super.onCreate(Bundle). 
* 
* <p/>Example of usage: 
* <pre> 
* {@code 
* public class BaseActivity extends SherlockFragmentActivity { 
* 
*  protected void onCreate(Bundle state) { 
*   applyCustomFontForPreICS(); 
*   super.onCreate(state); 
*  } 
* 
*  private void applyCustomFontForPreICS() { 
*   boolean isPreICS = Build.VERSION.SDK_INT < BUILD_VERSION_CODE_ICE_CREAM_SANDWICH 
*   if (isPreICS) { 
*    new Font(
*     "font/roboto_regular.ttf", 
*     "font/roboto_bold.ttf", 
*     "font/roboto_italic.ttf", 
*     "font/roboto_bold_italic.ttf" 
*    ).install(this); 
*   } 
*  } 
* } 
* } 
* </pre> 
* 
* @author Alexey Danilov ([email protected]) 
*/ 
public class Font { 

    private static final Map<String, Typeface> FONTS = Maps.newHashMap(); 

    private String regularFontPath; 
    private String boldFontPath; 
    private String italicFontPath; 
    private String boldItalicFontPath; 

    /** 
    * Creates instance to be used for setting particular font. 
    * 
    * @param regularPath regular font assets path, must be not {@code null} 
    * @param boldPath bold font assets path, must be not {@code null} 
    * @param italicPath italic font assets path, must be not {@code null} 
    * @param boldItalicPath bold and italic font assets path, must be not {@code null} 
    */ 
    public Font(String regularPath, String boldPath, String italicPath, String boldItalicPath) { 
     this.regularFontPath = checkNotNull(regularPath); 
     this.boldFontPath = checkNotNull(boldPath); 
     this.italicFontPath = checkNotNull(italicPath); 
     this.boldItalicFontPath = checkNotNull(boldItalicPath); 
    } 

    /** 
    * Installs custom font to activity. 
    * 
    * @param activity an activity custom font will be installed to, must be not {@code null}. 
    */ 
    public void install(Activity activity) { 
     checkNotNull(activity, "Activity must be not null!"); 

     LayoutInflater layoutInflater = activity.getLayoutInflater(); 
     boolean factoryIsEmpty = layoutInflater.getFactory() == null; 
     if (!factoryIsEmpty) { 
      throw new IllegalStateException("Impossible to use this method for this activity: layout factory is set!"); 
     } 
     layoutInflater.setFactory(new FontLayoutInflaterFactory()); 
    } 

    private Typeface getFont(int type, Context context) { 
     switch (type) { 
      case Typeface.NORMAL: 
       return getFont(context, regularFontPath); 
      case Typeface.BOLD: 
       return getFont(context, boldFontPath); 
      case Typeface.ITALIC: 
       return getFont(context, italicFontPath); 
      case Typeface.BOLD_ITALIC: 
       return getFont(context, boldItalicFontPath); 
      default: { 
       throw new IllegalArgumentException("Undefined font type " + type); 
      } 
     } 
    } 

    private Typeface getFont(Context context, String path) { 
     if (FONTS.containsKey(path)) { 
      return FONTS.get(path); 
     } else { 
      Typeface typeface = makeTypeface(context, path); 
      FONTS.put(path, typeface); 
      return typeface; 
     } 
    } 

    private Typeface makeTypeface(Context context, String path) { 
     try { 
      return Typeface.createFromAsset(context.getAssets(), path); 
     } catch (Exception e) { 
      // add user-friendly error message 
      throw new IllegalArgumentException(String.format("Error creating font from assets path '%s'", path), e); 
     } 
    } 

    private void applyFontToTextView(Context context, TextView textView, AttributeSet attrs) { 
     int[] fontStyleAttributes = {R.attr.textStyle}; 
     TypedArray typedArray = context.obtainStyledAttributes(attrs, fontStyleAttributes); 
     boolean isStyleSpecified = typedArray.getIndexCount() != 0; 
     int type = isStyleSpecified ? typedArray.getInt(0, Typeface.NORMAL) : Typeface.NORMAL; 
     Typeface font = getFont(type, context); 
     textView.setTypeface(font, type); 
    } 

    private final class FontLayoutInflaterFactory implements LayoutInflater.Factory { 

     // to improve perfomance the package with the most usable components should be the first. 
     private final String[] ANDROID_UI_COMPONENT_PACKAGES = { 
       "android.widget.", 
       "android.webkit.", 
       "android.view." 
     }; 

     @Override 
     public View onCreateView(String name, Context context, AttributeSet attrs) { 
      try { 
       // we install custom LayoutInflater.Factory, so FragmentActivity have no chance set own factory and 
       // inflate tag <fragment> in method onCreateView. So call it explicitly. 
       if ("fragment".equals(name) && context instanceof FragmentActivity) { 
        FragmentActivity fragmentActivity = (FragmentActivity) context; 
        return fragmentActivity.onCreateView(name, context, attrs); 
       } 

       View view = createView(name, attrs, LayoutInflater.from(context)); 
       if (view == null) { 
        // It's strange! The view is not ours neither android's. May be the package of this view 
        // is not listed in ANDROID_UI_COMPONENT_PACKAGES. Return null for the default behavior. 
        Log.d(LOG_TAG, "Cannot create view with name: " + name); 
        return null; 
       } 

       if (view instanceof TextView) { 
        TextView textView = (TextView) view; 
        applyFontToTextView(context, textView, attrs); 
       } 
       return view; 
      } catch (InflateException e) { 
       Log.e(LOG_TAG, "Error inflating view", e); 
       return null; 
      } catch (ClassNotFoundException e) { 
       Log.e(LOG_TAG, "Error inflating view", e); 
       return null; 
      } 
     } 

     private View createView(String name, AttributeSet attrs, LayoutInflater layoutInflater) throws ClassNotFoundException { 
      View view = null; 
      boolean isAndroidComponent = name.indexOf('.') == -1; 
      if (isAndroidComponent) { 
       // We don't know package name of the view with the given simple name. Try android ui packages listed in 
       // ANDROID_UI_COMPONENT_PACKAGES 

       // The same implementation is in the class PhoneLayoutInflater from internal API 
       for (String androidPackage : ANDROID_UI_COMPONENT_PACKAGES) { 
        try { 
         view = layoutInflater.createView(name, androidPackage, attrs); 
         if (view != null) { 
          break; 
         } 
        } catch (ClassNotFoundException e) { 
         // Do nothing, we will try another package 
        } 
       } 
      } else { 
       view = layoutInflater.createView(name, null, attrs); 
      } 
      return view; 
     } 
    } 
} 

Lưu ý nó có sự phụ thuộc vào guava, nhưng bạn có thể thực hiện phương pháp này một mình.