¿Cómo usar un tipo de letra personalizado en un widget?

Tengo un widget de reloj digital. ¿Cómo puedo usar una fuente personalizada de assets / fonts como fuente predeterminada en la vista de texto que muestra el reloj?

Este es mi código:

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(); } }; } } 

Lo que se necesita es renderizar la fuente en un canvas y luego pasarla a un bitmap y asignarla a un ImageView. Al igual que:

 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; } 

Esa es la parte que hace la fuente a la imagen thingie, y esta es la forma de usarlo:

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

Como puede observar, este código solo muestra la hora actual en la vista de la imagen, pero se puede ajustar fácilmente a cualquier necesidad.

Editar:

ARGB_4444 está en desuso para ARGB_8888 como se indica en la documentación

Este campo quedó obsoleto en el nivel de API 13. Debido a la mala calidad de esta configuración, se recomienda usar ARGB_8888 en su lugar.

Cambié un poco el tamaño de la medida, por lo que el bitmap admitirá diferentes tamaños de letra. Solo admite texto de una sola línea.

 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_8888); 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; } 

Esto representa la fuente en un bitmap y luego asigna el bitmap a un 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; } 

Esta solución creará un bitmap que es el tamaño exacto requerido para ajustarse al texto.

 /** * 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; } 

Como se mencionó en otra parte, el bitmap se puede asignar a ImageView de un widget.

 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); 

Esto tiene la ventaja de producir un bitmap que nunca rebasará o rebasará el texto que debería contener, lo cual es importante para los widgets, ya que sus dimensiones varían entre dispositivos y versiones.

No puedo encontrarlo ahora, pero hice la misma pregunta y obtuve una respuesta de Google de que esto no es posible. Está realmente restringido en lo que puede hacer con AppWidgets, por ejemplo, solo puede usar ciertos widgets de UI y no puede usar fonts personalizadas.

 remoteviews.setTextViewText(textview_id, new SpannableStringBuilder(Html.fromHtml(""+some_text_vaeiable"+"")) ); 

Así es como lo hago en mis aplicaciones, también debería aplicarse a los widgets.

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