如何在小部件中使用自定义字体?

我有一个数字时钟部件。 我如何使用资产/字体的自定义字体作为显示时钟的文本视图中的默认字体?

这是我的代码:

package android.tristan.widget.digiclock; import java.util.Calendar; import android.app.PendingIntent; import android.app.Service; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProvider; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.IBinder; import android.os.Vibrator; import android.text.format.DateFormat; import android.widget.RemoteViews; public class DigiClock extends AppWidgetProvider { @Override public void onDisabled(Context context) { super.onDisabled(context); context.stopService(new Intent(context, UpdateService.class)); } public void onReceive(Context context, Intent intent) { super.onReceive(context, intent); if(intent.getAction().equals("android.tristan.widget.digiclock.CLICK")) { Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); vibrator.vibrate(50); final Intent alarmClockIntent = new Intent(Intent.ACTION_MAIN, null); alarmClockIntent.addCategory(Intent.CATEGORY_LAUNCHER); final ComponentName cn = new ComponentName("com.android.deskclock", "com.android.deskclock.AlarmClock"); alarmClockIntent.setComponent(cn); alarmClockIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(alarmClockIntent); } if(intent.getAction().equals("android.tristan.widget.digiclock.CLICK_2")) { Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); vibrator.vibrate(50); final Intent calendarIntent = new Intent(Intent.ACTION_MAIN, null); calendarIntent.addCategory(Intent.CATEGORY_LAUNCHER); final ComponentName cn = new ComponentName("com.android.calendar", "com.android.calendar.LaunchActivity"); calendarIntent.setComponent(cn); calendarIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(calendarIntent); } } @Override public void onEnabled(Context context) { super.onEnabled(context); context.startService(new Intent(UpdateService.ACTION_UPDATE)); } @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { super.onUpdate(context, appWidgetManager, appWidgetIds); context.startService(new Intent(UpdateService.ACTION_UPDATE)); final int Top = appWidgetIds.length; final int Bottom = appWidgetIds.length; for (int i=0; i<Top; i++) { int[] appWidgetId = appWidgetIds; RemoteViews top=new RemoteViews(context.getPackageName(), R.layout.main); Intent clickintent=new Intent("android.tristan.widget.digiclock.CLICK"); PendingIntent pendingIntentClick=PendingIntent.getBroadcast(context, 0, clickintent, 0); top.setOnClickPendingIntent(R.id.TopRow, pendingIntentClick); appWidgetManager.updateAppWidget(appWidgetId, top); } for (int i=0; i<Bottom; i++) { int[] appWidgetId = appWidgetIds; RemoteViews bottom=new RemoteViews(context.getPackageName(), R.layout.main); Intent clickintent=new Intent("android.tristan.widget.digiclock.CLICK_2"); PendingIntent pendingIntentClick=PendingIntent.getBroadcast(context, 0, clickintent, 0); bottom.setOnClickPendingIntent(R.id.BottomRow, pendingIntentClick); appWidgetManager.updateAppWidget(appWidgetId, bottom); } } public static final class UpdateService extends Service { static final String ACTION_UPDATE = "android.tristan.widget.digiclock.action.UPDATE"; private final static IntentFilter sIntentFilter; private final static String FORMAT_12_HOURS = "h:mm"; private final static String FORMAT_24_HOURS = "kk:mm"; private String mTimeFormat; private String mDateFormat; private String mDayFormat; private Calendar mCalendar; static { sIntentFilter = new IntentFilter(); sIntentFilter.addAction(Intent.ACTION_TIME_TICK); sIntentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED); sIntentFilter.addAction(Intent.ACTION_TIME_CHANGED); } @Override public void onCreate() { super.onCreate(); reinit(); registerReceiver(mTimeChangedReceiver, sIntentFilter); } @Override public void onDestroy() { super.onDestroy(); unregisterReceiver(mTimeChangedReceiver); } @Override public void onStart(Intent intent, int startId) { super.onStart(intent, startId); if (ACTION_UPDATE.equals(intent.getAction())) { update(); } } @Override public IBinder onBind(Intent intent) { return null; } private void update() { mCalendar.setTimeInMillis(System.currentTimeMillis()); final CharSequence time = DateFormat.format(mTimeFormat, mCalendar); final CharSequence date = DateFormat.format(mDateFormat, mCalendar); final CharSequence day = DateFormat.format(mDayFormat, mCalendar); RemoteViews views = new RemoteViews(getPackageName(), R.layout.main); views.setTextViewText(R.id.Time, time); views.setTextViewText(R.id.Day, day); views.setTextViewText(R.id.Date, date); ComponentName widget = new ComponentName(this, DigiClock.class); AppWidgetManager manager = AppWidgetManager.getInstance(this); manager.updateAppWidget(widget, views); } private void reinit() { mDayFormat = getString(R.string.day_format); mDateFormat = getString(R.string.date_format); mTimeFormat = is24HourMode(this) ? FORMAT_24_HOURS : FORMAT_12_HOURS; mCalendar = Calendar.getInstance(); } private static boolean is24HourMode(final Context context) { return android.text.format.DateFormat.is24HourFormat(context); } private final BroadcastReceiver mTimeChangedReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); if (action.equals(Intent.ACTION_TIME_CHANGED) || action.equals(Intent.ACTION_TIMEZONE_CHANGED)) { reinit(); } update(); } }; } } 

