В этом уроке:

- работаем с ActionMode

 

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

Создадим проект:

Project name: P1131_ActionMode
Build Target: Android 4.1
Application name: ActionMode
Package name: ru.startandroid.develop.p1131actionmode
Create Activity: MainActivity

 

Добавим строки в strings.xml:

<string name="action_mode">Action Mode</string>
<string name="item1">Item1</string>
<string name="item2">Item2</string>
<string name="item3">Item3</string>
<string name="item4">Item4</string>

 

main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	xmlns:tools="http://schemas.android.com/tools"
	android:id="@+id/LinearLayout1"
	android:layout_width="match_parent"
	android:layout_height="match_parent"
	android:orientation="vertical">
	<Button
		android:id="@+id/btnActionMode"
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:onClick="onClick"
		android:text="@string/action_mode">
	</Button>
</LinearLayout>

Одна кнопка, по нажатию на которую будем показывать/скрывать ActionMode

 

Пункты меню, res/menu/context.xml:

<?xml version="1.0" encoding="utf-8"?>
<menu
	xmlns:android="http://schemas.android.com/apk/res/android">
	<item
		android:id="@+id/item1"
		android:showAsAction="always"
		android:title="@string/item1">
	</item>
	<item
		android:id="@+id/item2"
		android:icon="@android:drawable/ic_menu_call"
		android:showAsAction="ifRoom"
		android:title="@string/item2">
	</item>
	<item
		android:id="@+id/item3"
		android:icon="@android:drawable/ic_menu_info_details"
		android:showAsAction="ifRoom|withText"
		android:title="@string/item3">
	</item>
	<item
		android:id="@+id/item4"
		android:icon="@android:drawable/ic_menu_view"
		android:showAsAction="never"
		android:title="@string/item4">
	</item>
</menu>

Те же пункты, что мы использовали для примеров с ActionBar. Их будет отображать ActionMode.

 

MainActivity.java:

package ru.startandroid.develop.p1131actionmode;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;

public class MainActivity extends Activity {

  ActionMode actionMode;
  final String LOG_TAG = "myLogs";

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

  public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
  }

  public void onClick(View v) {
    if (actionMode == null)
      actionMode = startActionMode(callback);
    else
      actionMode.finish();
  }

  private ActionMode.Callback callback = new ActionMode.Callback() {

    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
      mode.getMenuInflater().inflate(R.menu.context, menu);
      return true;
    }

    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
      return false;
    }

    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
      Log.d(LOG_TAG, "item " + item.getTitle());
      return false;
    }

    public void onDestroyActionMode(ActionMode mode) {
      Log.d(LOG_TAG, "destroy");
      actionMode = null;
    }

  };

}

Чтобы вызвать ActionMode, используется метод startActionMode. На вход он берет объект callback, который будет обрабатывать все события, связанные с ActionMode. На выходе получаем объект ActionMode.

В методе onClick мы проверяем, если ActionMode еще не был вызван, то вызываем. Иначе – убираем его с помощью его же метода finish.

Объект callback реализует интерфейс ActionMode.Callback. Его методы:

onCreateActionMode – вызывается при создании ActionMode. Возвращаем true, если ActionMode можно создавать. Здесь мы наполняем ActionMode пунктами меню (через объект Menu).

onPrepareActionMode – вызывается при обновлении ActionMode. Например, в случае вызова метода invalidate. Возвращаем true, если ActionMode можно обновить.

onActionItemClicked – обработка нажатия на какой-либо пункт ActionMode. Будем выводить в лог текст нажатого пункта.

onDestroyActionMode – вызывается при закрытии ActionMode. Пишем лог и обнуляем переменную actionMode, чтобы в onClick (см.выше) у нас работала проверка (actionMode == null).

 

Все сохраняем, запускаем. Я включу горизонтальную ориентацию, чтобы лучше были видны пункты ActionMode.

 

Жмем кнопку, появляется ActionMode

Располагается он там же, где и ActionBar. Да и выглядит также, только слева у него кнопка закрытия. Пункты меню видны так, как мы описывали в файле context.xml.

 

Нажмем, например, на Item1, смотрим лог.

item Item1

Сработал метод onActionItemClicked.

 

Закрыть ActionMode мы теперь можем снова нажав кнопку Action Mode, либо нажав на галку в левой части, либо нажав кнопку Назад. Во всех этих случаях будет выполнен метод onDestroyActionMode. Опробуйте эти способы и смотрите логи, там должна появиться строка:

destroy

 

Мы рассмотрели способ ручного вызова ActionMode, обрабатывая нажатие на кнопку. Т.к. ActionMode позиционируется как замена контекстному меню, то вполне можно повесить его вызов на долгое нажатие на какой-либо элемент экрана. Чтобы обработать долгое нажатие надо вызывать для элемента метод setOnLongClickListener, передать туда объект, реализующий интерфейс OnLongClickListener, и в методе onLongClick этого объекта накодить вызов ActionMode.

