Освой Android играючи

Сайт Александра Климова

Шкодим

/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000

ActionBar (Панель действий)

Обновлено 11 июня 2012,10 апреля 2016, 30 апреля 2024

Немного теории
Прячем и показываем ActionBar
Устанавливаем заголовок и подзаголовок
Программно меняем цвет текста в заголовке
Используем Drawable
Узнать высоту панели
Добавляем меню в ActionBar
Используем значок приложения как элемент навигации
Выпадающий список
Обычный Spinner
Используем правильный Context для ActionBar
CustomView
Кастомизация ActionBar

Немного теории

Начиная с Android 3.0 (API 11), в приложениях появилась возможность использовать панель действий ActionBar, которая сочетает в себе заголовок и меню, упрощая навигацию и предоставляя быстрый доступ к частым операциям.

ActionBar заменяет Options Menu, которое было в ранних версиях Android, используя новые дополнительные параметры для меню. Следует отметить, что в Android 5.0 (API 21) появился новый компонент Toolbar, который является дальнейшим развитием ActionBar. И он активно вытесняет панель действий. Но вам всё равно следует изучить работу ActionBar, так как основные принципы работы завязаны на ней. Формально ActiobBar не объявлен устаревшим и по-прежнему используется в немного скрытом режиме.

За появление панели действий отвечает тема Theme.Holo или её производные. В API 21 и выше произошли небольшие изменения. Как я уже сказал выше, появился новый компонент ToolBar, который может заменить панель действий в активности. Поэтому тут нужно быть внимательным и выбирать соответствующую тему, например, Theme.Material.Light.DarkActionBar или Theme.MaterialComponents.DayNight.DarkActionBar. Для старых устройств используются библиотеки совместимости. В этом случае активность наследуется от AppCompatActivity, а используемая тема Theme.AppCompat или его производные, например, Theme.AppCompat.Light.DarkActionBar.

Для изучения ActionBar поступим следующим образом. Мы создадим вторую активность и пропишем у неё в манифесте собственную тему, которая перекроет стандартную тему приложения.


<activity
    android:name=".SecondActivity"
    android:exported="false"
    android:label="Second Activity"
    android:theme="@style/Theme.AppCompat.DayNight.DarkActionBar" />

Далее просто через щелчок кнопки с первой активности будем переходить на вторую активность и смотреть на работу с ActiobBar.

Сравните экраны двух активностей. У первой по умолчанию нет заголовка, а у второй активности появился тёмный заголовок (DarkActionBar) и поменялись цвета системных панелей вверху и внизу, так как используется старая тема Theme.AppCompat.

ActionBar

Прячем и показываем ActionBar

Кстати, изначально существовало два вида ActionBar для новых устройств и для старых устройств при помощи библиотеки совместимости. Нужно было быть внимательным и не смешивать вызовы методов из разных классов, чтобы не получить ошибку. На сегодняшний день студия создаёт проекты с применением библиотеки AndroidX и проблем сейчас не возникает.

Чтобы увидеть разницу в активности с ActionBar и без неё, напишем простой пример. Добавим на экран второй активности компонент ToggleButton и пропишем код, скрывающий и показывающий ActionBar:


override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_second)

    val toggleButton: ToggleButton = findViewById(R.id.toggleButton)
    toggleButton.setOnCheckedChangeListener { compoundButton, isChecked ->
        val actionBar = supportActionBar
        if (isChecked) {
            actionBar?.hide()
        } else {
            actionBar?.show()
        }
    }
}

ActionBar ActionBar

Как видите, на втором рисунке при скрытом ActionBar мы не видим заголовка приложения. Раньше при этом содержимое экрана поднималось вверх, сейчас такого не наблюдаю.

Текущее состояние панели действий можно узнать при помощи метода isShowing(). Таким образом мы можем отслеживать видимость панели и переписать код.


toggleButton.setOnClickListener {
    val actionBar = supportActionBar
    val isActionBarShowing = actionBar?.isShowing
    if (isActionBarShowing!!) actionBar.hide() else actionBar.show()
}

Казалось бы, всё замечательно. Но запустим пример, скроем панель действий и сменим ориентацию. Мы увидим, что ActionBar снова появился. Чтобы избежать подобной накладки, можно сохранить информацию о видимости панели в методе onSaveInstanceState(), используя его параметр типа Bundle, а в методе onCreate() восстановить нужное состояние.


