Sunday, August 7, 2011

Замена ContextMenu в Android

Эта статья - переведенное подведение итогов к вопросам http://stackoverflow.com/questions/6950586/how-could-i-customize-context-menu-item-click-callback и http://stackoverflow.com/questions/6958622/how-to-create-dialog-that-looks-exactly-like-contextmenu .

Контекстное меню на настольных ПК - это меню, вызываемое при нажатии правой кнопки мыши. Контекстное меню используется для того, чтобы предоставить пользователю выбор действия, которые надо совершить над элементом, чье контекстное меню выбрано.
В Android контекстное меню (класс ContextMenu) вызывается нажатием и удержанием графического элемента.

Чтобы контекстное меню было вызвано по нажатию на View, необходимо зарегистрировать в Activity методом registerForContextMenu(View). Все, что делает этот метод - вызывает View.setOnCreateContextMenuListener(OnCreateContextMenuListener), при чем Activity реализовывает интерфейс OnCreateContextMenuListener, так что как единственный параметр он передает ссылку на себя.

За создание контекстного меню отвечает метод Activity onCreateContextMenu (ContextMenu menu, View v,  ContextMenu.ContextMenuInfo menuInfo), который вызывается каждый раз при долгом нажатии на View v, а при выборе пункта контекстного меню вызывается метод onContextItemSelected (MenuItem item). Все просто.

Но тут возникают сложности.
Предположим, в нашем Activity есть несколько View, каждое из которых вызывает контекстное меню. Причем у каждого View контекстное меню совершенно разное, и поведение контекстного меню тоже разное. Тем не менее, обработку действий мы должны проводить в одном методе onContextItemSelected(MenuItem). Не очень логично реализовывать действия контекстного меню над, скажем, картинкой и элементом списка в одном методе, не так ли?
Конечно, можно перенаправиться в метод класса того элемента, чье контекстное меню вызывалось, где и будет реализовываться основная логика, но суть от этого не меняется.

Кроме того, в View должна храниться ссылка на Activity. Вроде бы мелочь, но заводить отдельное поле для того, чтобы зарегистрировать контекстное меню, которое из Activity перенаправится обратно во View не очень-то логично.

Изначально я хотел использовать кое-где ContextMenu, где это возможно, а кое-куда вставить хак - диалоговое окно, выглядящее и ведущее себя как ContextMenu. Оказалось, что да, во внутреннем пакете Android com.android.internal лежат необходимые классы, генерирующие ContextMenu посредством того же диалогового окна. Но копировать необходимые системные layout, недоступные извне, в свой проект мне показалось глупым - где гарантия, что какой-то из производителей Android-фонов не решит слегка изменить внешний вид контекстного меню?

Поэтому я решил не использовать ContextMenu вообще. Вместо контекстного меню можно использовать диалоговое окно (AlertDialog). Класс AlertDialog.Builder предоставляет несколько удобных методов для построения диалогового окна, среди которых самый важный в данном случае - setItems(CharSequence[], DialogInterface.OnClickListener) (также массив строк можно передавать через указатель на ресурс, например, R.array.foo), который создает ListView, адаптер к нему, используя, опять же, какую-то внутреннюю layout, и обработчик (подробнее - здесь). На вид получается точно такое же контекстное меню. Кроме того, сразу на месте можно создать listener, который обработает действия на месте, без перенаправления в другие классы. Таким образом, мы можем реализовывать логику в тех местах, в которых нам это необходимо, а внешний вид диалогов будет одинаковым везде.

No comments:

Post a Comment