所需要的是将字体渲染到canvas上,然后将其传递给位图并将其分配给ImageView。 像这样:

 public Bitmap buildUpdate(String time) { Bitmap myBitmap = Bitmap.createBitmap(160, 84, Bitmap.Config.ARGB_4444); Canvas myCanvas = new Canvas(myBitmap); Paint paint = new Paint(); Typeface clock = Typeface.createFromAsset(this.getAssets(),"Clockopia.ttf"); paint.setAntiAlias(true); paint.setSubpixelText(true); paint.setTypeface(clock); paint.setStyle(Paint.Style.FILL); paint.setColor(Color.WHITE); paint.setTextSize(65); paint.setTextAlign(Align.CENTER); myCanvas.drawText(time, 80, 60, paint); return myBitmap; } 

这是做字体的部分,以图像thingie,这是如何使用它:

 String time = (String) DateFormat.format(mTimeFormat, mCalendar); RemoteViews views = new RemoteViews(getPackageName(), R.layout.main); views.setImageViewBitmap(R.id.TimeView, buildUpdate(time)); 

正如你可能注意到的,这段代码只是在imageview中显示当前的时间,但它可以很容易地根据需要进行调整。

编辑:

如文档中所述,ARGB_4444已弃用ARGB_8888

此字段在API级别13中已弃用。由于此configuration的质量较差,build议使用ARGB_8888。

我改变了一些有关度量大小,所以位图将支持不同的字体大小。 它只支持单行文本。

 public static Bitmap getFontBitmap(Context context, String text, int color, float fontSizeSP) { int fontSizePX = convertDiptoPix(context, fontSizeSP); int pad = (fontSizePX / 9); Paint paint = new Paint(); Typeface typeface = Typeface.createFromAsset(context.getAssets(), "Fonts/Roboto-Regular.ttf"); paint.setAntiAlias(true); paint.setTypeface(typeface); paint.setColor(color); paint.setTextSize(fontSizePX); int textWidth = (int) (paint.measureText(text) + pad * 2); int height = (int) (fontSizePX / 0.75); Bitmap bitmap = Bitmap.createBitmap(textWidth, height, Bitmap.Config.ARGB_4444); Canvas canvas = new Canvas(bitmap); float xOriginal = pad; canvas.drawText(text, xOriginal, fontSizePX, paint); return bitmap; } public static int convertDiptoPix(Context context, float dip) { int value = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, context.getResources().getDisplayMetrics()); return value; } 

这将呈现字体到一个位图,然后将位图分配给一个ImageView。

 public static RemoteViews buildUpdate(Context context) { RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.main); Bitmap myBitmap = Bitmap.createBitmap(100, 50, Bitmap.Config.ARGB_4444); Canvas myCanvas = new Canvas(myBitmap); Paint paint = new Paint(); Typeface clock = Typeface.createFromAsset(context.getAssets(),"Clockopia.ttf"); paint.setAntiAlias(true); paint.setSubpixelText(true); paint.setTypeface(clock); paint.setStyle(Paint.Style.FILL); paint.setColor(Color.BLACK); paint.setTextSize(15); myCanvas.drawText("Test", 0, 20, paint); views.setImageViewBitmap(R.id.TimeView, myBitmap); return views; } 

此解决scheme将创build一个位图,该位图是适合文本所需的确切大小。

 /** * Creates and returns a new bitmap containing the given text. */ public static Bitmap createTextBitmap(final String text, final Typeface typeface, final float textSizePixels, final int textColour) { final TextPaint textPaint = new TextPaint(); textPaint.setTypeface(typeface); textPaint.setTextSize(textSizePixels); textPaint.setAntiAlias(true); textPaint.setSubpixelText(true); textPaint.setColor(textColour); textPaint.setTextAlign(Paint.Align.LEFT); Bitmap myBitmap = Bitmap.createBitmap((int) textPaint.measureText(text), (int) textSizePixels, Bitmap.Config.ARGB_8888); Canvas myCanvas = new Canvas(myBitmap); myCanvas.drawText(text, 0, myBitmap.getHeight(), textPaint); return myBitmap; } 

正如其他地方所提到的,位图可以被分配到一个widget的ImageView。

 final Bitmap textBitmap = createTextBitmap(text, FontManager.get().getTypeface("slab-serif", 0), context.getResources().getDimension(R.dimen.widget_font_size_large), context.getResources().getColor(R.color.widget_text) ); views.setImageViewBitmap(R.id.widget_cardTextImage, textBitmap); 

这具有生成位图的优点,该位图将永远不会下冲或超过它应该包含的文本,这对于小部件是重要的,因为它们的尺寸在设备和版本之间变化。

我现在找不到,但我问了同样的问题,得到了Google的回复,这是不可能的。 您在使用AppWidgets时可能会受到的限制,例如,您只能使用某些UI小部件,而且不能使用自定义字体。

 remoteviews.setTextViewText(textview_id, new SpannableStringBuilder(Html.fromHtml("<b>"+some_text_vaeiable"+"</b>")) ); 

这是我在我的应用程序中的做法,也应该适用于小部件。

 Typeface customfont = Typeface.createFromAsset(getAssets(), "fonts/somefont.ttf"); TextView textview = (TextView) findViewById(R.id.textview); textview.setTypeface(customfont); textview.setText("Hey custom font!");