- PVSM.RU - https://www.pvsm.ru -

Раскрашиваем значки от гугла

Дизайн приложения — очень важная часть разработки. Google значительно его упрощает, предоставляя в свободном доступе около 150 готовых значков, заготовленных под разную плотность пикселей. Однако по умолчанию они серого цвета. Сделано это специально для того, чтобы дизайнер сам раскрасил их. Но если мы хотим просто придать значкам некоторый цвет — нет смысла редактировать каждый отдельно. Проще написать скрипт, который сделает работу за нас.

В этой статье будет представлена небольшая программка на Java, которая автоматически раскрашивает все значки в заданный цвет, алгоритм, по которому она работает, а также — архив уже раскрашенных значков, которые вы сразу можете использовать в своих приложениях.

Сразу оговорюсь — вполне возможно, что средствами Adobe Illustrator можно сделать всё то, о чём написано в статье и уже есть готовые скрипты. Но его нет под линуксом и вообще он дорого стоит. Думаю, что пользователи Linux и сторонники свободного ПО оценят мой подход.

Раскрашиваем значки от гугла

Вступление

На сайте developer.android.com [1] любой желающий может скачать архив с ресурсами для дизайна, которые, по заявлению в NOTICE.TXT, можно использовать без каких-либо ограничений. Среди них есть значки Action Bar Icons. Давайте рассмотрим их более подробно. Есть два набора — для тем Holo Light и Holo Dark. Соответственно, для тёмной темы значки белые, а для белой — серые. Насколько я понял, исходники для значков находятся в формате Adobe Illustrator (*.ai). Но для меня эта программа слишком дорого стоит, да и вообще я сторонник свободного ПО. Так что мы будем раскрашивать уже заготовленные png файлы, благо их размер уже скорректирован под разную плотность пикселей.

А зачем?

Разумеется, крупной компании будет лучше нанять профессионального дизайнера, который нарисует уникальные иконочки с нуля. Или же доработает существующие. Однако если делать небольшое приложение на заказ — нет смысла вводить новый дизайн. Пользователи Android привыкли к стандартному набору значков, к их очертаниям. Мне кажется, что стоит просто придать этим очертаниям цвет и порадовать пользователя.

Но главная польза от таких значков — когда программист делает программу для себя, и у него нет желания и времени заниматься дизайном. Хочется, чтобы программа выглядела как у всех, но при этом предоставляла какие-то свои уникальные функции. Например, если вы начинающий программист и изучаете Android — будет весьма полезно параллельно с обучением разрабатывать какую-нибудь свою программу и продемонстрировать в ней полученные знания. При этом с раскрашенными значками она будет выглядеть значительно лучше.

Что мы имеем

Рассмотрим серый набор значков более подробно. Для каждого значка есть четыре версии, соответствующие разной плотности пикселей. Вы в программе всего лишь используете конечное название — например R.id.ic_action_important, и Android сам выбирает нужную папку. Это очень удобно и выглядит потрясающе в силу того, что на экранах для мобильных устройств ppi значительно выше, чем на обычном мониторе. Изображение кажется более сглаженным.

Кстати говоря, значки довольно универсальны и их можно использовать не только в Action Bar.

Первая проблема, с которой я столкнулся — нет общего обзора всех значков. Двадцать минут программирования — и уже готов Java-код, который пробегается по каталогам и считывает от туда значки, склеивая их в одну общую картинку. Вот она:

Раскрашиваем значки от гугла

Теперь же — попробуем их раскрасить. Я буду использовать для этих целей десктопную Java. Как бы её не ругали, она предоставляет множество готовых и работающих решений. Будем использовать awt — библиотеки для работы с 2D графикой. Первое, что я сделал — подгрузил картиночку в BufferedImage и прошёлся по каждому пикселю, выделив его 4 составляющие (ARGB). Беглый анализ вывода показал, что картинка по сути использует только один цвет — серый. И в тех местах где само изображение сглажено — просто уменьшен альфа канал.

Исходя из этого, чтобы раскрасить значок — нам нужно всего лишь заменить серый цвет на необходимый, без затрагивания альфа канала. Я попробовал взять звёздочку и раскрасить её в синий цвет, а потом протестировать на моём устройстве — и она отлично там смотрелась!

Программа

Поработав ещё некоторое время, я сделал такой скрипт, который перегоняет все значки из библиотеки в заданный цвет. Правда в каталоге должны быть только значки и папки — ничего лишнего.

