如何在程序中将自定义字体设置为下拉框文本的字体?

96

我在我的assets文件夹中有一个ttf字体文件。我知道如何在textview中使用它:

Typeface externalFont=Typeface.createFromAsset(getAssets(), "fonts/HelveticaNeueLTCom-Lt.ttf");
textview1.setTypeface(externalFont);

我已经在自己的xml文件中定义了旋转器文本的外观(通常在Android中):

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android" 
android:id="@+android:id/text1"
style="?android:attr/spinnerItemStyle"
android:singleLine="true"
android:textColor="#ffffff"
android:gravity="center" 
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:ellipsize="marquee" />

我无法从代码中引用此TextView,总是出现空指针异常。例如,我尝试了:

TextView spinner_text=(TextView)findViewById(R.id.text1);
spinner_text.setTypeface(externalFont);

我可以选择在其自己的xml中定义的spinner文本,使用我的外部字体吗?

谢谢。

带答案的编辑:

这个可以实现:

String [] items = new String[2];
    items[0]="Something1";
    items[1]="Something2";

ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
                    R.layout.spinaca, items) {

         public View getView(int position, View convertView, ViewGroup parent) {
                 View v = super.getView(position, convertView, parent);

                 Typeface externalFont=Typeface.createFromAsset(getAssets(), "fonts/HelveticaNeueLTCom-Lt.ttf");
                 ((TextView) v).setTypeface(externalFont);

                 return v;
         }


         public View getDropDownView(int position,  View convertView,  ViewGroup parent) {
                  View v =super.getDropDownView(position, convertView, parent);

                 Typeface externalFont=Typeface.createFromAsset(getAssets(), "fonts/HelveticaNeueLTCom-Lt.ttf");
                 ((TextView) v).setTypeface(externalFont);
                 v.setBackgroundColor(Color.GREEN);

                 return v;
         }
 };


     adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);                                 
     spinner.setAdapter(adapter);

可能需要添加

import android.view.ViewGroup;
将此代码添加到您文件顶部的导入列表中。出于某种原因,当Eclipse无法识别代码中涉及的ViewGroup类时,它不会提出此建议。

2
非常感谢朋友。经过长时间的奋斗,我终于找到了它。这最终拯救了我的一天。 - Andro Selva
感谢您添加答案! - CommonSenseCode
非常好的问题... - Rahul Kushwaha
7个回答

86

以下是我所使用的方法(结合了CommonsWaregsanllorente两位用户的意见):

private static class MySpinnerAdapter extends ArrayAdapter<String> {
    // Initialise custom font, for example:
    Typeface font = Typeface.createFromAsset(getContext().getAssets(),
                        "fonts/Blambot.otf");

    // (In reality I used a manager which caches the Typeface objects)
    // Typeface font = FontManager.getInstance().getFont(getContext(), BLAMBOT);

    private MySpinnerAdapter(Context context, int resource, List<String> items) {
        super(context, resource, items);
    }

    // Affects default (closed) state of the spinner
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        TextView view = (TextView) super.getView(position, convertView, parent);
        view.setTypeface(font);
        return view;
    }

    // Affects opened state of the spinner
    @Override
    public View getDropDownView(int position, View convertView, ViewGroup parent) {
        TextView view = (TextView) super.getDropDownView(position, convertView, parent);
        view.setTypeface(font);
        return view;
    }
}

如果您和我一样最初使用ArrayAdapter.createFromResource() 和数组资源(如Spinner文档中所述)填充Spinner,那么您应该像这样使用MySpinnerAdapter:

MySpinnerAdapter<String> adapter = new MySpinnerAdapter(
        getContext(),
        R.layout.view_spinner_item,
        Arrays.asList(getResources().getStringArray(R.array.my_array))
);
spinner.setAdapter(adapter);

1
太好了!我甚至进一步创建了一个assignAdapterWithOptions(Spinner spinner,int textArrayResId)方法,从spinner.getContext()获取上下文,并在其中将适配器分配给Spinner(Spinner布局是我的整个应用程序的标准)。 - I.G. Pascual
这对我很有帮助。谢谢...@Jonik - Rahul Kushwaha

24
你需要通过自己的定制SpinnerAdaptergetView()getDropDownView()方法中应用字体。

