Сплетение Android SlidingMenu с Support Library
Примерами качества для меня являются приложения VK, Forsquare и Instagram. Собственно из них сразу были взяты идеи использования ActionBar и SlidingMenu. Как и при верстке/разработке web-сайтов (моим основным делом) и их приличной работе в Internet Explorer, так и здесь, я первым делом задумался над совместимостью с устаревающими версиями Android, поскольку ActionBar поддерживается только с 3.0.
Начнем
1. Создаем новый проект в Eclipse. Минимальная поддерживаемая версия API 7. Желательно, без темы (Theme: None).
2. Подключаем Support Library, как это сделать, написано по ссылке, указанной выше. Вот еще раз, на всякий случай. Прописываем тему.
3. Вот и первая проблема, с которой мне пришлось столкнуться: все прекрасно работает на Android ниже 3.0. Оказывается забыл один момент: тему предлагается наследовать в файле res/values/styles.xml, однако же это оказался не самый верный вариант. При создании проекта, Eclipse сразу создает несколько стилевых файлов, для разных версий API:
- res/values/styles.xml — стандартный файл стилей;
- res/values-v11/styles.xml — файл стилей для API 11+;
- и res/values-v14/styles.xml — файл стилей для API 14+;
Мы же наследуем тему только в первом из приведенных файлов, поэтому работает только до версии Android 3.0 (API11).
Решается наследованием темы во всех файлах или непосредственно в манифесте проекта. Для этого переходим в AndroidManifest.xml и находим строки:
<application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" >
где заменяем:
android:theme="@style/AppTheme"
на:
android:theme="@style/Theme.AppCompat.Light"
При отсутствии строки android:theme — добавляем ее.
В данном случае стоит помнить, что при наследовании темы непосредственно в манифесте, свою цветовую схему использовать не получится, поэтому рекомендую использовать первый вариант, с наследованием в каждом файле отдельно.
На эту проблему ушло 2 дня, решение которой обнаружилось при поиске информации, на совершенно иную тему.
Запускаем, проверяем. Все работает.
Подключаем SlidingMenu
В процессе поиска такого меню, сразу наткнулся на Drawer. Однако, мне нужно было не это, по некоторым причинам, которые здесь описывать не стану.
Выбор пал на SlidingMenu. Библиотека полностью бесплатна и доступна на GitHub.
1. Скачиваем все файлы и приступаем к подключению в Eclipse: для этого, идем File → New → Other → Android Project from Existing Code, в открывшемся окне указываем путь до папки library ранее скачанного SlidingMenu. После того, как файлы скопируются, нажимаем ОК. Библиотека подключена.
2. Подключаем ее к нашему проекту, созданному ранее. Повторяем процедуру подключения Support Lirary, но в этот раз выбираем «library».
3. А вот и вторая проблема — консоль сообщает о конфликте двух файлов android-support-v4.jar. Оказалось, что это файл мало, что содержится в самом проекте, так еще и подключается Support Library и SlidingMenu. Решение оказалось простым, нашлось оно на StackOverflow.com: удаляем данный файл из SlidingMenu library и из нашего проекта (у обоих он находится в папке «lib»).
Теперь возникла новая проблема — SlidingMenu library сообщает о множестве ошибок. Это связано с отсутствием удаленного нами файла. Обход этого также был найден на StackOverflow: отключаем библиотеку Support Lib от нашего проекта и подключаем ее-же, но уже к SlidingMenu library. В таком случае, нужный для всех троих файл будет подключен сначала к библиотеке SlidingMenu, а SlidingMenu, уже вместе со своим функционалом, подключит и Support Lib к нашему проекту.
На решение этой проблемы ушло 3 дня (да, я люблю портить себе жизнь), просто потому, что не обращал внимания на ошибки в консоли.
Сложно, но если разобраться в таких нюансах, все становится вполне понятно и логично.
Чтобы красные надписи не смущали, очищаем консоль.
После всего вышеописанного, переходим в MainActivity.java и перед методом onCreate() объявляем переменную:
private SlidingMenu menu;
Далее, непосредственно в onCreate добавляем инициализатор меню:
menu = new SlidingMenu(this); menu.setMode(SlidingMenu.LEFT); menu.setTouchModeBehind(SlidingMenu.TOUCHMODE_FULLSCREEN); menu.setShadowDrawable(R.drawable.slidemenu_shadowgradient); menu.setShadowWidth(15); menu.setFadeDegree(0.0f); menu.attachToActivity(this, SlidingMenu.SLIDING_WINDOW); menu.setBehindWidth(200); menu.setMenu(R.layout.menu_frame);
Как видно в коде, указаны пути до файла с тенью (menu.setShadowDrawable(R.drawable.slidemenu_shadowgradient)) и, собственно, само меню (menu.setMenu(R.layout.menu_frame)). Эти файлы необходимо создать. Примеры всех исходников под спойлером ниже.
import com.jeremyfeinstein.slidingmenu.lib.SlidingMenu; import android.os.Bundle; import android.support.v7.app.ActionBarActivity; import android.view.Menu; import android.view.MenuItem; public class MainActivity extends ActionBarActivity { private SlidingMenu menu; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); menu = new SlidingMenu(this); menu.setMode(SlidingMenu.LEFT); menu.setTouchModeBehind(SlidingMenu.TOUCHMODE_FULLSCREEN); menu.setShadowDrawable(R.drawable.aslidingmenu_shadowgradient); menu.setShadowWidth(15); menu.setFadeDegree(0.0f); menu.attachToActivity(this, SlidingMenu.SLIDING_WINDOW); menu.setBehindWidth(200); menu.setMenu(R.layout.menu_frame); } }
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item> <shape> <gradient android:endColor="@color/purple_dark" android:startColor="@color/back" /> </shape> </item> </selector>
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:background="@color/back"> <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/menu_1" android:textAppearance="?android:attr/textAppearanceLarge" /> <TextView android:id="@+id/textView2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/menu_2" android:textAppearance="?android:attr/textAppearanceLarge" /> <TextView android:id="@+id/textView3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/menu_3" android:textAppearance="?android:attr/textAppearanceLarge" /> <TextView android:id="@+id/textView4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/menu_4" android:textAppearance="?android:attr/textAppearanceLarge" /> </LinearLayout>
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">SlideMenu Demo</string> <string name="action_settings">Settings</string> <string name="hello_world">Hello world!</string> <color name="back">#3d4140</color> <color name="purple_light">#ffffff</color> <color name="purple_dark">#353838</color> <string name="menu_1">Menu 1</string> <string name="menu_2">Menu 2</string> <string name="menu_3">Menu 3</string> <string name="menu_4">Menu 4</string> </resources>
Добавляем функционала
Все, что описано выше, конечно хорошо, но не хватает некоторых мелочей. Например, пользуясь приложением VK, часто приходится прибегать к боковому меню (SlidingMenu). Выдвигаю я его движением пальца по экрану, однако есть несколько разных способов его открытия.
Иконка в ActionBar
Для добавления функционала кнопке-иконке в экшенбаре, используем следующий код, который размещается в любое место в MainActivity.java:
@Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { // узнаем ID нажатой кнопки case android.R.id.home: // если это кнопка-иконка ActionBar, menu.toggle(true); // открываем меню (или закрываем) return true; } return super.onOptionsItemSelected(item); }
В конец onCreate() добавляем:
getSupportActionBar().setDisplayShowCustomEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
Помимо прочего, последний код добавляет красивую стрелочку к иконке.
Закрытие меню, по нажатию на кнопку «Назад»
Тут все просто, меню открыто, но при нажатии «Назад» закрывается приложение, а не меню. Исправляем следующим кодом:
public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { // если нажата кнопка "Назад" if(menu.isMenuShowing()){ // и если SlidingMenu открыто menu.toggle(true); // закрываем его return false; } } return super.onKeyDown(keyCode, event); }
Так же можно использовать использовать onBackPressed()
import com.jeremyfeinstein.slidingmenu.lib.SlidingMenu; import android.os.Bundle; import android.support.v7.app.ActionBarActivity; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; public class MainActivity extends ActionBarActivity { private SlidingMenu menu; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); menu = new SlidingMenu(this); menu.setMode(SlidingMenu.LEFT); menu.setTouchModeBehind(SlidingMenu.TOUCHMODE_FULLSCREEN); menu.setShadowDrawable(R.drawable.actionbar_gradient); menu.setShadowWidth(15); menu.setFadeDegree(0.0f); menu.attachToActivity(this, SlidingMenu.SLIDING_WINDOW); menu.setBehindWidth(200); menu.setMenu(R.layout.menu_frame); getSupportActionBar().setDisplayShowCustomEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: menu.toggle(true); return true; } return super.onOptionsItemSelected(item); } public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { if(menu.isMenuShowing()){ menu.toggle(true); return false; } } return super.onKeyDown(keyCode, event); } }
Результат:
В качестве заключения
Отвечу логичный на вопрос, который может возникнуть у читателя: «Почему не ActionBar Sherlock» (если вы конечно знаете, что это)?
Ответ прост: я не знал о его существовании в начале работы. Когда занялся SlidingMenu, узнал и о рекомендованном шерлоке, но менять Support Lib и половину кода с ним — уже не было желания.
Как выяснилось, начать программировать для Android не так то просто, учитывая, что половина проблем связана вовсе не с данной платформой, а с инструментами для нее.
Но, как я говорил выше, нужно только немного разобраться и дальше уже все становится куда понятнее.