Думаю, что большинство Android-разработчиков имело хотя бы небольшой опыт с десктопной Джавой — компиляция и запуск не вызовет трудностей. В принципе, тут всё просто.

Main.java

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class Main {
    static final int COLUMNS_COUNT = 15;
    static final String DIR_WITH_GRAY_IMAGES = "E:\wsys\prj\ImagePainter\test\gray";
    static final String OUTPUT_DIR = "E:\wsys\prj\ImagePainter\test";
    static final Color myColor = new Color(177, 0, 140);//Цвет, в который перекрашиваем

    static List<File> fileList = new ArrayList<>();

    public static void main(String[] strings) throws Throwable {
        painter();
    }

    private static void outputImages(File dir) {
        for (File subDir : dir.listFiles()) {
            if (subDir.getName().contains("hdpi")) {
                File pngFile = subDir.listFiles()[0];
                fileList.add(pngFile);
                break;
            }
            if (subDir.isDirectory()) {
                outputImages(subDir);
            }
        }
    }

    private static void painter() throws Throwable {
        File dirWithImages = new File(DIR_WITH_GRAY_IMAGES);
        File outputDir = new File(OUTPUT_DIR);
        String nameFolder = Integer.toHexString(myColor.getRGB());

        for (File subDir : dirWithImages.listFiles()) {
            for (File drawable : subDir.listFiles()) {
                System.out.println(drawable);
                for (File pngFile : drawable.listFiles()) {
                    System.out.println(pngFile);
                    BufferedImage okImage = handlePngFile(pngFile, myColor);
                    String partPath = pngFile.getAbsolutePath().replace(dirWithImages.getAbsolutePath(), "");
                    File newPngPath = new File(outputDir.getAbsolutePath() + "\" + nameFolder + "\" + partPath);
                    newPngPath.mkdirs();
                    ImageIO.write(okImage, "png", newPngPath);
                }
            }
        }

        File targetDir = new File(outputDir, "\" + nameFolder);
        outputImages(targetDir);
        ImageIO.write(getComplexImage(), "png", new File(targetDir, "all.png"));
    }

    private static BufferedImage getComplexImage() {
        BufferedImage bufferedImage = new BufferedImage(800, 500, BufferedImage.TYPE_INT_ARGB);
        Graphics g = bufferedImage.getGraphics();

        int i = 0;
        int j = 0;
        int counter = 0;
        try {
            for (File pngFile : fileList) {
                BufferedImage image = ImageIO.read(pngFile);
                g.drawImage(image, i, j, null);
                i += image.getWidth();
                counter++;
                if (counter == COLUMNS_COUNT) {
                    i = 0;
                    counter = 0;
                    j += image.getHeight();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bufferedImage;
    }

    private static BufferedImage handlePngFile(File pngFile, Color myColor) {
        try {
            BufferedImage image = ImageIO.read(pngFile);

            for (int i = 0; i < image.getWidth(); i++) {
                for (int j = 0; j < image.getHeight(); j++) {
                    Color pixelColor = new Color(image.getRGB(i, j), true);

                    int a = pixelColor.getAlpha();
                    //a += 50;//Повышаем интенсивность - уменьшаем прозрачность

                    if (pixelColor.getRed() != 0) {
                        int r = myColor.getRed();
                        int g = myColor.getGreen();
                        int b = myColor.getBlue();
                        Color newColor = new Color(r, g, b, a);
                        image.setRGB(i, j, newColor.getRGB());
                    }
                }
            }
            return image;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

Архив и заключение

Выложил [2] уже перекрашенные в разные цвета значки — пользуйтесь на здоровье! Или же можете разобраться со скриптом и перекрасить в тот цвет, который нужен конкретно вам. И забывайте о том, что все значки с альфа каналом, соответственно на белом фоне они будут выглядеть более светлыми, чем тот цвет, который вы задали для них.

Кроме того, возникла идея написать скрипт, который существующий большой значок меняет под разные размеры со сглаживанием и распихивает по папкам для разной плотности пикселей. Если нет готовых и бесплатных скриптов, которые это делают в автоматическом режиме — обязательно напишу свой и выложу.

Автор: kciray

Источник [3]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/java/62486

Ссылки в тексте:

[1] developer.android.com: https://developer.android.com/design/downloads/index.html

[2] Выложил: http://kciray.com/files/zip/Action_Bar_Icon_Pack_Colored.zip

[3] Источник: http://habrahabr.ru/post/226307/