我编辑了我的问题并提出了最新的问题,您能告诉我哪里做错了吗?谢谢。 - DixieFlatline
1
@DixieFlatline:你需要添加一个android.view.ViewGroup的导入,大概是这样。 - CommonsWare

15

如果你在另一个文件中实现了你的适配器,你可以从适配器的构造函数中访问"getAssets()"函数,因为你有上下文作为参数。

public class YourItemAdapter extends ArrayAdapter<String> {
int recurso;
Typeface tf;

public YourItemAdapter(Context _context, int _resource,
        List<String> _items) {

    super(_context, _resource, _items);
    recurso=_resource;
    tf=Typeface.createFromAsset(_context.getAssets(),"font/digital-7.ttf");
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    //You can use the new tf here.
    TextView spinner_text=(TextView)findViewById(R.id.text1);
    spinner_text.setTypeface(tf);
    }
}

这个还是有点用的,尽管对我没起作用:findViewById(R.id.text1) 似乎找不到 TextView,尽管 ID 是正确的。这段代码中还有其他问题:1)在 getView() 中缺少返回语句,2)未使用的字段 recurso,3)一些样式问题,例如将变量命名为 spinner_text(应该是 spinnerText)。这是对我有用的解决方案 - Jonik

4

尝试创建自定义custom_spinner.xml

<?xml version="1.0" encoding="utf-8"?>

<com.xxxx.xxxx.CheckedTextViewC

    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@android:id/text1"
    style="?android:attr/spinnerDropDownItemStyle"
    android:singleLine="true"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:ellipsize="marquee"
    android:textAlignment="center"
    android:paddingTop="5dp"
    android:paddingBottom="5dp"
    android:textSize="18sp"

    />

创建自定义的 CheckedTextView,如下所示:
import android.content.Context;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.widget.CheckedTextView;

public class CheckedTextViewC extends CheckedTextView {

    public CheckedTextViewC(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        // TODO Auto-generated constructor stub
    }
    public CheckedTextViewC(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
    }
    public CheckedTextViewC(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
    }
    public void setTypeface(Typeface tf, int style) {
        if(!this.isInEditMode()){
        Typeface normalTypeface = Typeface.createFromAsset(getContext().getAssets(), "font/Roboto-Light.ttf");
        Typeface boldTypeface = Typeface.createFromAsset(getContext().getAssets(), "font/Roboto-Light.ttf");

        if (style == Typeface.BOLD) {
            super.setTypeface(boldTypeface/*, -1*/);
        } else {
            super.setTypeface(normalTypeface/*, -1*/);
        }
        }

    }
}

实现新布局

adapter= new ArrayAdapter <String>(Menu.this,R.layout.custom_spinner, list);

我认为这样会更好,因为你不需要在适配器上动手脏,而且你可以在应用程序的任何其他地方使用这个自定义文本视图。 - Milad

2
这是我之前回答的延续:https://dev59.com/yG035IYBdhLWcg3wVud1#51100507 为了兼容性,您可以在Android中针对小部件使用样式和自定义类。尽管Android版本15及以上引入了新的`/res/font`资源文件夹,但仍然支持旧的方法。 参见:Android 中的字体资源
步骤1:声明 `item_spinner.xml`。
<?xml version="1.0" encoding="utf-8"?>
<com.my_package.custom_views.FontTextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/tv_spinner"
    style="@style/App_TextViewStyleSmall"
    android:layout_gravity="start|bottom"
    android:layout_marginLeft="@dimen/dp_5"
    android:layout_marginStart="@dimen/dp_5"
    android:ellipsize="marquee"
    android:gravity="start|bottom"
    android:padding="@dimen/dp_10"
    android:singleLine="true"
    android:textAlignment="inherit" />
    <!--declared in layout: item_spinner.xml-->
    <!-- removed attributes:  android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:textColor="@color/text_grey_light"
               android:textSize="@dimen/sp_14" -->
    <!--style="?android:attr/spinnerItemStyle"-->

