Android nine-patch — растягиваем андроида

    nine-patchРекомендации по созданию UI виджетов и некоторых блоков приложений для ОС Android предписывают нам использовать блоки с закруглёнными углами и/или с эффектом объема, отбрасывающие тени. Какие же инструменты нам дает SDK для реализации таких интерфейсов?
    При описании простых векторных форм без дополнительного оформления углов и краев блока рекомендуется использовать shape формы. Если в блоке имеются сложные графические элементы по углам или с краев блока можно использовать Nine-patch изображения, о которых и пойдет речь в этой статье. На случай сложного оформления блока, например в виде единого объекта, остается использовать только фиксированное изображение, что уменьшит рамки масштабирования и не даст изменить соотношение сторон блока.

    Итак, что же такое 9-path? Фактически это обычное изображение (.png), в котором края размером в 1 пиксел содержат некую служебную информацию, которую может интерпретировать графическая подсистема Android.
    Эта служебная информация делится на две составляющие: информация для корректного масштабирования блока и информация об области размещения контента в блоке. При отсутствии первой – изображение будет масштабироваться целиком (всегда нужно указывать и горизонтальную и вертикальную полосы масштабирования, или не указывать обе), второй – контент будет размещаться по всей площади изображения.

    Теперь о том, как задается эта информация. Верхняя и левая часть рамки задают масштабируемые области, правая и нижняя – область отображения контента. При этом прозрачные пикселы обозначают неотмеченную область, а черные (#000000) – отмеченную. Подробнее на картинке.



    Теперь попробуем сделать свое 9-patch изображение. Как я писал выше, это должна быть рамка со сложными углами и/или краями, поэтому я нагуглил такую картинку:

    pattern

    Чтобы сделать из этой обычной картинки 9-patch можно воспользоваться любым графическим редактором, поддерживающим *.png или специальной утилитой draw9patch, которая наверняка уже лежит у вас в папке tools в месте установки Android SDK. Преимущество этой утилиты в том, что результаты масштабирования и область размещения контента наглядно показываются в окне редактора. А также утилита дает редактировать только рамку, не позволяя случайно чиркнуть на изображении.

    Запустим приложение, откроем нашу рамку (File > Open 9-patch) и отметим повторяемые области и область размещения контента.

    image

    В правом блоке утилиты можно увидеть результат масштабирования.
    Теперь необходимо сохранить полученное изображение c расширением *.9.png. Затем добавить в проект, в папку /drawable/ (если вы не предусматриваете различных вариантов изображений для разных устройств ldpi/mdpi/hdpi ) и использовать его как фон для различных элементов управления (без расширения .9).

    В ОС Android nine-path используется для определения вида кнопок, полей ввода, выпадающих списков и т.д. Вы можете модифицировать эти изображения для своих разработок, взяв их из папки: android_sdk\platforms\android-8\data\res\drawable-hdpi.

    image

    Ссылки по теме:
    Android User Interface Guidelines
    Android 2d graphics
    Working with NinePatch Stretchable Graphics in Android
    public class android.graphics.NinePatch
    Draw 9-patch tool
    Поделиться публикацией
    Похожие публикации
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 35
    • +2
      А можно взять 9-path и его как-то обработать с помощью shape? Ну там скруглить края например и т.д?
      • +1
        Извините, конечно же 9-patch
        • +2
          Хм, в поле backround класса View может содержаться или цвет(solid color), или форма(shape), или 9-patch или statelist или еще что нибудь, но только одно.
          Не могу придумать ситуацию с совместным применением. Ничего же не мешает округлить углы прямо на изображении *.png.
          Но тем не менее если уж очень надо — можно поместить их друг на другом используя вложенные контейнеры и применяя к ним различные типы фона.
          • 0
            Нет, ну я имел в виду может быть можно выплюнуть на канву для shape 9-patch, а потом скруглить края, используя некие методы для shape? Или тут какая-то загвоздка?
            • 0
              Все еще не могу представить зачем может понадобиться обрабатывать таким образом изображение, но думаю если отрисовывать nine-patch на канву, то можно использовать тот же класс Paint для задания способа отрисовки. (метод draw)
              Но я такое не пробовал и не вижу причин пока.
              • 0
                Эмм… Я как понимаю, есть у нас некий ShapeDrawable — его мы можем засунуть в качестве фона для произвольного View. Если так, то можно ли скруглить края у корнеров, используя соответствующие радиусы, причем заранее залив на канву в onDraw(Canvas canvas) некую имагу?

                Лично мне это вообще в общем случае интересно, не только с 9-patch. Написав один некий Shape вы можете скругливать произвольные имаги только при помощи кода, внутри самого приложения.

                Что правда не интересно? Вообще работа с пэйнтом не очень прельщает, порадовало бы прямое выставление размеров радиусов для корнеров…
                • 0
                  Пробежавшись сейчас по докам пришел к выводу, что скорее всего это можно сделать методами канвы. Попробуйте. А даже если не удастся обрезать используя Shape — всегда можно вручную обрезать углы насколько вам надо, на канве то с этим проблем нет
                  • 0
                    Есть класс PaintDrawable, являющийся наследником ShapeDrawable, там есть явный метод setCornersRadii(float[] radii). Вопрос следующий — возможно ли ShapeDrawable сунуть в фон?
                    • 0
                      Извиняйте, можно ли PaintDrawable сунуть в фон?)
                      • 0
                        В фон View? Да, принимает, попробовал
                        • 0
                          Класс, значит таки можно выкрутиться с помощью PaintDrawable, спасибо за дискусс)
                          • 0
                            По идее в качестве бонуса, можно проверить — принимает ли Viewв фон любого произвольного наследника от Drawable) Есть мнение, что принимает)
                            • 0
                              Естественно принимает, тут и думать нечего :)
                              void setBackgroundDrawable(Drawable d)
                              Set the background to a given Drawable, or remove the background.
                              • 0
                                Ну это уже был сарказм, к тому что вы решили проверить PaintDrawable)

                                Если серьезно, то есть еще момент — как после скругления PaintDrawable себя поведет система. То есть непонятно будет ли она рендерить 9-patch как обычно когда он будет неявно присутствовать на канве View, или она реагирует только на 9-patch, который в явном виде устанавливается как фоном. В общем и целом тут, видимо, нужно экспериментировать.
                                • 0
                                  Этого можно легко добиться стандартными средствами оформления, я делал так: рисуем рамку с нужным округлением, задаём границы растяжения, ставим в качестве background, а затем с помощью padding указываем размеры тени, чтобы вся тень была видима.

                                  image
          • +1
            Андроминатор.
            • 0
              Жаль, что такого нет в вебе.
              • +1
                Есть нечто похожее в CSS3, но, конечно, это не универсальное решение, только для границ www.css3.info/preview/border-image/
                • 0
                  Ещё как есть, всё ещё используется. Только более извращёнными способами.
                • +2
                  Получается я могу, используя 9-patch, сделать резиновый фон для табов в TabWidget?

                  • +1
                    Резиновые фоны, резиновые контролы, резиновый интерфейс короче говоря — основное назначение 9-patch.
                    • +1
                      Да, только что попробовал. Можно для каждой вкладки
                      mTabHost.getTabWidget().getChildAt(0).setBackgroundDrawable(getResources().getDrawable(R.drawable.bg));
                      • +1
                        По идее 9-patch можно подсунуть в любое View, как ни странно)
                        • 0
                          Прекрасно) Я точно также сейчас устанавливаю простой background. Получается, просто нужно изменить png-файл.
                        • +1
                          Применил 9-patch :)

                          Вот фотоотчёт:


                          P.S. долго не мог понять кривого растяжения фона, пока не перечитал статью и не дошел до момента, что расширение надо делать *.9.png :)
                          • +1
                            Рад что статья кому-то помогла :)
                            • +1
                              И если не сложно поделитесь своими картинками табов и кодом установки :)
                              • +1
                                Конечно, не вопрос :)

                                private OnTabChangeListener MyOnTabChangeListener = new OnTabChangeListener()
                                {
                                  @Override
                                  public void onTabChanged(String tabId)
                                  {
                                    // Всем табам устанавливаем фон NonSeled (белый)
                                    for(int i = 0; i < tabHost.getTabWidget().getChildCount(); i++)
                                    {
                                      tabHost.getTabWidget().
                                              getChildAt(i).
                                              setBackgroundDrawable(
                                              getResources().
                                              getDrawable(R.drawable.nonseled));
                                
                                      final TextView tv = (TextView)
                                              tabHost.getTabWidget().
                                              getChildAt(i).
                                              findViewById(android.R.id.title);
                                
                                      tv.setTextColor(getResources().
                                              getColorStateList(R.color.nonseledcolor));
                                    }
                                
                                    // Выбранному табу ставим фон Seled (чёрный)
                                    tabHost.getTabWidget().
                                            getChildAt(tabHost.getCurrentTab()).
                                            setBackgroundDrawable(
                                            getResources().
                                            getDrawable(R.drawable.seled));
                                
                                    final TextView tv = (TextView)
                                            tabHost.getTabWidget().
                                            getChildAt(tabHost.getCurrentTab()).
                                            findViewById(android.R.id.title);
                                
                                    tv.setTextColor(getResources().
                                            getColorStateList(R.color.seledcolor));
                                  }
                                };
                                


                                9-patch картинки:



                                habreffect.ru/files/086/a54be4e77/nonseled.9.png



                                habreffect.ru/files/bcd/5563e8401/seled.9.png
                      • +2
                        ожидал картинки вроде этой
                      • 0
                        Спасибо за статью. Как вы считаете, в связке программист — дизайнер кто должен выполнять подготовку изображений 9patch? Или лучше, чтобы это был отдельный человек-верстальщик?
                        • 0
                          Спасибо, ваша статья только что сэкономила мне кучу времени :)
                          • 0
                            начиная с какой версии SDK это работает а то что то под 2.1 у меня просто подставляет ужасно подогнанное изображения с черными линиями а в 4 все нормально.
                            • 0
                              Не могу найти данную утилиту в папке sdk/tools… Её убрали полностью или переместили куда-то?

                              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.