Google은 흑인 공동체를 위한 인종 간 평등을 진전시키기 위해 노력하고 있습니다. Google에서 어떤 노력을 하고 있는지 확인하세요.

메뉴

메뉴는 수많은 유형의 애플리케이션에서 사용되는 보편적인 사용자 인터페이스 구성 요소입니다. 친숙하고 일관적인 사용자 경험을 제공하기 위해 Menu API를 사용하여 활동에서 사용자 작업과 다른 옵션을 제공해야 합니다.

Android 3.0(API 수준 11)부터 Android로 구동하는 기기는 전용 Menu 버튼을 제공하지 않아도 됩니다. 이번 변경사항이 적용된 이후에 Android 앱은 종래의 6개 항목으로 구성된 메뉴 패널에 의존하는 방식에서 벗어나서 그 대신 앱 바를 통해 일반적인 사용자 작업을 제공해야 합니다.

일부 메뉴 항목의 설계와 사용자 경험이 변경되었지만, 작업과 옵션 세트를 정의하는 의미 체계 세트는 여전히 Menu API에 기초합니다. 이 가이드는 모든 버전의 Android에서 세 가지 기본적인 유형의 메뉴 또는 작업 표시를 생성하는 방법을 보여줍니다.

옵션 메뉴 및 앱 바
옵션 메뉴는 활동과 관련된 메뉴 항목의 기본 모음입니다. 이 모음에 '검색', '이메일 작성', '설정'과 같은 앱 전체에 영향을 미치는 작업을 배치해야 합니다.

옵션 메뉴 만들기 섹션을 참고하세요.

컨텍스트 메뉴 및 상황별 작업 모드
컨텍스트 메뉴는 사용자가 요소를 길게 클릭하면 나타나는 플로팅 메뉴입니다. 이 메뉴의 작업은 선택된 콘텐츠나 컨텍스트 프레임에 영향을 미칩니다.

상황별 작업 모드는 선택된 콘텐츠에 영향을 미치는 작업 항목을 화면 상단의 막대에 표시하고 사용자가 여러 개의 항목을 선택할 수 있도록 해 줍니다.

컨텍스트 메뉴 만들기에 관한 섹션을 참고하세요.

팝업 메뉴
팝업 메뉴는 메뉴를 호출한 뷰에 고정된 항목 목록을 수직 목록으로 표시합니다. 이는 특정 콘텐츠와 관련된 작업의 오버플로를 제공하거나 명령의 두 번째 부분과 관련해 옵션을 제공하는 데 좋습니다. 팝업 메뉴의 작업은 해당 콘텐츠에 직접적으로 영향을 미쳐서는 안 됩니다. 상황별 작업이 이 용도로 사용되기 때문입니다. 팝업 메뉴는 그보다는 활동의 콘텐츠 영역과 관련 있는 확장 작업을 위한 용도입니다.

팝업 메뉴 만들기 섹션을 참고하세요.

XML로 메뉴 정의

Android는 모든 메뉴 유형과 관련해 메뉴 항목을 정의하기 위한 표준 XML 형식을 제공합니다. 활동 코드에서 메뉴를 빌드하는 대신 XML 메뉴 리소스에서 메뉴와 관련 모든 항목을 정의해야 합니다. 그러면 활동이나 프래그먼트에서 메뉴 리소스를 확장할 수 있습니다(Menu 객체로 로드할 수 있음).

메뉴 리소스를 사용하는 것이 좋은 이유에는 다음과 같은 몇 가지가 있습니다.

  • 메뉴 구조를 XML로 시각화하기가 더 쉽습니다.
  • 애플리케이션의 동작 코드에서 메뉴 콘텐츠를 분리합니다.
  • 앱 리소스 프레임워크를 활용하여 다양한 플랫폼 버전, 화면 크기 및 기타 구성에 관한 대체 메뉴 설정을 생성할 수 있습니다.

메뉴를 정의하려면 프로젝트의 res/menu/ 디렉터리에서 XML 파일을 생성하고 다음 요소로 메뉴를 빌드합니다.

<menu>
메뉴 항목의 컨테이너인 Menu를 정의합니다. <menu> 요소는 파일의 루트 노드여야 하고 하나 이상의 <item><group> 요소를 보유할 수 있습니다.
<item>
메뉴 내 단일 항목을 나타내는 MenuItem을 생성합니다. 이 요소는 하위 메뉴를 생성하기 위한 중첩 <menu> 요소를 포함할 수 있습니다.
<group>
<item> 요소를 위한 투명 컨테이너입니다(선택사항). 이 컨테이너를 사용하면 메뉴 항목을 분류하여 활성 상태와 가시성 등의 속성을 공유할 수 있습니다. 자세한 내용은 메뉴 그룹 만들기에 관한 섹션을 참고하세요.

다음은 game_menu.xml이라는 예시 메뉴입니다.

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/new_game"
          android:icon="@drawable/ic_new_game"
          android:title="@string/new_game"
          android:showAsAction="ifRoom"/>
    <item android:id="@+id/help"
          android:icon="@drawable/ic_help"
          android:title="@string/help" />
</menu>

<item> 요소는 항목의 외관과 동작을 정의하는 데 사용할 수 있는 여러 속성을 지원합니다. 위 메뉴의 항목에는 다음 속성이 포함되어 있습니다.

android:id
항목에 고유한 리소스 ID입니다. 사용자가 항목을 선택하면 애플리케이션이 이 ID를 통해 해당 항목을 인식할 수 있습니다.
android:icon
항목의 아이콘으로 사용할 수 있는 드로어블에 관한 참조입니다.
android:title
항목의 제목으로 사용할 문자열에 관한 참조입니다.
android:showAsAction
항목이 앱 바에서 작업 항목으로 표시되어야 하는 시기와 방법을 지정합니다.

이러한 항목은 개발자가 사용해야 하는 가장 중요한 속성이지만 그 외에도 여러 가지 속성이 있습니다. 모든 지원되는 속성에 관한 내용은 메뉴 리소스 문서를 참고하세요.

