デジタル時計ウィジェットがあります。時計を表示するテキストビューのデフォルトのフォントとして、assets/fontsのカスタムフォントを使用するにはどうすればよいですか?
これは私のコードです:
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();
}
};
}
}
必要なのは、フォントをキャンバスにレンダリングし、それをビットマップに渡して、それを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));
お気づきかもしれませんが、このコードはイメージビューに現在の時刻を表示するだけですが、必要に応じて簡単に調整できます。
編集:
ドキュメント に記載されているように、ARGB_4444はARGB_8888に対して非推奨です。
このフィールドはAPIレベル13で廃止されました。この構成は品質が低いため、代わりにARGB_8888を使用することをお勧めします。
小節のサイズについて少し変更したので、ビットマップは異なるフォントサイズをサポートします。 1行のテキストのみをサポートします。
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;
}
これにより、フォントがビットマップにレンダリングされ、ビットマップが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;
}
このソリューションは、テキストに合わせるために必要な正確なサイズであるビットマップを作成します。
/**
* 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;
}
他の場所で述べたように、ビットマップはウィジェットの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);
これには、含まれるテキストをアンダーシュートまたはオーバーシュートしないビットマップを生成するという利点があります。これは、ウィジェットの寸法がデバイスやバージョンによって異なるため、ウィジェットにとって重要です。
カスタムフォントに直接アクセスできないため、リモートビューにカスタムフォントを設定する方法はありません(アプリケーションのビューではありません)。
ただし、プログラムで必要なすべての属性を含むtextViewを作成し、それをビットマップに変換して、TextViewではなくImageViewに設定することができます。
これは、以前使用したサンプルコードです。
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)
}
現在それを見つけることができませんが、同じ質問をしたところ、これは不可能であるというGoogleからの回答を得ました。 AppWidgetでできることは本当に制限されています。たとえば、特定のUIウィジェットしか使用できず、カスタムフォントは使用できません。