步骤2:声明item_spinner_dropdown.xml:
<?xml version="1.0" encoding="utf-8"?>
<com.my_package.custom_views.FontTextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/tv_spinner"
    style="@style/App_TextViewStyleSmall"
    android:layout_gravity="start|bottom"
    android:layout_marginLeft="@dimen/dp_5"
    android:layout_marginStart="@dimen/dp_5"
    android:ellipsize="marquee"
    android:gravity="start|bottom"
    android:padding="@dimen/dp_10"
    android:singleLine="true" />
    <!--declared in layout: item_spinner_dropdown.xml -->
    <!--removed: ?android:attr/dropdownListPreferredItemHeight-->
    <!--style="?android:attr/spinnerDropDownItemStyle"-->

步骤三:在布局中使用旋转器:

<LinearLayout
            android:id="@+id/ll_my_spinner"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/fet_bus_entity"
            android:layout_marginTop="@dimen/dp_12"
            android:orientation="horizontal">

            <com.my_package.custom_views.FontTextView
                style="@style/App_TextViewStyleSmall"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="start|bottom"
                android:gravity="start|bottom"
                android:text="@string/are_you_a" />

            <Spinner
                android:id="@+id/sp_my_spinner"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="@dimen/dp_5"
                android:layout_marginStart="@dimen/dp_5"
                android:layout_gravity="end|bottom"
                android:spinnerMode="dropdown" />
        </LinearLayout>

[注意:FontTextView的id在布局、下拉项和选中项中都是相同的]

步骤4:在Activity/Fragment中使用它:

private void initSpinnerBusinessType(View rootView) {
        String[] ar_dd_bus_type = getResources().getStringArray(R.array.ar_dd_bus_type);
        List<String> lst_bus_type = Arrays.asList(ar_dd_bus_type);
        ArrayList<String> ar_bus_type = new ArrayList<>(lst_bus_type);
        //==

        ArrayAdapter<String> adapter = new ArrayAdapter<>(activity, R.layout.item_spinner, R.id.tv_spinner, ar_bus_type);
        adapter.setDropDownViewResource(R.layout
                .item_spinner_dropdown);
        //=========
        Spinner sp_my_spinner= rootView.findViewById(R.id.sp_my_spinner);
        sp_my_spinner.setAdapter(adapter);
    }

