web-dev-qa-db-fra.com

Comment utiliser une police de caractères personnalisée dans un widget?

J'ai un widget d'horloge numérique. Comment puis-je utiliser une police personnalisée à partir d'actifs/polices comme police par défaut dans la vue de texte affichant l'horloge?

Voici mon 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();
            }
        };
    }
}
39
tristan202

Ce qui est nécessaire est de rendre la police sur un canevas, puis de la transmettre à un bitmap et de l'attribuer à une ImageView. Ainsi:

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

C'est la partie qui fait la police de l'image, et voici comment l'utiliser:

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

Comme vous le remarquerez peut-être, ce code affiche simplement l'heure actuelle dans la vue d'image, mais il peut facilement être ajusté en fonction des besoins.

Modifier:

ARGB_4444 est déconseillé pour ARGB_8888 comme indiqué dans le documentation

Ce champ est obsolète au niveau de l'API 13. En raison de la mauvaise qualité de cette configuration, il est conseillé d'utiliser à la place ARGB_8888.

60
tristan202

J'ai un peu changé la taille de la mesure, donc le bitmap supportera différentes tailles de police. Il prend simplement en charge le texte sur une seule ligne.

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

Cela rend la police à un bitmap, puis affecte le bitmap à une 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;
}
8
tristan202

Cette solution créera un bitmap qui est la taille exacte requise pour s'adapter au texte.

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

Comme mentionné ailleurs, le bitmap peut ensuite être affecté à ImageView d'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);

Cela a l'avantage de produire un bitmap qui ne sous-dépassera ni ne dépassera jamais le texte qu'il devrait contenir, ce qui est important pour les widgets car leurs dimensions varient entre les appareils et les versions.

3
Tom

Il n'y a aucun moyen de définir votre police personnalisée sur des vues distantes car vous n'y avez pas directement accès (ce n'est pas la vue de votre application).

Mais vous pouvez créer un textView avec tous les attributs que vous souhaitez par programme, puis le convertir en Bitmap et le définir en ImageView au lieu de votre TextView.

Voici un exemple de code que j'ai utilisé auparavant:

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)
    }
0
Ehsan

Je ne le trouve pas maintenant, mais j'ai posé la même question et obtenu une réponse de Google que ce n'est pas possible. Vous êtes vraiment limité dans ce que vous pouvez faire avec AppWidgets, par exemple, vous ne pouvez utiliser que certains widgets d'interface utilisateur et vous ne pouvez pas utiliser de polices personnalisées.

0
Mark B