<menu> 요소를 <item>의 하위 요소로 추가하여 모든 메뉴의 항목에 하위 메뉴를 추가할 수 있습니다. 하위 메뉴는 PC 애플리케이션의 메뉴 바(예: 파일, 편집, 보기)의 항목과 같이 주제별로 체계화할 수 있는 기능이 애플리케이션에 많을 때 유용합니다. 예:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/file"
          android:title="@string/file" >
        <!-- "file" submenu -->
        <menu>
            <item android:id="@+id/create_new"
                  android:title="@string/create_new" />
            <item android:id="@+id/open"
                  android:title="@string/open" />
        </menu>
    </item>
</menu>

활동에서 메뉴를 사용하려면 MenuInflater.inflate()로 메뉴 리소스를 확장해야 합니다(XML 리소스를 프로그래밍 가능한 객체로 변환). 다음 섹션에서는 각 메뉴 유형에 메뉴를 확장하는 방법을 보여줍니다.

옵션 메뉴 만들기

그림 1. 브라우저의 옵션 메뉴.

옵션 메뉴는 '검색', '이메일 작성', '설정'과 같은 현재 활동 컨텍스트와 관련 있는 작업과 기타 옵션을 배치해야 하는 위치입니다.

화면의 옵션 메뉴에 항목이 나타나는 위치는 개발자가 애플리케이션을 어느 버전에서 개발했는지에 따라 달라집니다.

  • Android 2.3.x(API 수준 10) 이하용으로 애플리케이션을 개발한 경우 사용자가 Menu 버튼을 클릭하면 옵션 메뉴의 콘텐츠가 화면 상단에 표시됩니다(그림 1 참조). 열리면 가장 먼저 표시되는 부분이 아이콘 메뉴이고, 이 메뉴에는 최대 여섯 개의 메뉴 항목이 들어 있습니다. 메뉴에 여섯 개가 넘는 항목이 있는 경우, Android는 여섯 번째 항목과 나머지 항목을 더보기 메뉴에 배치합니다. 이 항목들은 사용자가 More를 선택하면 열 수 있습니다.
  • Android 3.0(API 수준 11) 이상용으로 애플리케이션을 개발한 경우, 옵션 메뉴의 항목은 앱 바에서 사용할 수 있습니다. 기본적으로 시스템은 모든 항목을 작업 오버플로에 배치합니다. 사용자는 앱 바 오른쪽에 있는 작업 오버플로 아이콘을 사용하여 이를 표시할 수 있습니다(또는 기기의 Menu 버튼이 사용 가능한 경우 이를 눌러 표시할 수 있음). 중요한 작업에 빠르게 액세스할 수 있도록 하려면 android:showAsAction="ifRoom"을 해당 <item> 요소에 추가하여 몇 가지 항목이 앱 바에 표시되도록 승격하면 됩니다(그림2 참고).

    작업 항목 및 기타 앱 바 동작에 관한 자세한 내용은 앱 바 추가 교육 과정을 참고하세요.

그림 2. 작업 오버플로 버튼을 포함해 몇 가지 버튼이 표시된 Google 스프레드시트 앱.

Activity 서브클래스나 Fragment 서브클래스에서 옵션 메뉴 항목을 선언할 수 있습니다. 활동과 프래그먼트가 모두 옵션 메뉴 항목을 선언할 경우, 이들은 UI에 결합됩니다. 활동의 항목이 먼저 나타나고, 뒤이어 각 프래그먼트의 항목이 나타납니다. 이때 각 프래그먼트가 활동에 추가된 순서를 따릅니다. 필요한 경우, 옮겨야 할 각 <item>에서 android:orderInCategory 속성을 사용하여 메뉴 항목을 다시 정렬할 수 있습니다.

활동에 관한 옵션 메뉴를 지정하려면 onCreateOptionsMenu()를 재정의합니다(프래그먼트는 자체적인 onCreateOptionsMenu() 콜백을 제공함). 이 메서드에서 메뉴 리소스(XML에서 정의됨)를 콜백에서 제공된 Menu로 확장할 수 있습니다. 예:

Kotlin

override fun onCreateOptionsMenu(menu: Menu): Boolean {
    val inflater: MenuInflater = menuInflater
    inflater.inflate(R.menu.game_menu, menu)
    return true
}

자바

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.game_menu, menu);
    return true;
}

또한 add()를 사용하여 메뉴 항목을 추가하고 findItem()으로 항목을 검색한 다음, MenuItem API로 속성을 수정할 수 있습니다.

Android 2.3.x 이하를 대상으로 애플리케이션을 개발한 경우, 사용자가 처음으로 메뉴를 열면 시스템에서 onCreateOptionsMenu()를 호출해 옵션 메뉴를 생성합니다. Android 3.0 이상을 대상으로 개발한 경우, 시스템은 활동을 시작할 때 앱 바에 항목을 표시하기 위해 onCreateOptionsMenu()를 호출합니다.

클릭 이벤트 처리

사용자가 옵션 메뉴에서 항목(앱 바의 작업 항목 포함)을 선택하면, 시스템이 활동의 onOptionsItemSelected() 메서드를 호출합니다. 이 메서드가 선택된 MenuItem을 전달합니다. 항목을 식별하려면 getItemId()를 호출하면 됩니다. 이 메서드는 (메뉴 리소스의 android:id 속성으로 정의되었거나 add() 메서드에 지정된 정수가 포함된) 메뉴 항목의 고유 ID를 반환합니다. 이 ID와 알려진 메뉴 항목을 일치시켜 올바른 작업을 실행할 수 있습니다. 예:

Kotlin

override fun onOptionsItemSelected(item: MenuItem): Boolean {
    // Handle item selection
    return when (item.itemId) {
        R.id.new_game -> {
            newGame()
            true
        }
        R.id.help -> {
            showHelp()
            true
        }
        else -> super.onOptionsItemSelected(item)
    }
}