[如需更多指导,请参阅我的其他帖子:https://stackoverflow.com/a/51077569/787399https://dev59.com/kGIk5IYBdhLWcg3wPcCB#22164007]

0
请按照基本的自定义步骤对FontTextView、FontEditView、FontRadioButton、FontCheckBox和FontButton进行定制。
[ 如需确切答案,请查看此指南后访问: https://dev59.com/yG035IYBdhLWcg3wVud1#51113022 ]
在ArrayAdapter项目布局中使用自定义的FontTextView,如下所示:
public class FontEditText extends AppCompatEditText {

//    private String FONT = "fonts/roboto_regular.ttf";

    public FontEditText(Context context) {
        super(context, null);
//        setFontFromAsset(context, null, R.style.DefaultFontTextView);
//        FONT = getContext().getString(R.string.font_roboto_regular);
    }

    public FontEditText(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        setFontFromAsset(context, attrs, R.attr.fetFontStyle);
    }

    public FontEditText(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setFontFromAsset(context, attrs, defStyleAttr);
    }

    private void setFontFromAsset(Context context, AttributeSet attrs, int defStyle) {
        BaseActivity activity = (BaseActivity)((MyApplication) context.getApplicationContext()).getCurrentActivity();
        FontAndLocaleManager fontAndLocaleManager = activity.getFontAndLocaleManager();
        fontAndLocaleManager.setFontFromAsset(this, R.styleable.FontEditText, R.styleable.FontEditText_fetFontFace, attrs, defStyle);
    }
}

使用以下代码:

public void setFontFromAsset(View view, int[] resViewStyleable, int resStyleableViewFontFace, AttributeSet attrs, int defStyle) {
        String strFont = null;
        Typeface tfFontFace = null;
        String strButton = FontButton.class.getCanonicalName(),
                strTextView = FontTextView.class.getCanonicalName(),
                strEditText = FontEditText.class.getCanonicalName(),
                strView = view.getClass().getCanonicalName();
        try {
            if (view.isInEditMode()) {
                return;
            }
            //R.string.font_roboto_regular
            strFont = context.getString(R.string.font_roboto_regular);
            tfFontFace = Typeface.createFromAsset(context.getAssets(), strFont);

            //AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes
            //R.styleable.FontButton
            TypedArray a = context.obtainStyledAttributes(attrs, resViewStyleable, defStyle, 0);
            //R.styleable.FontButton_btFontFace
            String derivedFont = a.getString(resStyleableViewFontFace);

            a.recycle();

            //==
            try {
                if (derivedFont != null) {
                    Typeface derivedFontFace = Typeface.createFromAsset(context.getAssets(), derivedFont);
                    if (strView.equals(strButton)) {
                        ((FontButton) view).setTypeface(derivedFontFace);
                    } else if (strView.equals(strTextView)) {
                        ((FontTextView) view).setTypeface(derivedFontFace);
                    } else if (strView.equals(strEditText)) {
                        ((FontEditText) view).setTypeface(derivedFontFace);
                    }
                    return;
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            if (strFont != null && tfFontFace != null) {
                if (strView.equals(strButton)) {
                    ((FontButton) view).setTypeface(tfFontFace);
                } else if (strView.equals(strTextView)) {
                    ((FontTextView) view).setTypeface(tfFontFace);
                } else if (strView.equals(strEditText)) {
                    ((FontEditText) view).setTypeface(tfFontFace);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

在各自的XML中描述样式和属性:

<!--FontTextView-->
    <declare-styleable name="FontTextViewStyle">
        <!-- Style of the FontTextView. -->
        <attr name="ftvFontStyle" format="reference"/>

    </declare-styleable>
    <declare-styleable name="FontTextView">
        <!-- Font face of FontTextView. -->
        <attr name="ftvFontFace" format="reference"/>
    </declare-styleable>

并且

<!--FontTextView-->
<style name="StyledFontTextView" parent="@android:style/Theme.Light">
<item name="ftvFontStyle">@style/DefaultFontTextView</item>
</style>

<style name="DefaultFontTextView">
<item name="ftvFontFace">@string/font_roboto_regular</item>
</style>

定义一些更多的样式:

<style name="App_TextViewStyle" parent="@android:style/Widget.TextView">
        <item name="android:textColor">@color/text_grey</item>
        <item name="android:textSize">@dimen/sp_20</item>
        <item name="android:layout_width">match_parent</item>
        <item name="android:layout_height">wrap_content</item>
    </style>
    <style name="App_TextViewStyleMedium" parent="@android:style/Widget.TextView">
        <item name="android:textColor">@color/text_hint</item>
        <item name="android:textSize">@dimen/sp_18</item>
        <item name="android:layout_width">match_parent</item>
        <item name="android:layout_height">wrap_content</item>
    </style>
    <style name="App_TextViewStyleSmall" parent="@android:style/Widget.TextView">
        <item name="android:textColor">@color/text_grey_light</item>
        <item name="android:textSize">@dimen/sp_14</item>
        <item name="android:layout_width">match_parent</item>
        <item name="android:layout_height">wrap_content</item>
    </style>

在你的 strings.xml 文件中提到字体:
...
<string name="font_roboto_regular">fonts/roboto_regular.ttf</string>
...

并在布局中使用,节省一些代码和时间:

<com.mypackage.custom_views.FontTextView
                style="@style/App_TextViewStyleMedium"
                android:layout_gravity="start|bottom"
                android:gravity="start|bottom"
                app:fetFontFace="@string/font_roboto_regular"
                android:text="@string/are_you_a" />

在 Android 16 及以上版本中,所有这些都变得更简单了,因为现在您可以将 TTF 和其他字体资源保存在 /res/font 文件夹中,而不是在 assets 中。这消除了大部分自定义类、样式和属性,详情请参见:

Android 中的字体资源

祝编码愉快!:-)


长篇回答,但只需一次性工作。之后这就是可重复使用的。 - Abhinav Saxena

-1

大家好,我找到了一个很棒的解决方案,我通过助手包装原始适配器,像这样:

使用这个类SpinnerViewHelper,愉快地进行Android编程吧。

new SpinnerViewHelper((Spinner)view.findViewById(R.id.labelSurveyNumber),(parent, v, position, id) -> UrduFontHelper.set(v));

使用Lambda表达式。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接