override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_second)

    if (savedInstanceState != null) {
        val savedActionBarisShowing = savedInstanceState.getBoolean(
            "ACTIONBAR_VISIBLE", true
        )
        if (savedActionBarisShowing) {
            supportActionBar?.show()
        } else {
            supportActionBar?.hide()
        }
    }

    val toggleButton: ToggleButton = findViewById(R.id.toggleButton)

    toggleButton.setOnClickListener {
        val isActionBarShowing = supportActionBar?.isShowing
        if (isActionBarShowing!!) supportActionBar?.hide() else supportActionBar?.show()
    }
}

override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState)

    if (supportActionBar?.isShowing == true) {
        outState.putBoolean("ACTIONBAR_VISIBLE", true)
    } else {
        outState.putBoolean("ACTIONBAR_VISIBLE", false)
    }
}

Напомню, чтобы избавиться от ActionBar на этапе разработки, можно в манифесте прописать нужную тему с использованием NoActionBar, например Theme.Holo.NoActionBar или Theme.Holo.NoActionBar.Fullscreen и т.п:


<application
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@android:style/Theme.Holo.NoActionBar" >

Но если вы убрали панель действий через тему, то программно показать её не получится, так как supportActionBar будет возвращать null.

Устанавливаем заголовок и подзаголовок

Панель действий ActionBar - это самостоятельный элемент, который заменяет стандартный заголовок у активности. Сейчас мы в этом убедимся. Напишем код:


val actionBar = supportActionBar
actionBar?.apply {
    title = "Рыжик"
    subtitle = "Самый умный кот"
}
this.title = "Котяра" // этот текст вы не увидите

Мы установили свой заголовок. Имя метода совпадает с именем метода для активности Activity.setTitle(), но имеет больший приоритет. Поэтому, если вы даже установите текст для заголовка активности, то его не увидите.

Кроме того, у панели действий есть ещё один интересный метод setSubtitle(), который выводит подзаголовок мелким шрифтом под основным заголовком.

Заголовок в ActionBar Заголовок в ActionBar

Первый скриншот я делал несколько лет назад, когда панель действий только появилась. Второй скриншот показывает современный вид, в котором по умолчанию значок приложения не выводится (стиль Material Design).

Если вы всё-таки хотите выводить значок, то нужно вызвать пару методов.


actionBar?.setDisplayShowHomeEnabled(true)
actionBar?.setIcon(R.mipmap.ic_launcher)

В API 11 и 14 появились новые методы, позволяющие выводить не значок, а логотип. Логотип может иметь больший размер и лучшую детализацию.


val actionBar = supportActionBar
actionBar?.apply {
    setDisplayShowHomeEnabled(true)
    setDisplayUseLogoEnabled(true)
    setLogo(R.drawable.sleepbedcat)
}

Программно меняем цвет текста в заголовке

Для Material Design принято использовать готовые схемы для элементов панели. Например, для строки состояния над панелью используется colorPrimaryDark, для самой панели - colorPrimary, для декоративных элементов на панели - colorAccent.

Можно воспользоваться Spannable. Поменяем цвет текста на жёлтый.


val actionBar = supportActionBar
val foregroundColorSpan = ForegroundColorSpan(Color.YELLOW)
val spannableString = SpannableString("Рыжик")
spannableString.setSpan(
    foregroundColorSpan,
    0,
    "Рыжик".length,
    Spannable.SPAN_INCLUSIVE_INCLUSIVE
)
actionBar?.title = spannableString

Также можно использовать CustomView и приготовить текст со всеми нужными атрибутами.

Используем Drawable

Можно использовать графические ресурсы в качестве фонового рисунка для панели через вызов метода ActionBar.setBackgroundDrawable(). Картинка будет растягиваться по всей панели, поэтому позаботьтесь о размерах заранее или используйте форматы 9-patch или XML-drawable. В Android 4.2 можно использовать анимационные drawable.


val actionBar = supportActionBar
val catDrawable = resources.getDrawable(R.drawable.cat)
actionBar?.setBackgroundDrawable(catDrawable)
// или просто меняем цвет
actionBar?.setBackgroundDrawable(ColorDrawable(Color.RED))

Пример с градиентом.


val actionBar = supportActionBar
val gradientDrawable = GradientDrawable(
    GradientDrawable.Orientation.TOP_BOTTOM, intArrayOf(
        Color.RED,
        Color.BLUE
    )
)
actionBar?.setBackgroundDrawable(gradientDrawable)

ActionBar с градиентом

Узнать высоту панели

Узнать высоту панели можно через свойства темы.


