UI-паттерны: QuickAction-меню

9 марта 2012
Дмитрий Казаков, Developer

UI-паттерны: QuickAction-меню Сегодня я расскажу об одном очень популярном UI-паттерне, который используется на всех мобильных платформах. QuickAction-меню позволяет собрать и показать элементы, необходимые пользователю в какой-то определенный момент времени. Кроме того, что этот контрол позволяет не перегрузить экран лишними элементами, он очень удобен для пользователя, поскольку, как правило, появляется рядом с пальцем и позволяет быстро выполнить следующее действие.

В рамках этой статьи в основе QuickAction-меню используется PopuWindow – стандартный класс Android, доступный для всех версий платформы. PopupWindow представляет собой контейнер, появляющийся поверх любой Activity. Интерфейс компонента предпочтительно создать в XML:

<RelativeLayout         xmlns:android="http://schemas.android.com/apk/res/android"         android:layout_width="wrap_content"         android:layout_height="wrap_content">    <!-- любые элементы, которые вы хотите видеть на своем меню-->  ...      <LinearLayout                  android:id="@+id/footer"               android:layout_width="fill_parent"               android:layout_height="wrap_content"               android:layout_below="@id/scroll"               android:background="@drawable/quickaction_bottom_frame" />        <ImageView               android:id="@+id/my_arrow_down"               android:layout_width="wrap_content"               android:layout_height="wrap_content"               android:layout_marginTop="-2dp"               android:layout_below="@id/footer"               android:src="@drawable/quickaction_arrow_down" />    </RelativeLayout>    

Отдельного внимания заслуживает стрелка, указывающая на компонент интерфейса, для которого будет вызвано QuickAction-меню. Этот View будет вызываться из кода перед каждым показом контрола, и для него будут заново высчитываться ширина и высота компонента относительно элемента, над которым должно появиться меню. Чтобы стрелка немного отстояла от края контейнера, marginTop ей выставлен в -3dp: мы также могли просто нарисовать стрелку меньшего размера и указать нужный правый отступ, но тогда вычисления положения стрелки, которые мы будем проводить в коде, станут чуть сложнее. Также необходимо расположить элемент со стрелкой после основного контейнера footer: система выстраивает дерево элементов View, начиная с начала XML документа, и идёт по нему от корня, рисуя один элемент за другим; если изменить порядок элементов и определить View со стрелкой перед основным контейнером, то Android отрисует стрелку под другими элементами, и мы не достигнем нужного визуального эффекта.

Далее в классе Activity, на которой необходимо показать QuickAction-меню, в коде создается объект PopupWindow и его layout-контейнер связывается с родительским View. Это выглядит так:

    PopupWindow window = new PopupWindow();         window.setWidth(WindowManager.LayoutParams.MATCH_PARENT);         window.setHeight(WindowManager.LayoutParams.WRAP_CONTENT);                  LayoutInflater inflater = (LayoutInflater) context.         	        getSystemService(Context.LAYOUT_INFLATER_SERVICE);         View root = inflater.inflate(R.layout.quick_action_menu, null);                  root.setLayoutParams(         	        new LayoutParams(                         	    	LayoutParams.MATCH_PARENT,                             	LayoutParams.MATCH_PARENT));         root.measure(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);         int[] location = new int[2];         anchor.getLocationInWindow(location);               final int newHeight = location[1] - root.getMeasuredHeight();           window.setContentView(root);         window.showAtLocation(anchor, Gravity.NO_GRAVITY,         	         anchor.getMeasuredWidth(), newHeight);    

Здесь есть ряд нечасто используемых методов, которые, я думаю, нуждаются в пояснении. Как только мы получили объект с нашим контролом и выставили ему параметры расположения внутри контейнера, у него вызывается метод mesure(). Этот метод позволяет узнать размеры элемента до того как он будет вставлен в уже существующий layout. Эти размеры нам понадобятся для вычисления позиции на экране, где будет нужно отобразить наше QuickAction-меню. Далее вызывается метод getLocationInWindow(), который вычислит и вернет координаты элемента, для которого он бы вызван. Используя эти координаты и значения высоты и ширины нашего контрола, мы можем вычислить точное расположение элемента. Остается лишь показать наше QuickAction меню, что и делает метод showAtLocation().

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

Определение положения стрелки вынесено в отдельный метод, представленный ниже. Туда мы передаём необходимое расстояние для отступа, и через вызов getMeasuredWidth(), упомянутый ранее, определяем ширину будущего указателя.

    private void showArrow(int whichArrow, int requestedX) {         final int arrowWidth = arrow.getMeasuredWidth();                  ViewGroup.MarginLayoutParams param = (ViewGroup.MarginLayoutParams)                    showArrow.getLayoutParams();         param.leftMargin = requestedX - arrowWidth / 2;     }  

Заключение

Что ж, это все. Сам этап построения QuickAction меню не так сложен, но требует некоторых знаний об особенностях работы с UI из кода Android-приложения. Надеюсь, поможет в реализации ваших проектов.