Предисловие
Здравствуй!
Сразу хочу сделать небольшое отступление от темы: я не профессиональный android-разработчик и только учусь. В этом посте, рассчитанном более на новичков, мне бы хотелось объединить решения тех проблем, с которыми столкнулся, при написании задуманного приложения, а в частности использовании ActionBar при помощи Support Library и присоединении к ним SlidingMenu.
Примерами качества для меня являются приложения VK, Forsquare и Instagram. Собственно из них сразу были взяты идеи использования ActionBar и SlidingMenu. Как и при верстке/разработке web-сайтов (моим основным делом) и их приличной работе в Internet Explorer, так и здесь, я первым делом задумался над совместимостью с устаревающими версиями Android, поскольку ActionBar поддерживается только с 3.0. Благодаря поиску быстро нашел решение — ActionBar для версий 2.1+.
Начнем
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 (API >11).
Решается наследованием темы во всех файлах или непосредственно в манифесте проекта. Для этого переходим в 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, но после onCreate():
@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);
Помимо прочего, последний код добавляет красивую стрелочку к иконке.
Закрытие меню, по нажатию на кнопку «Назад»
Тут все просто, меню открыто, но при нажатии «Назад» закрывается приложение, а не меню. Исправляем кодом (после onCreate()):
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);
}
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);
}
}
Результат:
(к сожалению, этот скрин не грузился полностью на HabraStorage, пришлось воспользоваться сторонним
В качестве заключения
Отвечу логичный на вопрос, который может возникнуть у читателя: «Почему не ActionBar Sherlock» (если вы конечно знаете, что это)?
Ответ прост: я не знал о его существовании в начале работы. Когда занялся SlidingMenu, узнал и о рекомендованном шерлоке, но менять Support Lib и половину кода с ним — уже не было желания.
Как выяснилось, начать программировать для Android не так то просто, учитывая, что половина проблем связана вовсе не с данной платформой, а с инструментами для нее.
Но, как я говорил выше, нужно только немного разобраться и дальше уже все становится куда понятнее.
Спасибо, если вы дочитали до конца.
P.S. Если у вас имеются замечания по качеству кода или качеству текста — высказывайте, но не забывайте, пожалуйста, что все рассчитано для новичков от такого-же новичка.
Автор: IgorT