вторник, 29 марта 2011 г.

А не сделать ли что-то полезное?.. Туториал!

Не забецать ли такие вот часики:
Сказано - сделано; делаем цифровые часы.
Смысл этого туториала в том, чтобы показать, как использовать в виджете поменьше ресурсов и при этом нарисовать что-то симпатичное; к примеру тут цифры белые и красные - в каком-то из виджетов (очень похожем на этот по конструкции, минуты перекрывают часы) использовались отдельно картинки для белых цифр, отдельно для синих, собственно можно было использовать единственный вариант расцветки, тут я это "пофикшу"
Для начала надо надизайнить макет, в фотошопе/гимпе я беру скриншот с эмулятора, ограничиваю пространство для виджета (он будет 2*2 клетки) и рисую собственно вот ту картинку выше. Шрифт Gota 100 пунктов. Пусть это все, ничего лишнего я добавлять не буду - надо подготовить изображение, которое будет использоваться для вывода цифр
Я сделал так: в отдельный файл я впечатал в колонку цифры от 0 до 9, черного цвета; в свойствах слоя с текстом я добавил наружную обводку, красного цвета, 5 пикселей размером. Теперь я добавляю пустой слой, размещаю его выше слоя с текстом, выделяю оба слоя и нажимаю Ctrl-E - слои обьединяются и от текста не остается и следа - теперь это просто картинка; если все делать как я, то слой должен быть прозрачным, с черными буквами с красной обводкой, где-то посредине "листа.

По "замыслу" цифры должны быть "контурные", поэтому надо инструментом Magic Wand (Anti-Alias вкл, Tolerance 0) повыделять внутренности букв и поудалять выделенное - остануться только красные контуры, если все нормально

Почти готово, за исключением того, что расстояние между буквами неприлично неправильное, надо сместить все буквы друг к дружке и обрезать файл; я например сделал так - по самой большой букве (ну вообще этот шрифт моноширинный, но в общем случае) выделил габарит будущего символа, разметил место для 10 символов и поперетаскивал все цифры так, чтобы они помещались ровно в центре своих ячеек. Не знаю, понятна ли мысль и как это проиллюстировать, вот что-то такое должно получится в процессе:
Файл сохраняем в 24-битный png с прозрачностью с именем test_digits; собственно подготовка все
Дальше надо начать новый проект, добавить в его ресурсы файл с цифрами, сделать заготовку виджета 2*2манифест и xml с описанием) и файл разметки, пусть он пока выглядит так:



    

Т.е. просто выводится картинка, все.
Ну и код для рисования (все упрощено до минимума, часы обновляться не будут, смысл - показать, как сделать картинку):
package com.example;

import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.*;
import android.widget.RemoteViews;

import java.util.Calendar;

public class DigiWidget extends AppWidgetProvider {

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {

        Calendar curDateTime = Calendar.getInstance(); // это текущее время/дата
        // вообще нам надо знать только часы и минуты сейчас..
        final int hh = curDateTime.get(Calendar.HOUR);
        final int mm = curDateTime.get(Calendar.MINUTE);

        Bitmap bmp = null; // а вот это - будущий "циферблат"

        Resources r = context.getResources(); // так мы будем пользоваться ресурсами

        final Bitmap digits = BitmapFactory.decodeResource(r, R.drawable.test_digits);
        final int w = digits.getWidth();
        final int h = digits.getHeight() / 10;
        // ^^ я загрузил подготовленную картинку с цифрами и узнал ее размер

        // создаю битмап, по которому буду "рисовать"; чтобы не терять детали, надо использовать ARGB8888
        // при его создании, "тру" 32-битный цвет
        bmp = Bitmap.createBitmap(w * 2, h * 2, Bitmap.Config.ARGB_8888);
        // а это - "холст"; по нему можно "мазюкать" кисточками/перьями/штампами etc
        // все, что намазюкаю в меру своего художественного таланта отобразится в битмап
        Canvas canvas = new Canvas(bmp);

        // этот обьект используется для настройки рисования по холсту; например..
        Paint p = new Paint();

        // сейчас я скажу, что все что рисуется должно окрашиваться в белый цвет
        // вот и наши белые буквы, для которых нет отдельных картинок ;)
        p.setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_ATOP));
        p.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));

        // тут я вывожу цифры для часов; координаты холста меняются снизу вверх слева направо
        // так что у первой цифры (десятки часов) координата всегда (0, 0), у второй (единицы
        // часов) - (w, 0), ц - это ширина каждой цифры
        // drawBitmap рисует по холсту битмап, а точнее он вырезает из битмапа digits
        // часть картинки, которая ограничивается прямоугольником, чьи размеры передаются 2-м параметром,
        // и кладет этот вырезанный кусок на холст в прямоугольник, указанный в 3-м параметре
        // при этом используются настройки, которые передаются в 4-м параметре (в этом случае
        // все окрашивается в белый цвет)
        int dh = hh / 10;
        canvas.drawBitmap(digits, new Rect(0, dh * h, w, (dh + 1) * h), new Rect(0, 0, w, h), p);
        dh = hh % 10;
        canvas.drawBitmap(digits, new Rect(0, dh * h, w, (dh + 1) * h), new Rect(w, 0, 2 * w, h), p);

        int dy = 60; // экспериментально/дизайнерски подобранная цифра -
        // насколько минуты "перехлещиваются" с цифрами

        // минуты я хочу вывести.. СИНИМ цветом, цифры лягут поверх часов (можно задавать и
        // другие варианты наложения
        p.setColorFilter(new PorterDuffColorFilter(Color.BLUE, PorterDuff.Mode.SRC_ATOP));
        p.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
        dh = mm / 10;
        canvas.drawBitmap(digits, new Rect(0, dh * h, w, (dh + 1) * h), new Rect(0, dy, w, dy + h), p);
        dh = mm % 10;
        canvas.drawBitmap(digits, new Rect(0, dh * h, w, (dh + 1) * h), new Rect(w, dy, 2 * w, dy + h), p);

        digits.recycle(); // освобождаем битмап сразу же, чтобы не накапливать память -
        // когда там его увидит/освободит сборщик мусора

        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.test);

        for (int appWidgetId : appWidgetIds) {

            views.setImageViewBitmap(R.id.idTestImage, bmp);
            appWidgetManager.updateAppWidget(appWidgetId, views);
        }
    }
}
Не без глюка (при "старте" виджета выводится на мгновение картинка левая, лень убирать из xml разметки  ссылку на картинку), но получаем следующее (слева - работающий виджет):
Если ничего не накосячил - вот архив с проектом: скачать быстро и без рекламы :)

1 комментарий: