Соединение SlidingMenu с Support Library и обход возможных проблем

от автора

Предисловие

Здравствуй, Хабр!

Сразу хочу сделать небольшое отступление от темы: я не профессиональный 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)). Эти файлы необходимо создать. Примеры всех исходников под спойлером ниже.

Исходники

MainActivity.java

 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); 	} } 

res/drawable/slidingmenu_shadowgradient.xml

 <?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> 

res/layout/menu_frame.xml

 <?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> 

res/values/strings.xml

 <?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); } 
Полный код MainActivity.java

 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. Если у вас имеются замечания по качеству кода или качеству текста — высказывайте, но не забывайте, пожалуйста, что все рассчитано для новичков от такого-же новичка.

ссылка на оригинал статьи http://habrahabr.ru/post/194056/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *