суббота, 5 июля 2014 г.

Соответствие квалификаторов размеров экранов с реальными размерами экранов в пикселях

Устройства, работающие под Android'ом, имеют экраны самых разных размеров и плотности пикселей. Чтобы адаптировать разметку интерфейса под каждый из этих размеров, в проекте рядом с папкой layout можно создавать альтернативные папки для каждого из размеров экранов и даже его ориентации, например:

размеры
layout-small
layout-normal
layout-large
layout-xlarge

размеры и ландшафтная ориентация
layout-small-land
layout-normal-land
layout-large-land
layout-xlarge-land

размеры и портретная ориентация
layout-small-port
layout-normal-port
layout-large-port
layout-xlarge-port

размеры в независящих от плотности пикселях (доступно, начиная с Android 3.2):
layout-sw600dp (для наименьшей стороны равной и более)
layout-w600dp (для ширины равной и более)
layout-h400dp (для высоты равной и более)
и другие вариации

Вышеописанные квалификаторы оперируют условными пикселями, независящими от плотности (dp), тогда как в характеристиках экранов устройств мы видим разрешение в реальных пикселях плюс плотность в точках на дюйм (или коэффициент масштабирования).

Нижеприведённая таблица поможет сравнить значения квалификаторов с размерами экранов реальных устройств:

Пиксели, зависящие от плотности
Физические пиксели при разных плотностях
квалификатор
размер в dp
ldpi
mdpi
tvdpi
hdpi
xhdpi
xxhdpi
120 (dpi)
160 (dpi)
213 (dpi)
240 (dpi)
320 (dpi)
480 (dpi)
k=0,75
k=1
k=1,33
k=1,5
k=2
k=3
стороны
стороны
стороны
стороны
стороны
стороны
стороны
длинная
короткая
длинная
короткая
длинная
короткая
длинная
короткая
длинная
короткая
длинная
короткая
длинная
короткая
small
426
320
320
240
426
320
567
426
639
480
852
640
1278
960
normal
470
320
353
240
470
320
625
426
705
480
940
640
1410
960
large
640
480
480
360
640
480
851
638
960
720
1280
960
1920
1440
xlarge
960
720
720
540
960
720
1277
958
1440
1080
1920
1440
2880
2160

Таким образом, можно констатировать, что квалификаторы small и normal, грубо говоря, соответствуют смартфонам, а large и xlarge - планшетам.

пятница, 4 июля 2014 г.

fill_parent или match_parent?

Вступление

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

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

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

Почему я начал так издалека? Да просто при изучении программирования под Android на каждом шагу возникают какие-то попутные вопросы, которых, казалось бы, не должно быть. Так и с этими fill_parent и match_parent - постоянно забываешь, какой из них устарел. К тому же Android Studio 0.8.1 до сих пор по умолчанию вставляет устаревший вариант.

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

fill_parent или match_parent?

До API 8 для указания размеров ширины и высоты элементов layout:width и layout:height использовалось значение  fill_parent. Это значение можно перевести, как "заполнить родителя". Такой перевод можно истолковать так, что элемент заполняет в родителе всю свободную от других элементов площадь. На самом же деле свойство fill_parent просто приравнивает размер дочернего элемента к соответствующему размеру родителя. То есть, если в родителе рядом будут другие элементы, то этот элемент просто сам выпрет наружу родителя или выпрет туда другие находящиеся в родителе элементы.

В связи с этим, начиная с API 8, значение fill_parent стало считаться устаревшим, и вместо него появилось значение match_parent. Это значение переводится, как "такой-же как у родителя", что уже точно соответствует его предназначению.

Замечу, что оба значения в представлении системы одинаковы и равны -1.

А как сделать fill_parent?

Возникает вопрос, а как же смоделировать заполнение дочерним элементом свободного места у родителя, если в нём содержаться другие дочерние элементы? Для этого свойство layout:width или layout:height устанавливается в ноль, а в свойстве layout:weight ставится единица.

четверг, 3 июля 2014 г.

Интерфейс

Интерфейс — это сущность с простым перечнем неких методов, выглядящая как класс, которую можно подключить к реальному классу, где эти методы будут уже реализовываться в коде. Будучи подключенным к разным классам, интерфейс определяет одинаковые имена для своих методов, которые в этих классах нужно будет реализовать (прописать их код). То есть, получается одинаковый интерфейс взаимодействия со всеми классами, к которым подключен этот интерфейс.

Благодаря такой конструкции возникает дополнительная возможность обращаться к этим методам, реализованным в разных классах, через переменную одного и того же типа (тип переменной —конкретный интерфейс), а не через разные имена классов, в которых эти методы реализованы. Разумеется, предварительно нужно присвоить нужный класс этой переменной. При этом в переменную попадает только та часть класса, которая объявлена этим интерфейсом.

Практически это выглядит следующим образом:
  1. Описывается интерфейс, в котором, как в классе, перечисляются все нужные нам методы, но с пустыми телами, без кода.
  2. Описывается класс, к которому специальной языковой конструкцией подключают интерфейс, и в этом классе прописывают реализацию описанных в интерфейсе методов.
  3. В другом классе объявляется переменная, тип которой обозначается в виде имени описанного выше интерфейса. То есть тип — наш интерфейс.
  4. Этой переменной присваивается класс, к которому выше был подключен интерфейс. Переменная становится мостиком к методам интерфейса, описанным в классе.
Теперь мы можем присваивать этой переменной любой класс, к которому подключен соответствующий интерфейс, и вызывать методы этого класса.