val typedValue = TypedValue()

if (this.theme.resolveAttribute(
        android.R.attr.actionBarSize,
        typedValue,
        true
    )){
    val actionBarHeight = TypedValue.complexToDimensionPixelSize(
        typedValue.data,
        resources.displayMetrics
    )

    Log.i("ActionBar", "ActionBar height : $actionBarHeight pixels")
}

Сейчас у нас пустой ActionBar, если не учитывать заголовок и значок приложения (как опция). Но ActionBar переводится как Панель действий, а никаких действий мы пока не видим. Вспоминаем урок, посвящённый разработке меню, и создаём меню для ActionBar по такому же принципу. Так как ActionBar подменяет стандартный заголовок, то никаких отличий нет. Разработанное меню автоматически появится на панели действий.

Дальше идут старые примеры на Java. Я не стал их переписывать, так как в основном код дублируется в других статьях.

Используем значок приложения как элемент навигации

Начиная с Android 3.0, можно использовать значок приложения как элемент навигации для быстрого возврата на главный экран приложения (на самом деле возможности шире, я беру простейший пример). Когда пользователь щёлкает по значку, система вызывает метод onOptionsItemSelected() с зарезервированным идентификатором android.R.id.home. Вы можете вернуться на главную активность приложения или на один шаг назад в программной иерархии. Возврат на начальную активность происходит при помощи намерения с флагом FLAG_ACTIVITY_CLEAR_TOP

Чтобы реализовать данный пример, необходимо создать еще одну активность. В первой основной активности нужно прописать код для перехода на вторую активность, например, при щелчке на кнопку. Далее во второй активности пишем следующий код:


package ru.alexanderklimov.actionbar;

import android.app.ActionBar;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.MenuItem;

public class Second extends Activity {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.two);
		ActionBar actionBar = getActionBar();
		actionBar.setHomeButtonEnabled(true); // обязательно для Android 4.0
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case android.R.id.home:
			// app icon in action bar clicked; go home
			Intent intent = new Intent(this, ActionBarActivity.class);
			intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
			startActivity(intent);
			return true;
		default:
			return super.onOptionsItemSelected(item);
		}
	}
}

Особо нужно обратить внимание на код в методе onCreate(). Чтобы значок распознавал нажатие, нужно явно вызвать метод setHomeButtonEnabled(). Это правило действует для версий 4.0, в версии Android 3.0 такой необходимости не было, что вызвало определённые трудности у части проектов, которые вдруг перестали работать.

Теперь при щелчке по значку мы со второго экрана должны вернуться на первый экран. Не всякий пользователь догадается о такой возможности. Поэтому можно вызвать еще один метод, который добавит стрелку слева от значка:


actionBar.setDisplayHomeAsUpEnabled(true);

Метод можно вызвать после первого метода setHomeButtonEnabled() или даже вместо него, так как значок в этом случае у меня также распознавал нажатие. Также обязательно нужно указать родительскую активность, иначе можно получить исключение (см. ниже).

Up

Внешний вид стрелки меняется в зависимости от версии системы. На скриншоте стрелка от версии Android 4.0.

Стоит также отметить, что вместо значка можно использовать логотип. По умолчанию система использует атрибут android:icon в элементах <application> или <activity> файла-манифеста. Вы можете использовать атрибут android:logo в тех случаях, когда квадратный значок не очень подходит дизайну вашего приложения. Логотип будет вести себя также, как и значок и реагировать на нажатие.

Начиная с Android 4.1 (API 16), можно не писать код для возврата на главную активность. Достаточно установить атрибут parentActivityName в манифесте у второй активности. Тогда нажатие на значок будет распознаваться автоматически.


<activity
  android:name=".SecondActivity"
  android:label="@string/app_name" 
  android:parentActivityName="MainActivity">
</activity> 

Для версий ниже API 16 следует добавить метаданные.


<activity
    android:name=".SecondActivity"
    android:label="@string/app_name"
    android:parentActivityName=".MainActivity">
    <meta-data
        android:name="android.support.PARENT_ACTIVITY"
        android:value=".MainActivity" />
</activity>

Часто поведение кнопки навигации и кнопки Back совпадает и не все понимают разницу. Кнопка навигации всегда возвращает пользователя на родительскую активность. Кнопка Back возвращает всегда на предыдущий экран. Представьте себе, что вы оказались на третьей активности с первого экрана. Если у третьей активности прописана в качестве родительской вторая активность, то кнопка навигации перебросит вас на вторую активность, а кнопка Back на первую активность.