자바

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle item selection
    switch (item.getItemId()) {
        case R.id.new_game:
            newGame();
            return true;
        case R.id.help:
            showHelp();
            return true;
        default:
            return super.onOptionsItemSelected(item);
    }
}

메뉴 항목을 성공적으로 처리한 경우 true를 반환합니다. 메뉴 항목을 처리하지 않는 경우, onOptionsItemSelected()의 슈퍼클래스 구현을 호출해야 합니다(기본 구현은 false를 반환함).

활동에 프래그먼트가 포함되어 있으면 시스템은 먼저 활동에, 그다음으로 각 프래그먼트에 onOptionsItemSelected()를 호출합니다(각 프래그먼트가 추가된 순서를 따름). 이 작업은 시스템이 true를 반환하거나 모든 프래그먼트가 호출될 때까지 계속됩니다.

팁: Android 3.0에는 android:onClick 속성을 사용하여 XML에서 메뉴 항목의 클릭 시 동작을 정의할 수 있는 기능이 추가되었습니다. 속성 값은 해당 메뉴를 사용하여 활동을 통해 정의한 메서드의 이름이어야 합니다. 메서드는 공개 메서드여야 하며 하나의 MenuItem 매개변수를 수락해야 합니다. 시스템에서 이 메서드를 호출하면 메서드가 선택된 메뉴 항목을 전달합니다. 자세한 내용과 예는 메뉴 리소스 문서를 참고하세요.

팁: 애플리케이션에 여러 활동이 포함되어 있고 그중 일부가 동일한 옵션 메뉴를 제공하는 경우 onCreateOptionsMenu()onOptionsItemSelected() 메서드를 제외하고 아무것도 구현하지 않는 활동을 만드는 것이 좋습니다. 그런 다음 이 클래스를 같은 옵션 메뉴를 공유해야 하는 각 활동에 확장하면 됩니다. 이렇게 하면 메뉴 작업을 처리하는 코드를 하나의 세트로 관리할 수 있고 각 하위 클래스가 메뉴 동작을 상속합니다. 하위 활동 중 하나에 메뉴 항목을 추가하고 싶다면 해당 활동에서 onCreateOptionsMenu()를 재정의합니다. super.onCreateOptionsMenu(menu)를 호출해서 원래 메뉴 항목을 생성하고 나면, menu.add()로 새로운 메뉴 항목을 추가합니다. 각 메뉴 항목의 슈퍼클래스 동작을 재정의할 수도 있습니다.

런타임에 메뉴 항목 변경

시스템은 onCreateOptionsMenu()를 호출한 후 개발자가 입력하는 Menu의 인스턴스를 유지하고, 메뉴가 무효화되지 않는 한 onCreateOptionsMenu()를 다시 호출하지 않습니다. 그러나 개발자는 onCreateOptionsMenu()를 초기 메뉴 상태를 생성할 때만 사용하고, 활동 수명 주기에서 변경할 때는 사용하지 않습니다.

활동 수명 주기에서 발생하는 이벤트에 기반하여 옵션을 수정하고자 할 경우, onPrepareOptionsMenu() 메서드에서 수정할 수 있습니다. 이 메서드는 현재 존재하는 상태 그대로 Menu 객체를 전달하므로 항목을 추가, 삭제, 중지하는 등의 수정이 가능합니다. 프래그먼트는 onPrepareOptionsMenu() 콜백도 제공합니다.

Android 2.3.x 이하에서는 사용자가 옵션 메뉴를 열 때마다(즉 Menu 버튼을 누를 때마다) 시스템에서 onPrepareOptionsMenu()를 호출합니다.

Android 3.0 이상에서는 메뉴 항목이 앱 바에 표시되어 있는 경우 항상 옵션 메뉴가 열려 있는 것으로 간주됩니다. 이벤트가 발생하고 메뉴 업데이트를 수행하고자 하는 경우, invalidateOptionsMenu()를 호출하여 시스템이 onPrepareOptionsMenu()를 호출하도록 요청해야 합니다.

참고: 현재 포커스를 가진 View를 기반으로 한 옵션 메뉴의 항목은 절대로 변경하지 않아야 합니다. 터치 모드(사용자가 트랙볼이나 D패드를 사용하지 않는 경우)에서는 뷰가 포커스를 받을 수 없기 때문에 옵션 메뉴에 있는 항목을 수정할 근거로 포커스를 사용해서는 안 됩니다. View에 상황에 맞는 메뉴 항목을 제공하고자 하는 경우에는 컨텍스트 메뉴를 사용하세요.

컨텍스트 메뉴 만들기

그림 3. 플로팅 컨텍스트 메뉴(왼쪽)와 상황별 작업 모음(오른쪽)의 스크린샷.

컨텍스트 메뉴의 작업은 UI에서 특정 항목이나 컨텍스트 프레임에 영향을 미칩니다. 컨텍스트 메뉴는 어느 뷰에나 제공할 수 있지만 ListViewGridView 또는 사용자가 각 항목에 직접 작업을 수행할 수 있는 기타 뷰 컬렉션의 항목에 흔히 사용됩니다.

상황별 작업을 제공하는 방법에는 두 가지가 있습니다.

  • 첫째, 플로팅 컨텍스트 메뉴를 사용합니다. 사용자가 컨텍스트 메뉴 지원을 선언하는 뷰를 길게 클릭(길게 누름)하면 메뉴가 메뉴 항목의 플로팅 목록으로 표시됩니다(대화상자와 유사함). 사용자는 한 항목에서 한 번에 하나의 상황별 작업을 실행할 수 있습니다.
  • 둘째, 상황별 작업 모드를 사용합니다. 이 모드는 선택된 항목에 영향을 미치는 작업 항목과 함께 상황별 작업 모음을 화면 상단에 표시하는 ActionMode의 시스템 구현입니다. 이 모드가 활성 상태이면 사용자는 여러 개의 항목에서 한 작업을 한꺼번에 수행할 수 있습니다(앱이 이를 허용하는 경우).