В Java это выглядит примерно так. В смысл кода особенно не вникайте, он максимально сокращён. Продемонстрированы всего лишь языковые конструкции:

Пункт 1:

public interface onItemClickListener {
    public void itemClick(int position);
}

Пункт 2:

public class Activity1 implements onItemClickListener {

    int position = 0;

    public void itemClick(int position) {
        this.position = position;
    }
}

Пункты 3 и 4:

public class Activity2 {

    onItemClickListener listener;

    public void onAttach(Activity activity) {
        listener = (onItemClickListener) activity;
    }

    public void onListItemClick(ListView l, View v, int position, long id) {
        listener.itemClick(position);
    }
}

Андрюшу порвало нафиг!

Итак, на днях Android порвало на несколько веток - по одной на каждый новый вид устройств. Смартфоны и планшеты теперь имеют букву L рядом с номером версии.  Помимо этих устройств, поддерживаемых Android'ом, появились также 3 других вида устройств:
  1. Носимые устройства (Wear). Рядом с номером версии ставят букву W. Форм-фактор устройств - наручные часы. Устройства показывают время, погоду, сообщения из соцсетей, пульс, различные голосовые подсказки и пр. (http://www.android.com/intl/ru/wear/)
  2. Телевизоры с расширенными функциями (TV). Просмотр интернет-каналов, поиск видео и пр. (http://www.android.com/intl/ru/tv/)
  3. Навигаторы с расширенными функциями (Auto). Навигация, музыка, климат-контроль, информация автомобиля, пуск двигателя, погода, новости, расход топлива и пр. (http://www.android.com/intl/ru/auto/)
API Level всех новых веток Android, согласно SDK-менеджеру, равен 20.

пятница, 27 июня 2014 г.

Встроенная в программу цифровая клавиатура

Штатная экранная клавиатура в Android занимает слишком много места. Да и имеющиеся альтернативы редко бывают компактными. Между тем, существует ряд задач, где количество кнопок на клавиатуре можно существенно ограничить. Например, сейчас я пишу приложение, помогающее рассчитывать электрику в СНТ, и выезжающая в пол-экрана цифровая клавиатура, закрывающая половину полей ввода, меня категорически не устроила.

Тогда я разработал вот такую двухстрочную клавиатуру, где присутствуют только нужные в этой активности кнопки: 0-9, десятичная точка, кнопки перемещения курсора влево-вправо, забой и табуляция (переход к следующему полю ввода):


Реализована клавиатура очень просто. На корневом вертикальном LinearLayout размещены scrollView (с layout:weight = 1) и вертикальный LinearLayout, в котором и построена клавиатура.

Для всех полей отключена экранная клавиатура командой:

editText.setInputType(InputType.TYPE_NULL);

и спрятан LinearLayout с клавиатурой командой:

linearLayout.setVisibility(View.GONE);

При касании поля вызывается команда:

linearLayout.setVisibility(View.VISIBLE);

и клавиатура вызывается.

При нажатии аппаратной кнопки "Назад" вызывается команда прятать клавиатуру, если она видна. Это реализовано переопределением соответствующего метода:

    @Override
    public void onBackPressed() {
        if (linearLayout.getVisibility() == View.VISIBLE) linearLayout.setVisibility(View.GONE);
        else super.onBackPressed();
    }

На каждую кнопку подключена вибра. Для этого в разметке кнопки оказан атрибут:

android:hapticFeedbackEnabled="true"

А при нажатии на кнопку вызывается:

button,performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);

Впечатывание символов в поля ввода в позицию курсора при нажатии на цифровые кнопки осуществляется следующими командами:

    String textToInsert = "1";
    int start = Math.max(editTetxt.getSelectionStart(), 0);
    int end = Math.max(editTetxt.getSelectionEnd(), 0); 
    editTetxt.getText().replace(Math.min(start, end), Math.max(start, end), textToInsert, 0, textToInsert.length());

Функционал остальных кнопок реализован тоже несложно.

понедельник, 23 июня 2014 г.

Устранение непредсказуемого поведение веса (layout:weight) при разметке активности

layout:weight
 
Настройки веса при разметки активности реализованы на Android не очень прозрачно. Я долго мучился с непонятными скачками размеров и вылетами элементов за пределы разметки, пока не плюнул на интуицию и не изучил рекомендуемые правила их настройки.

Итак, у вас не получится чётко управлять весом элементов, пока вы в свойстве layout:width не установите значение ширины элемента в ноль вместо match_parent или wrap_content.

Замечу, что настройки подразделяются на обычные и с приставкой layout. Обычные настройки влияют на содержимое внутри элемента. Настройки с приставкой layout влияют на внешние размеры элемента и его положение во внешнем контейнере.

суббота, 21 июня 2014 г.

Error:Cause: failed to find Build Tools revision...

Обновился на днях через SDK Manager, и при открытии старого проекта вывалилась ошибка:

Error:Cause: failed to find Build Tools revision 19.0.3
Please install the missing Build Tools from the Android SDK Manager.

Но возвращать старый Build Tools мне как-то не хотелось.

Решение оказалось предсказуемо - надо открыть внутри папки модуля уже знакомый нам файл build.gradle и поменять в нём номер текущей версии на свежий:

android {
    compileSdkVersion 19
    buildToolsVersion "19.1.0"


    // Остальные настройки
}

Решение нашёл тут:
http://stackoverflow.com/questions/21353832/failed-to-build-project-with-android-studio