Выпадающий список

Когда у вас две-три вкладки, то работать вполне комфортно. Но если вам нужно больше экранов, то навигация между ними становится утомительной. В таких случаях подойдёт выпадающий список на основе компонента Spinner. Если вы посмотрите на приложение Google Карты, то там как раз реализовано данное решение.

Для начала следует установить нужный режим, а также убрать заголовок, чтобы освободить место:


actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
actionBar.setTitle( "" );

Далее осталось создать адаптер, заполнить его данными и вывести на экран. Нам необходим список объектов Map, где каждая карта содержит две записи: "title" (заголовок), который выводится в самом Spinner и "fragment" (фрагмент), который содержит Fragment, выводимый при выборе элемента из выпадающего списка. Для адаптера используется SimpleAdapter, который использует стандартную системную разметку для списка Spinner, карту с записями title и элемент TextView для отдельного элемента списка.

SimpleAdapter является подклассом SpinnerAdapter и удобен для связывания статических данных. В более сложных приложениях могут понадобиться другие адаптеры.

Когда адаптер будет готов, следует реализовать обработчик OnNavigationListener для навигации. Получится следующий код для навигации по ранее созданным фрагментам (поменялся код только в onCreate(), лишнее закомментировал):


@Override
public void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.main);

	if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
		// For the main activity, make sure the app icon in the action bar
		// does not behave as a button
		actionBar = getActionBar();
	}

	actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
	actionBar.setTitle("");

	final List<Map<String, Object>> data = new ArrayList<Map<String, Object>>();
	Map<String, Object> map = new HashMap<String, Object>();
	
	map.put("title", "Фрагмент 1");
	map.put("fragment",
			Fragment.instantiate(this, FragmentA.class.getName()));
	data.add(map);
	
	map = new HashMap<String, Object>();
	map.put("title", "Фрагмент 2");
	map.put("fragment",
			Fragment.instantiate(this, FragmentB.class.getName()));
	data.add(map);
	
	map = new HashMap<String, Object>();
	map.put("title", "Фрагмент 3");
	map.put("fragment",
			Fragment.instantiate(this, FragmentC.class.getName()));
	data.add(map);
	
	SimpleAdapter adapter = new SimpleAdapter(this, data,
			android.R.layout.simple_spinner_dropdown_item,
			new String[] { "title" }, new int[] { android.R.id.text1 });

	actionBar.setListNavigationCallbacks(adapter,
			new OnNavigationListener() {
				@Override
				public boolean onNavigationItemSelected(int itemPosition,
						long itemId) {
					Map<String, Object> map = data.get(itemPosition);
					Object o = map.get("fragment");
					if (o instanceof Fragment) {
						FragmentTransaction tx = getFragmentManager()
								.beginTransaction();
						tx.replace(android.R.id.content, (Fragment) o);
						tx.commit();
					}
					return true;
				}
			});
}

Метод onNavigationItemSelected() вызывается, когда пользователь выбирает пункт из выпадающего списка. При этом мы получаем экземпляр Fragment, добавленный в data и заменяем им текущий контент экрана.

ActionBar Spinner

При создании нового проекта через мастер, если вы выберите шаблон BlankActivity, то в поле Navigation Type можете выбрать вариант ActionBar Spinner, который сгенерирует нужный код.

Обычный Spinner

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

Добавим в меню (res/menu/action_items.xml) новый элемент:


<item
    android:id="@+id/menu_spinner"
    android:actionViewClass="android.widget.Spinner"
    android:showAsAction="always"
    android:title="Месяцы"/>

Параметр always заставит выводит выпадающий список всегда на панели действий. Заполним список массивом строк в файле res/values/strings.xml:


<string-array name="spinner_data">
    <item>Январь</item>
    <item>Февраль</item>
    <item>Март</item>
    <item>Апрель</item>
</string-array>

Осталось связать данные с компонентом Spinner. В коде главной активности пишем код:


// объявим переменную в классе
private MenuItem mSpinnerItem;

@Override
public boolean onCreateOptionsMenu( Menu menu )
{
    getMenuInflater().inflate( R.menu.main, menu );
    mSpinnerItem = menu.findItem( R.id.menu_spinner );
	setupSpinner(mSpinnerItem);
    return true;
}