참고: 상황별 작업 모드는 Android 3.0(API 수준 11) 이상에서 사용할 수 있으며 사용 가능한 경우 상황별 작업 표시에 선호되는 기술입니다. 앱이 3.0 이하의 버전을 지원할 경우 해당 기기에서는 플로팅 컨텍스트 메뉴로 돌아가야 합니다.

플로팅 컨텍스트 메뉴 만들기

플로팅 컨텍스트 메뉴를 제공하려면 다음과 같이 합니다.

  1. registerForContextMenu()를 호출하여, 컨텍스트 메뉴를 연결해야 하는 View를 등록하고 이 컨텍스트 메뉴에 View를 전달합니다.

    활동이 ListView 또는 GridView를 사용하고 각 항목에서 같은 컨텍스트 메뉴를 제공하게 하고 싶을 경우, ListViewGridViewregisterForContextMenu()에 전달하여 컨텍스트 메뉴에 모든 항목을 등록합니다.

  2. Activity 또는 Fragment에서 onCreateContextMenu() 메서드를 구현합니다.

    등록된 뷰가 길게 클릭 이벤트를 수신하면 시스템에서 onCreateContextMenu() 메서드를 호출합니다. 여기에서 메뉴 항목을 정의하는데, 주로 메뉴 리소스를 확장시키는 방법을 사용합니다. 예:

    Kotlin

    override fun onCreateContextMenu(menu: ContextMenu, v: View,
                            menuInfo: ContextMenu.ContextMenuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo)
        val inflater: MenuInflater = menuInflater
        inflater.inflate(R.menu.context_menu, menu)
    }
    

    자바

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v,
                                    ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.context_menu, menu);
    }
    

    MenuInflater를 사용하면 메뉴 리소스에서 컨텍스트 메뉴를 확장할 수 있습니다. 콜백 메서드 매개변수에는 사용자가 선택한 View와 선택된 항목에 관한 추가 정보를 제공하는 ContextMenu.ContextMenuInfo 객체가 포함됩니다. 활동에 여러 개의 뷰가 있고 이들이 각각 서로 다른 컨텍스트 메뉴를 제공하는 경우, 이러한 매개변수를 사용하여 확장할 컨텍스트 메뉴를 정할 수 있습니다.

  3. onContextItemSelected()를 구현합니다.

    사용자가 메뉴 항목을 선택할 경우, 시스템은 이 메서드를 호출하여 개발자가 적절한 작업을 할 수 있게 해줍니다. 예:

    Kotlin

    override fun onContextItemSelected(item: MenuItem): Boolean {
        val info = item.menuInfo as AdapterView.AdapterContextMenuInfo
        return when (item.itemId) {
            R.id.edit -> {
                editNote(info.id)
                true
            }
            R.id.delete -> {
                deleteNote(info.id)
                true
            }
            else -> super.onContextItemSelected(item)
        }
    }
    

    자바

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
        switch (item.getItemId()) {
            case R.id.edit:
                editNote(info.id);
                return true;
            case R.id.delete:
                deleteNote(info.id);
                return true;
            default:
                return super.onContextItemSelected(item);
        }
    }
    

    getItemId() 메서드는 선택된 메뉴 항목의 ID를 쿼리합니다. 이 ID는 XML에서 메뉴 정의 섹션에 설명되어 있듯이 android:id 속성을 사용하여 XML의 각 메뉴 항목에 할당해야 합니다.

    메뉴 항목을 성공적으로 처리한 경우 true를 반환합니다. 메뉴 항목을 처리하지 않는 경우, 해당 메뉴 항목을 슈퍼클래스 구현에 전달해야 합니다. 활동에 프래그먼트가 포함되어 있는 경우 해당 활동이 먼저 이 콜백을 수신합니다. 처리되지 않을 때 슈퍼클래스를 호출하면 시스템은 이벤트를 각 프래그먼트의 해당 콜백 메서드에 전달합니다. 이때 true 또는 false가 반환될 때까지 (각 프래그먼트가 추가된 순서대로) 한 번에 하나씩 전달합니다. (Activityandroid.app.Fragment의 기본 구현은 false를 반환하므로 처리되지 않을 때는 언제나 슈퍼클래스를 호출해야 합니다.)

상황별 작업 모드 사용

상황별 작업 모드는 사용자 상호작용을 상황별 작업 수행에 집중시키는 ActionMode의 시스템 구현입니다. 사용자가 항목을 선택하여 이 모드를 사용 설정하면, 상황별 작업 모음이 상단에 표시되고 사용자가 현재 선택된 항목에 수행할 수 있는 작업을 표시합니다. 이 모드가 사용 설정되면 사용자는 여러 항목을 선택하거나(개발자가 이를 허용하는 경우) 항목을 선택 해제하거나 활동 내에서 탐색을 계속할 수 있습니다(개발자가 허용하는 범위 내에서). 사용자가 모든 항목을 선택 해제하거나 'BACK' 버튼을 누르거나 작업 모음 왼쪽의 Done 작업을 누르면 이 작업 모드가 꺼지고 상황별 작업 모음이 사라집니다.

참고: 상황별 작업 모음이 반드시 앱 바와 연결되지는 않습니다. 시각적으로는 상황별 작업 모음이 앱 바 위치를 가리는 것처럼 보이지만 이 두 메뉴는 서로 독립적으로 작동합니다.

상황별 작업을 제공하는 뷰의 경우, 일반적으로 두 이벤트 중 하나(또는 두 가지 모두)에서 상황별 작업 모드를 호출해야 합니다.

  • 사용자가 뷰를 길게 클릭하는 경우.
  • 사용자가 뷰 내에서 체크박스 또는 그와 유사한 UI 구성 요소를 선택하는 경우.

애플리케이션이 상황별 작업 모드를 호출하는 방법과 각 작업 동작을 정의하는 방법은 설계에 따라 다릅니다. 기본적으로 두 가지 설계가 있습니다.

  • 개별적 임의 뷰와 관련된 상황별 작업을 위한 경우.
  • ListView 또는 GridView의 항목 그룹과 관련된 상황별 일괄 작업을 위한 경우(사용자가 여러 항목을 선택하고 항목 모두에 대해 작업을 수행할 수 있도록 허용).

다음 섹션은 각 시나리오에 필요한 설정을 설명합니다.

개별 뷰에 상황별 작업 모드 사용 설정

사용자가 특정 뷰를 선택했을 때만 상황별 작업 모드를 호출하고자 하는 경우 다음과 같이 해야 합니다.

  1. ActionMode.Callback 인터페이스를 구현합니다. 콜백 메서드에서 상황별 작업 모음의 작업을 지정하고, 작업 항목에 관한 클릭 이벤트에 응답하고, 작업 모드의 다른 수명 주기 이벤트를 처리할 수 있습니다.
  2. 작업 모음을 표시하고자 하는 경우(예: 사용자가 뷰를 길게 클릭하는 경우) startActionMode()를 호출합니다.

예:

  1. ActionMode.Callback 인터페이스 구현:

    Kotlin

    private val actionModeCallback = object : ActionMode.Callback {
        // Called when the action mode is created; startActionMode() was called
        override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
            // Inflate a menu resource providing context menu items
            val inflater: MenuInflater = mode.menuInflater
            inflater.inflate(R.menu.context_menu, menu)
            return true
        }
    
        // Called each time the action mode is shown. Always called after onCreateActionMode, but
        // may be called multiple times if the mode is invalidated.
        override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
            return false // Return false if nothing is done
        }
    
        // Called when the user selects a contextual menu item
        override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
            return when (item.itemId) {
                R.id.menu_share -> {
                    shareCurrentItem()
                    mode.finish() // Action picked, so close the CAB
                    true
                }
                else -> false
            }
        }
    
        // Called when the user exits the action mode
        override fun onDestroyActionMode(mode: ActionMode) {
            actionMode = null
        }
    }
    

    자바

    private ActionMode.Callback actionModeCallback = new ActionMode.Callback() {
    
        // Called when the action mode is created; startActionMode() was called
        @Override
        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
            // Inflate a menu resource providing context menu items
            MenuInflater inflater = mode.getMenuInflater();
            inflater.inflate(R.menu.context_menu, menu);
            return true;
        }
    
        // Called each time the action mode is shown. Always called after onCreateActionMode, but
        // may be called multiple times if the mode is invalidated.
        @Override
        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
            return false; // Return false if nothing is done
        }
    
        // Called when the user selects a contextual menu item
        @Override
        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
            switch (item.getItemId()) {
                case R.id.menu_share:
                    shareCurrentItem();
                    mode.finish(); // Action picked, so close the CAB
                    return true;
                default:
                    return false;
            }
        }
    
        // Called when the user exits the action mode
        @Override
        public void onDestroyActionMode(ActionMode mode) {
            actionMode = null;
        }
    };
    

    이벤트 콜백은 옵션 메뉴의 콜백과 거의 똑같으며, 각 이벤트 콜백이 이벤트와 연결된 ActionMode 객체를 함께 전달한다는 점만 다릅니다. ActionMode API를 사용하면 CAB를 다양하게 변경할 수 있습니다. 예를 들어 setTitle()setSubtitle()을 사용하여 제목과 하위 제목을 수정할 수 있습니다(몇 개의 항목이 선택되었는지 나타낼 때 유용함).

    또한 위 샘플은 작업 모드가 소멸될 때 actionMode 변수를 null로 설정한다는 점도 유의하세요. 다음 단계에서는 활동이나 프래그먼트의 멤버 변수를 초기화하고 저장하는 방법을 살펴볼 것입니다.

  2. startActionMode()를 호출하여 필요 시(예: View를 길게 클릭한 동작에 대한 반응으로) 상황별 작업 모드를 사용 설정합니다.

    Kotlin

    someView.setOnLongClickListener { view ->
        // Called when the user long-clicks on someView
        when (actionMode) {
            null -> {
                // Start the CAB using the ActionMode.Callback defined above
                actionMode = activity?.startActionMode(actionModeCallback)
                view.isSelected = true
                true
            }
            else -> false
        }
    }
    

    자바

    someView.setOnLongClickListener(new View.OnLongClickListener() {
        // Called when the user long-clicks on someView
        public boolean onLongClick(View view) {
            if (actionMode != null) {
                return false;
            }
    
            // Start the CAB using the ActionMode.Callback defined above
            actionMode = getActivity().startActionMode(actionModeCallback);
            view.setSelected(true);
            return true;
        }
    });
    

    startActionMode()를 호출하면 시스템이 생성된 ActionMode를 반환합니다. 이 요소를 멤버 변수에 저장하여 다른 이벤트에 관한 응답으로 상황별 작업 모음을 변경할 수 있습니다. 위 샘플에서 ActionModeActionMode 인스턴스가 이미 활성 상태인 경우 작업 모드를 시작하기 전에 구성원이 null인지 확인함으로써 이 인스턴스가 다시 생성되지 않도록 하기 위해 사용됩니다.

ListView 또는 GridView에 상황별 일괄 작업 사용 설정

ListView 또는 GridView(또는 AbsListView의 다른 확장)에 항목 모음이 있고 사용자에게 일괄 작업을 수행하도록 허용하려면 다음과 같이 해야 합니다.

예:

Kotlin