Для некоторых элементов вызов ActionMode по долгому нажатию уже реализован. Это наследники класса AbsListView, например GridView и ListView. Попробуем на ListView, как это работает.

 

Меняем main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	xmlns:tools="http://schemas.android.com/tools"
	android:id="@+id/LinearLayout1"
	android:layout_width="match_parent"
	android:layout_height="match_parent"
	android:orientation="vertical">
	<ListView
		android:id="@+id/lvActionMode"
		android:layout_width="match_parent"
		android:layout_height="wrap_content">
	</ListView>
</LinearLayout>

На экране будет только список.

 

Меняем MainActivity.java:

package ru.startandroid.develop.p1131actionmode;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.AbsListView.MultiChoiceModeListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;

public class MainActivity extends Activity {

  ActionMode actionMode;
  ListView lvActionMode;
  final String LOG_TAG = "myLogs";

  String[] data = { "one", "two", "three", "four", "five" };

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

    ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
        android.R.layout.simple_list_item_activated_1, data);
    lvActionMode = (ListView) findViewById(R.id.lvActionMode);
    lvActionMode.setAdapter(adapter);
    lvActionMode.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
    lvActionMode.setMultiChoiceModeListener(new MultiChoiceModeListener() {

      public boolean onCreateActionMode(ActionMode mode, Menu menu) {
        mode.getMenuInflater().inflate(R.menu.context, menu);
        return true;
      }

      public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
        return false;
      }

      public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
        mode.finish();
        return false;
      }

      public void onDestroyActionMode(ActionMode mode) {
      }

      public void onItemCheckedStateChanged(ActionMode mode,
          int position, long id, boolean checked) {
        Log.d(LOG_TAG, "position = " + position + ", checked = "
            + checked);
      }
    });

  }

  public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
  }

}

В onCreate мы создаем адаптер и присваиваем его списку. Далее для списка мы включаем режим выбора (Урок 43) CHOICE_MODE_MULTIPLE_MODAL (появившийся в API Level 11) и устанавливаем объект обработчик, реализующий AbsListView.MultiChoiceModeListener. Методы здесь все те же, что и ранее нами рассмотренные в ActionMode.Callback, плюс добавляется один – onItemCheckedStateChanged, в котором мы получаем инфу о выделенных пунктах списка. Т.е. этот обработчик и выделение пунктов списка отслеживает и ActionMode контролирует.

В onCreateActionMode мы указываем, из какого файла брать пункты меню, в onActionItemClicked закрываем ActionMode независимо от того, какой пункт меню был выбран, а в onItemCheckedStateChanged просто выводим в лог инфу о выбираемых пунктах списка. Остальные методы не трогаем, сейчас они не нужны.

 

Все сохраняем и запускаем приложение.

 

Долгое нажатие на какой-либо пункт списка вызовет ActionMode

А в логах появится инфа о выбранном пункте:

position = 2, checked = true

 

Теперь мы можем дальше простыми нажатиями выбирать и «развыбирать» пункты списка

и после этого нажать на какой-либо пункт ActionMode для операции над группой выделенных пунктов. Получить список выбранных пунктов можно так же, как в Уроке 43 для множественного выбора.

 

Как нам теперь закрыть ActionMode?

- кнопка отмены (слева)

- кнопка Назад

- любой пункт в ActionMode, т.к. мы повесили вызов метода finish в onActionItemClicked

При закрытии ActionMode «развыделятся» и все выделенные пункты.

Еще, как вариант закрытия ActionMode, – это не оставить ни одного выделенного пункта. Если в процессе выделения пунктов не останется ни одного выделенного, то ActionMode закроется.

 

Для списка в режиме CHOICE_MODE_MULTIPLE_MODAL есть еще один способ, кроме долгого нажатия на пункт, вызвать ActionMode. Это метод setItemChecked. Если выделить этим методом какой-либо пункт, то ActionMode появится.

 

У ActionMode, как и у ActionBar есть методы:

setTitle - установить свой заголовок

setSubtitle - установить свой подзаголовок 

 

На следующем уроке:

- разбираемся, зачем нужна библиотека Support Library
- на примере фрагментов используем библиотеку v4 


Присоединяйтесь к нам в Telegram:

- в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.

- в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Compose, Kotlin, RxJava, Dagger, Тестирование, Performance 

- ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня




Language

Автор сайта

Дмитрий Виноградов

Подробнее можно посмотреть или почитать.

Никакие другие люди не имеют к этому сайту никакого отношения и просто занимаются плагиатом.

Социальные сети

 

В канале я публикую ссылки на интересные и полезные статьи по Android

В чате можно обсудить вопросы и проблемы, возникающие при разработке



Группа ВКонтакте



Поддержка проекта

Яндекс
410011180491924

WebMoney
R248743991365
Z551306702056

Paypal