Reputation: 1139
I have a digital clock widget. How can I use a custom font from assets/fonts as the default font in the textview showing the clock?
This is my code:
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();
}
};
}
}
Upvotes: 44
Views: 29661
Reputation: 1236
Since Android 8 (SDK 26
) you can use Downloadable Fonts but from set of Google Fonts only.
Just create a FontFamily
via Android Studio layout editor:
<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:android="http://schemas.android.com/apk/res/android"
android:fontProviderAuthority="com.example.fontprovider.authority"
android:fontProviderPackage="com.example.fontprovider"
android:fontProviderQuery="example font"
android:fontProviderCerts="@array/certs">
</font-family>
and use it via attribute android:fontFamily
in a layout for your app widget.
Upvotes: 0
Reputation: 1139
What is needed is to render the font onto a canvas, and then pass it on to a bitmap and assign that to an ImageView. Like so:
public Bitmap buildUpdate(String time)
{
Bitmap myBitmap = Bitmap.createBitmap(160, 84, Bitmap.Config.ARGB_8888);
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;
}
That's the part doing the font to image thingie, and this is how to use it:
String time = (String) DateFormat.format(mTimeFormat, mCalendar);
RemoteViews views = new RemoteViews(getPackageName(), R.layout.main);
views.setImageViewBitmap(R.id.TimeView, buildUpdate(time));
As you might notice, this code just shows the current time in the imageview, but it can easily be adjusted to whatever needs.
Edit:
ARGB_4444 is deprecated for ARGB_8888 as stated in the documentation
This field was deprecated in API level 13. Because of the poor quality of this configuration, it is advised to use ARGB_8888 instead.
Upvotes: 65
Reputation: 853
I was facing the same problem, but I didn't wanted to use bitmap and canvas-like everyone is saying. Because I don't know how they work and how to handle them.
so this is what I did. I created multiple text views inside my widget layout file and set each text view a different font style using android:fontFamily and then set the visibility of all text views to "gone".
And at run time in the onUpdate method of appWidgetProvider class. I set the visibility of text view to Visible (of the text view which I wanted or which font I wanted to display to the user.
And to set the visibility dynmatically I used remoteViews.setViewVisibility(R.id.hadees_tv2, View.VISIBLE)
Upvotes: 0
Reputation: 2781
There is no way to set your custom font to remote views because you don't have direct access to them(they are not your application's view).
But you can create a textView will all of the attributes that you want programmatically, Then convert it to a Bitmap and set it to an ImageView instead of your TextView.
This is a sample code that I used before:
private fun createTextBitmap(text: String, typeface: Typeface, textSize: Float, textColour: Int): Bitmap? {
val textView = TextView(mService)
textView.isDrawingCacheEnabled = true
textView.text = text
textView.typeface = typeface
textView.setTextColor(textColour)
textView.textSize = textSize
textView.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT)
textView.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED))
textView.layout(0, 0, textView.measuredWidth, textView.measuredHeight)
return textView.getDrawingCache(true)
}
Upvotes: 1
Reputation: 1457
I changed a little about measure size, so the bitmap will support different fontsize. It just support single line text.
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;
}
Upvotes: 18
Reputation: 7824
This solution will create a bitmap that is the exact size required to fit the text.
/**
* 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;
}
As mentioned elsewhere, the bitmap can then be assigned to a widget's 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);
This has the advantage of producing a bitmap that will never undershoot or overshoot the text it should contain, which is important for widgets as their dimensions vary between devices and versions.
Upvotes: 6
Reputation: 1
remoteviews.setTextViewText(textview_id, new SpannableStringBuilder(Html.fromHtml("<b>"+some_text_vaeiable"+"</b>")) );
Upvotes: -4
Reputation: 1139
This renders the font to a bitmap, and then assigns the bitmap to an 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;
}
Upvotes: 10
Reputation: 13060
This is how I do it in my applications, should apply to widgets as well.
Typeface customfont = Typeface.createFromAsset(getAssets(), "fonts/somefont.ttf");
TextView textview = (TextView) findViewById(R.id.textview);
textview.setTypeface(customfont);
textview.setText("Hey custom font!");
Upvotes: -17
Reputation: 201088
I can't find it now, but I asked the same question and got a response from Google that this isn't possible. You are really restricted in what you can do with AppWidgets, for example you can only use certain UI widgets and you can't use custom fonts.
Upvotes: 0