val listView: ListView = getListView()
with(listView) {
    choiceMode = ListView.CHOICE_MODE_MULTIPLE_MODAL
    setMultiChoiceModeListener(object : AbsListView.MultiChoiceModeListener {
        override fun onItemCheckedStateChanged(mode: ActionMode, position: Int,
                                               id: Long, checked: Boolean) {
            // Here you can do something when items are selected/de-selected,
            // such as update the title in the CAB
        }

        override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
            // Respond to clicks on the actions in the CAB
            return when (item.itemId) {
                R.id.menu_delete -> {
                    deleteSelectedItems()
                    mode.finish() // Action picked, so close the CAB
                    true
                }
                else -> false
            }
        }

        override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
            // Inflate the menu for the CAB
            val menuInflater: MenuInflater = mode.menuInflater
            menuInflater.inflate(R.menu.context, menu)
            return true
        }

        override fun onDestroyActionMode(mode: ActionMode) {
            // Here you can make any necessary updates to the activity when
            // the CAB is removed. By default, selected items are deselected/unchecked.
        }

        override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
            // Here you can perform updates to the CAB due to
            // an <code><a href="/reference/android/view/ActionMode.html#invalidate()">invalidate()</a></code> request
            return false
        }
    })
}

자바

ListView listView = getListView();
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
listView.setMultiChoiceModeListener(new MultiChoiceModeListener() {

    @Override
    public void onItemCheckedStateChanged(ActionMode mode, int position,
                                          long id, boolean checked) {
        // Here you can do something when items are selected/de-selected,
        // such as update the title in the CAB
    }

    @Override
    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
        // Respond to clicks on the actions in the CAB
        switch (item.getItemId()) {
            case R.id.menu_delete:
                deleteSelectedItems();
                mode.finish(); // Action picked, so close the CAB
                return true;
            default:
                return false;
        }
    }

    @Override
    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
        // Inflate the menu for the CAB
        MenuInflater inflater = mode.getMenuInflater();
        inflater.inflate(R.menu.context, menu);
        return true;
    }

    @Override
    public void onDestroyActionMode(ActionMode mode) {
        // Here you can make any necessary updates to the activity when
        // the CAB is removed. By default, selected items are deselected/unchecked.
    }

    @Override
    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
        // Here you can perform updates to the CAB due to
        // an <code><a href="/reference/android/view/ActionMode.html#invalidate()">invalidate()</a></code> request
        return false;
    }
});

이상입니다. 이제 사용자가 항목을 길게 클릭하여 선택하면 시스템이 onCreateActionMode() 메서드를 호출하고 지정된 작업이 포함된 상황별 작업 모음을 표시합니다. 상황별 작업 모음이 표시되는 동안 사용자가 추가 항목을 선택할 수 있습니다.

상황별 작업이 공통 작업 항목을 제공하는 일부 경우에는 사용자가 길게 클릭하는 동작을 발견하지 못할 수 있기 때문에, 사용자가 항목을 선택할 수 있는 체크박스나 그와 비슷한 UI 요소를 추가하고자 할 수 있습니다. 사용자가 체크박스를 선택하면 개발자는 setItemChecked()로 확인된 상태에 각 목록 항목을 설정하여 상황별 작업 모드를 호출할 수 있습니다.

팝업 메뉴 만들기

그림 4. Gmail 앱의 오른쪽 위에 있는 오버플로 버튼에 고정된 팝업 메뉴.

PopupMenuView에 고정된 모달 메뉴입니다. 이 메뉴는 앵커 뷰 아래에 공간이 있으면 아래에, 없으면 뷰 위에 나타납니다. 이는 다음과 같은 상황에 유용합니다.

  • 특정 콘텐츠와 관련된 작업에 더 보기 스타일 메뉴를 제공하는 경우(예: 그림 4의 Gmail 이메일 헤더).

    참고: 이 메뉴는 일반적으로 선택된 콘텐츠에 영향을 미치는 작업의 컨텍스트 메뉴와는 다릅니다. 선택된 콘텐츠에 영향을 미치는 작업에는 상황별 작업 모드 또는 플로팅 컨텍스트 메뉴를 사용하세요.

  • 명령어 문장의 두 번째 부분을 제공하는 경우(예: 다양한 'Add' 옵션이 있는 팝업 메뉴를 생성하는 'Add' 버튼).
  • 영구적인 선택이 포함되지 않은 Spinner와 유사한 드롭다운을 제공하는 경우.

참고: PopupMenu는 API 수준 11 이상에서 이용할 수 있습니다.

XML로 메뉴를 정의하는 경우, 팝업 메뉴를 표시하는 방법은 다음과 같습니다.

  1. 해당 생성자를 사용하여 PopupMenu를 인스턴스화합니다. 생성자는 현재 애플리케이션 ContextView를 메뉴가 고정되어야 하는 위치로 가져갑니다.
  2. MenuInflater를 사용하여 PopupMenu.getMenu()가 반환하는 Menu 객체로 메뉴 리소스를 확장합니다.
  3. PopupMenu.show()를 호출합니다.

예를 들어 다음은 팝업 메뉴를 표시하는 android:onClick 속성이 있는 버튼입니다.

<ImageButton
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/ic_overflow_holo_dark"
    android:contentDescription="@string/descr_overflow_button"
    android:onClick="showPopup" />

그러면 활동이 이렇게 팝업 메뉴를 표시할 수 있습니다.

Kotlin

fun showPopup(v: View) {
    val popup = PopupMenu(this, v)
    val inflater: MenuInflater = popup.menuInflater
    inflater.inflate(R.menu.actions, popup.menu)
    popup.show()
}

자바

public void showPopup(View v) {
    PopupMenu popup = new PopupMenu(this, v);
    MenuInflater inflater = popup.getMenuInflater();
    inflater.inflate(R.menu.actions, popup.getMenu());
    popup.show();
}

API 수준 14 이상의 경우, PopupMenu.inflate()로 메뉴를 확장하는 두 개의 줄을 결합할 수 있습니다.

사용자가 항목을 선택하거나 메뉴 영역 바깥쪽을 터치하면 메뉴가 닫힙니다. PopupMenu.OnDismissListener를 사용하여 해제 이벤트를 수신 대기할 수 있습니다.

클릭 이벤트 처리