// вспомогательный метод
private void setupSpinner( MenuItem item )
{
	item.setVisible( getActionBar().getNavigationMode() == ActionBar.NAVIGATION_MODE_LIST );
	View view = item.getActionView();
	if (view instanceof Spinner)
	{
		Spinner spinner = (Spinner) view;
		spinner.setAdapter( ArrayAdapter.createFromResource( this,
				R.array.spinner_data,
				android.R.layout.simple_spinner_dropdown_item ) );
	}
}

Опустим реализацию OnItemSelectedListener для обработки выбора элементов. Запустим код и увидим Spinner с месяцами года.

Spinner ActionBar

Используем правильный Context для ActionBar

В примерах, когда требовалось указать контекст, мы использовали this. Но более грамотным решением будет использование конструкции getActionBar().getThemedContext(), так как панель действий может иметь свою тему, и тогда список будет соответствовать теме ActionBar. В этом случае текст на выпадающем списке будет всегда хорошо виден. Для сравнения посмотрите на выпадающий список названий месяцев. В первом случае использовалось ключевое слово this и текст едва различим, во втором использовался код getActionBar().getThemedContext() и текст стал отлично виден.

ActionBar ActionBar

CustomView

На панель действий также можно добавлять элементы View. Выше мы видели пример, когда SearchView добавлялся как элемент меню. Сейчас мы добавим компонент EditText как самостоятельный элемент. Для этого нужно создать разметку, затем вызвать метод setCustomView() для класса ActionView, а также установить флаг ActionBar.DISPLAY_SHOW_CUSTOM для метода setDisplayOptions().

Подготовим разметку в файле editlayout.xml


<?xml version="1.0" encoding="utf-8"?>
<EditText xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/textfield"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:inputType="textFilter" >

</EditText> 

И напишем код:


actionBar = getActionBar();

actionBar.setCustomView(R.layout.editlayout);
EditText edit = (EditText) actionBar.getCustomView().findViewById(R.id.textfield);

edit.setOnEditorActionListener(new OnEditorActionListener() {

    @Override
    public boolean onEditorAction(TextView v, int actionId,
        KeyEvent event) {
      Toast.makeText(ActionBarActivity.this, "Вы напечатали: " + v.getText(),
          Toast.LENGTH_LONG).show();
      return false;
    }
  });

actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM
        | ActionBar.DISPLAY_SHOW_HOME);

Другой способ с использованием метода setDisplayShowCustomEnabled(), когда мы программно добавляем изображение в центр панели.


// Java
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

    ActionBar actionBar = getSupportActionBar(); // для старых устройств
    actionBar.setDisplayShowCustomEnabled(true);

    ImageView imageView = new ImageView(this);
    imageView.setImageResource(R.drawable.ic_launcher_cat);
    imageView.setScaleType(ImageView.ScaleType.CENTER);
    ActionBar.LayoutParams lp = new ActionBar.LayoutParams(
            ActionBar.LayoutParams.MATCH_PARENT,
            ActionBar.LayoutParams.MATCH_PARENT);
    actionBar.setCustomView(imageView, lp);
}

// Kotlin val actionBar = supportActionBar val imageView = ImageView(this) imageView.setImageResource(R.mipmap.ic_launcher) imageView.scaleType = ImageView.ScaleType.CENTER val lp: ActionBar.LayoutParams = ActionBar.LayoutParams( ActionBar.LayoutParams.MATCH_PARENT, ActionBar.LayoutParams.MATCH_PARENT ) actionBar?.apply { setDisplayShowCustomEnabled(true) setCustomView(imageView, lp) }

По такому же принципу можно использовать TextView вместо ImageView и настроить его внешний вид (размер, цвет, стиль).


// Kotlin
val actionBar = supportActionBar

val textView = TextView(this)
textView.apply {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        setTextAppearance(android.R.style.TextAppearance_Material_Widget_ActionBar_Title)
    } else {
        setTextSize(TypedValue.COMPLEX_UNIT_SP, 17F)
        setTypeface(null, Typeface.BOLD)
    }
    text = "The cat"
    setTextColor(Color.GREEN)
    typeface = Typeface.SERIF
    paintFlags = Paint.UNDERLINE_TEXT_FLAG
    setTypeface(typeface, Typeface.ITALIC)
}

val lp: ActionBar.LayoutParams = ActionBar.LayoutParams(
    ActionBar.LayoutParams.WRAP_CONTENT,
    ActionBar.LayoutParams.WRAP_CONTENT
)

actionBar?.apply {
    title = ""
    setDisplayShowCustomEnabled(true)
    setCustomView(textView, lp)
}