사용자가 메뉴 항목을 선택할 때 특정 작업을 수행하려면 개발자는 PopupMenu.OnMenuItemClickListener 인터페이스를 구현한 후 setOnMenuItemclickListener()를 호출하여 PopupMenu에 등록해야 합니다. 사용자가 항목을 선택하면 시스템이 인터페이스에서 onMenuItemClick() 콜백을 호출합니다.

예:

Kotlin

fun showMenu(v: View) {
    PopupMenu(this, v).apply {
        // MainActivity implements OnMenuItemClickListener
        setOnMenuItemClickListener(this@MainActivity)
        inflate(R.menu.actions)
        show()
    }
}

override fun onMenuItemClick(item: MenuItem): Boolean {
    return when (item.itemId) {
        R.id.archive -> {
            archive(item)
            true
        }
        R.id.delete -> {
            delete(item)
            true
        }
        else -> false
    }
}

자바

public void showMenu(View v) {
    PopupMenu popup = new PopupMenu(this, v);

    // This activity implements OnMenuItemClickListener
    popup.setOnMenuItemClickListener(this);
    popup.inflate(R.menu.actions);
    popup.show();
}

@Override
public boolean onMenuItemClick(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.archive:
            archive(item);
            return true;
        case R.id.delete:
            delete(item);
            return true;
        default:
            return false;
    }
}

메뉴 그룹 만들기

메뉴 그룹은 특정한 특징을 공유하는 메뉴 항목의 컬렉션입니다. 그룹으로 다음과 같은 작업을 할 수 있습니다.

메뉴 리소스의 <group> 요소 안에 <item> 요소를 중첩하거나 add() 메서드로 그룹 ID를 지정하여 그룹을 생성할 수 있습니다.

다음은 그룹을 포함하는 메뉴 리소스의 예입니다.

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/menu_save"
          android:icon="@drawable/menu_save"
          android:title="@string/menu_save" />
    <!-- menu group -->
    <group android:id="@+id/group_delete">
        <item android:id="@+id/menu_archive"
              android:title="@string/menu_archive" />
        <item android:id="@+id/menu_delete"
              android:title="@string/menu_delete" />
    </group>
</menu>

그룹에 있는 항목은 첫 번째 항목과 같은 수준에서 표시됩니다. 메뉴 안에 있는 세 가지 항목은 모두 형제입니다. 그러나 이 그룹에 있는 항목 두 개의 특징을 수정할 수 있습니다. 그룹 ID를 참조하고 위에 나열된 메서드를 사용하면 됩니다. 시스템 또한 그룹화된 항목은 절대 분리하지 않습니다. 예를 들어 각 항목에 android:showAsAction="ifRoom"을 선언하면 각 항목은 작업 모음에 모두 나타나거나 작업 오버플로에 모두 나타납니다.

선택 가능한 메뉴 항목 사용

그림 5. 선택 가능한 항목이 있는 하위 메뉴의 스크린샷.

메뉴는 옵션을 켜거나 끄기 위한 인터페이스 또는 독립 옵션의 체크박스나 상호 배타적 옵션 그룹의 라디오 버튼을 사용하기 위한 인터페이스로 유용하게 사용할 수 있습니다. 그림 5에는 라디오 버튼으로 선택 가능한 항목이 포함된 하위 메뉴가 나와 있습니다.

참고: 옵션 메뉴의 아이콘 메뉴에 있는 메뉴 항목은 체크박스나 라디오 버튼을 표시할 수 없습니다. 아이콘 메뉴의 항목을 선택 가능하게 만들고자 하는 경우, 상태가 변경될 때마다 아이콘 및/또는 텍스트를 전환하여 선택된 상태를 수동으로 나타내야 합니다.

<item> 요소에서 android:checkable 속성을 사용하여 개별 메뉴 항목에 관한 선택 가능한 동작을 정의하거나 <group> 요소에서 android:checkableBehavior 속성을 사용하여 전체 그룹에 관한 선택 가능한 동작을 정의할 수 있습니다. 예를 들어 이 메뉴 그룹의 모든 항목은 라디오 버튼으로 선택할 수 있습니다.

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <group android:checkableBehavior="single">
        <item android:id="@+id/red"
              android:title="@string/red" />
        <item android:id="@+id/blue"
              android:title="@string/blue" />
    </group>
</menu>

android:checkableBehavior 속성은 다음 중 하나를 받습니다.

single
그룹의 한 항목만 선택할 수 있습니다(라디오 버튼).
all
모든 항목을 선택할 수 있습니다(체크박스).
none
선택 가능한 항목이 없습니다.

<item> 요소에 android:checked 속성을 사용하여 항목에 선택됨 상태(기본값)를 적용하고 setChecked() 메서드를 사용하여 코드 내에서 이를 변경할 수 있습니다.

선택 가능한 항목이 선택되면 시스템이 항목을 선택한 각 콜백 메서드(예: onOptionsItemSelected())를 호출합니다. 여기에서 체크박스 상태를 설정해야 합니다. 체크박스 또는 라디오 버튼으로 체크박스 상태가 자동으로 변경되지 않기 때문입니다. isChecked()로 항목의 현재 상태를 (사용자가 선택하기 전 상태 그대로) 쿼리하고 그런 다음 setChecked()로 확인된 상태를 설정할 수 있습니다. 예:

Kotlin

override fun onOptionsItemSelected(item: MenuItem): Boolean {
    return when (item.itemId) {
        R.id.vibrate, R.id.dont_vibrate -> {
            item.isChecked = !item.isChecked
            true
        }
        else -> super.onOptionsItemSelected(item)
    }
}

자바

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.vibrate:
        case R.id.dont_vibrate:
            if (item.isChecked()) item.setChecked(false);
            else item.setChecked(true);
            return true;
        default:
            return super.onOptionsItemSelected(item);
    }
}