Если мы используем CustomView, то никто не мешает повесить обработчик щелчков. Для предыдущего примера щелчок будет срабатывать только внутри текста.


// Kotlin
    ...
    setTypeface(typeface, Typeface.ITALIC)
}

textView.setOnClickListener {
    Log.d("ActionBar", "Meow")
}

val lp: ActionBar.LayoutParams = ActionBar.LayoutParams(
...

Кастомизация ActionBar

Следующий пример показывает, как стилизовать ActionBar под общий вид приложения. Будем изменять стандартную тему оформления Holo.Light.


<style name="Theme.AndroidDevelopers" parent="android:style/Theme.Holo.Light">
…
</style>
Список

В стандартном списке используется цветовая схема, в которой доминирует голубой цвет. Чтобы реализовать нужную нам схему, перегрузим android:actionDropDownStyle


<!-- стиль для списка -->
<style name="MyDropDownNav" parent="android:style/Widget.Holo.Light.Spinner.DropDown.ActionBar">
    <item name="android:background">@drawable/ad_spinner_background_holo_light</item>
    <item name="android:popupBackground">@drawable/ad_menu_dropdown_panel_holo_light</item>
    <item name="android:dropDownSelector">@drawable/ad_selectable_background</item>
</style>

В этом xml-файле для оформления подсветки, Spinner и верхней панели используется комбинация state-list'ов и nine-patch изображений.

Вкладки

Для переоформления перегрузим android:actionBarTabStyle.


<!-- стиль для вкладок -->
<style name="MyActionBarTabStyle" parent="android:style/Widget.Holo.Light.ActionBarView_TabView">
    <item name="android:background">@drawable/actionbar_tab_bg</item>
    <item name="android:paddingLeft">32dp</item>
    <item name="android:paddingRight">32dp</item>
</style>

Действия (Actions)

При выделении того или иного, элемента он подсвечивается голубым. Для изменения, перегрузим android:selectableItemBackground


<item name="android:selectableItemBackground">@drawable/ad_selectable_background</item>

Также, меню при расширении показывает голубой прямоугольник наверху списка. Для изменения перегрузим android:popupMenuStyle.


<!-- стиль для меню -->
<style name="MyPopupMenu" parent="android:style/Widget.Holo.Light.ListPopupWindow">
    <item name="android:popupBackground">@drawable/ad_menu_dropdown_panel_holo_light</item> 
</style>

Также изменим цвет выделенных элементов в меню.


<!-- стиль для элементов внутри меню -->
<style name="MyDropDownListView" parent="android:style/Widget.Holo.ListView.DropDown">
    <item name="android:listSelector">@drawable/ad_selectable_background</item>
</style>

Кроме этого еще нужно изменить оформление флажков и радиокнопок.


<item name="android:listChoiceIndicatorMultiple">@drawable/ad_btn_check_holo_light</item>
<item name="android:listChoiceIndicatorSingle">@drawable/ad_btn_radio_holo_light</item>

Фон

В принципе, можно также изменить фон. По умолчанию, в теме Holo.Light он прозрачный. Для изменения нужно перегрузить android:actionBarStyle.

Соединяем всё вместе

Для объединения всех элементов, создадим кастомный стиль.


<style name="Theme.AndroidDevelopers" parent="android:style/Theme.Holo.Light">
    <item name="android:selectableItemBackground">@drawable/ad_selectable_background</item>
    <item name="android:popupMenuStyle">@style/MyPopupMenu</item>
    <item name="android:dropDownListViewStyle">@style/MyDropDownListView</item>
    <item name="android:actionBarTabStyle">@style/MyActionBarTabStyle</item>
    <item name="android:actionDropDownStyle">@style/MyDropDownNav</item>
    <item name="android:listChoiceIndicatorMultiple">@drawable/ad_btn_check_holo_light</item>
    <item name="android:listChoiceIndicatorSingle">@drawable/ad_btn_radio_holo_light</item>
</style>

Теперь можно применять этот стиль оформления к активности или всему приложению.


<activity android:name=".MainActivity" 
    android:label="@string/app_name"
    android:theme="@style/Theme.AndroidDevelopers"
    android:logo="@drawable/ad_logo">

Важно еще отметить, что некоторые стили, которые мы перегрузили, действуют на все виджеты (например android:selectableItemBackground). То есть, мы таким образом изменяем оформление всего приложения, что бывает весьма полезно, если вы стараетесь выдержать общий стиль.<

Дополнительное чтение

Реклама