이런 방식으로 선택된 상태를 설정하지 않으면, 사용자가 선택했을 때 항목의 가시적 상태(체크박스 또는 라디오 버튼)가 변경되지 않습니다. 상태를 설정하면 활동은 항목의 선택된 상태를 그대로 보존합니다. 따라서 사용자가 나중에 메뉴를 열면 개발자가 선택하여 설정한 상태가 표시됩니다.

참고: 선택 가능한 메뉴 항목은 세션별 기준으로만 사용하도록 만들어져 있으며 애플리케이션이 제거된 후에는 저장되지 않습니다. 사용자를 위해 저장하고자 하는 애플리케이션 설정이 있으면, 공유 기본 설정으로 해당 데이터를 저장해야 합니다.

인텐트에 기반한 메뉴 항목 추가

활동이 어느 애플리케이션에 속해 있든 관계없이, Intent를 사용하여 활동을 실행하는 메뉴 항목이 필요할 수도 있습니다. 사용하고자 하는 인텐트를 알고 있고 이 인텐트를 시작해야 하는 특정 메뉴 항목이 있을 경우, 적합한 항목 선택 콜백 메서드(예: onOptionsItemSelected() 콜백) 동안 startActivity()를 사용하여 인텐트를 실행할 수 있습니다.

그러나 사용자 기기에 인텐트를 처리하는 애플리케이션이 있는지 모를 때 그 인텐트를 호출하는 메뉴 항목을 추가하면 메뉴 항목이 작동하지 않을 수도 있습니다. 그 인텐트가 어떤 활동인지 확인되지 않을 수 있기 때문입니다. 이 문제를 해결하기 위해 Android에서는 Android가 기기에서 개발자의 인텐트를 처리하는 활동을 찾을 때 개발자가 동적으로 메뉴 항목을 메뉴에 추가할 수 있도록 했습니다.

사용 가능한 활동에 따라 인텐트를 허용하는 메뉴 항목을 추가하려면 다음과 같이 합니다.

  1. 카테고리 CATEGORY_ALTERNATIVE 및/또는 CATEGORY_SELECTED_ALTERNATIVE, 기타 요구사항으로 인텐트를 정의합니다.
  2. Menu.addIntentOptions()를 호출합니다. 그러면 Android가 인텐트를 수행하는 애플리케이션을 검색하고 이들을 개발자의 메뉴에 추가합니다.

인텐트를 만족시키는 애플리케이션이 설치되어 있지 않으면, 메뉴 항목이 추가되지 않습니다.

참고: CATEGORY_SELECTED_ALTERNATIVE는 화면에서 현재 선택된 요소를 처리하는 데 사용됩니다. 그러므로 이 항목은 onCreateContextMenu()에서 메뉴를 생성할 때만 사용해야 합니다.

예:

Kotlin

override fun onCreateOptionsMenu(menu: Menu): Boolean {
    super.onCreateOptionsMenu(menu)

    // Create an Intent that describes the requirements to fulfill, to be included
    // in our menu. The offering app must include a category value of Intent.CATEGORY_ALTERNATIVE.
    val intent = Intent(null, dataUri).apply {
        addCategory(Intent.CATEGORY_ALTERNATIVE)
    }

    // Search and populate the menu with acceptable offering applications.
    menu.addIntentOptions(
            R.id.intent_group,  // Menu group to which new items will be added
            0,                  // Unique item ID (none)
            0,                  // Order for the items (none)
            this.componentName, // The current activity name
            null,               // Specific items to place first (none)
            intent,             // Intent created above that describes our requirements
            0,                  // Additional flags to control items (none)
            null)               // Array of MenuItems that correlate to specific items (none)

    return true
}

자바

@Override
public boolean onCreateOptionsMenu(Menu menu){
    super.onCreateOptionsMenu(menu);

    // Create an Intent that describes the requirements to fulfill, to be included
    // in our menu. The offering app must include a category value of Intent.CATEGORY_ALTERNATIVE.
    Intent intent = new Intent(null, dataUri);
    intent.addCategory(Intent.CATEGORY_ALTERNATIVE);

    // Search and populate the menu with acceptable offering applications.
    menu.addIntentOptions(
         R.id.intent_group,  // Menu group to which new items will be added
         0,      // Unique item ID (none)
         0,      // Order for the items (none)
         this.getComponentName(),   // The current activity name
         null,   // Specific items to place first (none)
         intent, // Intent created above that describes our requirements
         0,      // Additional flags to control items (none)
         null);  // Array of MenuItems that correlate to specific items (none)

    return true;
}

정의된 인텐트와 일치하는 인텐트 필터를 제공하는 각 활동이 발견되면, 인텐트 필터의 android:label의 값을 메뉴 항목 제목으로, 애플리케이션 아이콘을 메뉴 항목 아이콘으로 사용하여 메뉴 항목이 추가됩니다. addIntentOptions() 메서드는 추가된 메뉴 항목 개수를 반환합니다.

참고: addIntentOptions()를 호출하면 첫 번째 인수에 지정된 메뉴 그룹을 통해 모든 메뉴 항목이 재정의됩니다.

다른 메뉴에 활동을 추가하도록 허용

개발자 활동의 서비스를 다른 애플리케이션에 제공하여 다른 애플리케이션의 메뉴에 개발자의 애플리케이션이 추가되도록 할 수도 있습니다(위에서 설명한 역할과 반대임).

다른 애플리케이션 메뉴에 포함하려면 인텐트 필터는 종전처럼 정의하면 되지만 인텐트 필터 카테고리에 CATEGORY_ALTERNATIVE 및/또는 CATEGORY_SELECTED_ALTERNATIVE 값을 반드시 포함해야 합니다. 예:

<intent-filter label="@string/resize_image">
    ...
    <category android:name="android.intent.category.ALTERNATIVE" />
    <category android:name="android.intent.category.SELECTED_ALTERNATIVE" />
    ...
</intent-filter>

인텐트 필터 작성에 관한 자세한 내용은 인텐트와 인텐트 필터 문서를 참고하세요.