웹/모바일 안드로이드 기초

2.4 메뉴와 피커

메뉴 개요

메뉴는 여러 옵션의 집합으로 이해할 수 있으며 사용자는 메뉴를 통해 다양한 동작을 수행할 수 있습니다.

메뉴 유형

안드로이드 앱에서는 크게 4가지 방식으로 메뉴를 제공할 수 있습니다.

[그림: 메뉴 유형], 출처: https://developer.android.com
  1. Option menu: 앱 이름이 표시되는 앱바(App Bar 혹은 Action Bar)에 제공되는 메뉴로 오른쪽에 3개의 점을 눌렀을때 나와는 Overflow menu와 그 옆에 나와 있는 아이콘 형태로 제공되는 Action Button들을 말함.
  2. Contextual menu: 특정 상황에 나타나는 메뉴로 예를들어 아이템 항목을 길게 택하는 경우 항목을 수정, 삭제 하는 등의 동작을 지원하기 위한 메뉴
  3. Contextual action bar: 기본 앱바 위에 오버레이 되어 나타나는 메뉴로 상황에 따라 필요한 메뉴를 제공
  4. Pop up menu: ImageButton과 같이 View의 이벤트에 의해 팝업형태로 제공되는 메뉴


App Bar 와 Option Menu

App Bar는 기본적으로 Activity를 생성하면 앱의 상단에 나타나며 Action Bar라고도 합니다. 별다른 설정이 없다면 App Bar에는 앱의 이름이 출력 됩니다.

App Bar는 네 영역으로 구분되며 다음과 같이 구성할 수 있습니다.

[그림: 앱바 구조], 출처: https://developer.android.com
  1. 네비게이션 버튼 혹은 업 버튼: navigation drawer를 오픈하거나 이전/상위 화면으로 이동
  2. 타이틀: 앱의 이름(AndroidManifest.xml의 android:label 속성값)을 출력
  3. 액션 아이콘: Overflow option menu의 항목중 가장 빈번하게 사용되는 메뉴 항목을 아이콘과 함께 출력.
  4. Overflow option menu: 기본 옵션 메뉴항목

Option menu xml 리소스 추가

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:title="New" />
    <item android:title="Search" />
    <item android:title="List" />
</menu>

메뉴 라벨은 문자열 리소스로 변환이 가능하며 아이콘의 경우 레이아웃 에디터와 마찬가지의 방법으로 이미지 리소스를 만든후 사용할 수 있습니다.

메뉴의 순서는 orderInCategory 속성으로 조절할 수 있으며 Action 으로 표시되도록 하는 것은 showAsAction 속성을 이용합니다.

   android:orderInCategory="40"
   app:showAsAction="ifRoom"

메뉴 리소스 적용

menu xml 리소스만으로는 실행시 메뉴가 보이지 않으며 MainActivity.java 에서 메뉴 Inflate 를 진행해야 합니다.

MainActivity.java

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

메뉴 이벤트 처리

메뉴 아이템의 onClick 속성을 이용해 일반적인 Button 등의 이벤트 처리와 동일하게 처리하는 것이 가능합니다. 각각의 메뉴 선택에 따라 특별히 수행해야 하는 작업이 분리되어 있다면 개별적으로 onClick 속성을 사용하는 것이 좋고 그렇지 않다면 MainActivityonOptionsItemSelected()메서드를 오버라이딩해서 선택된 메뉴에 따라 이벤트 처리를 할 수 있도록 코드를 작성해도 됩니다.

public boolean onOptionsItemSelected(@NonNull MenuItem item) {
    switch (item.getItemId()) {
        case R.id.action_new:
            Toast.makeText(this, "New Member", Toast.LENGTH_SHORT).show();
            break;
        ...
    }
    return super.onOptionsItemSelected(item);
}

프로그램 코드로 App Bar 핸들링

App Bar 는 Activity 클래스에서 getSupportActionBar() 메서드를 이용해 참조 가능하며 제공되는 메서들 통해 다양한 속성값을 변경하는 것이 가능 합니다.

ActionBar actionBar = getSupportActionBar();
actionBar.setTitle("New Title");
...


컨텍스트 메뉴(Context Menu)

컨텍스트 메뉴는 상황에 따라 필요한 기능을 제공하는 메뉴로 보통 목록 형태의 데이터를 보여주는 RecyclerViewGridView 에서 아이템을 길게 누르면 팝업되는 메뉴의 형태를 말합니다.

컨텍스트 메뉴는 두가지 방식으로 구현할 수 있는데 하나는 앞에서 설명한 (floating) context menu 이고 다른 하나는 contextual action bar 입니다.

[그림: context menu, contextual action bar]

Floating context menu

아이템을 길게 누를때 플로팅 메뉴 형태로 나타나는 컨텍스트 메뉴를 말합니다.

App Bar 메뉴와 유사하게 리소스 확장 디자인 패턴을 사용하며 Activity 클래스의 registerForContextMenu() 메서드를 사용해 플로팅 컨텍스트 메뉴를 사용할 View 클래스를 등록 합니다.

메뉴 리소스 생성

먼저 menu 리소스 폴더에 새로운 xml 파일을 생성 합니다.

menu_fcm.xml

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:title="Photo" />
    <item android:title="Avatar" />
    <item android:title="Reset" />
</menu>

View에 컨텍스트 메뉴 등록

View를 컨텍스트 메뉴에 추가하려면 Activity 에서 registerForContextMenu() 메서드를 사용하면 됩니다.

MainActivity.java

ImageButton photo;
...
photo = findViewById(R.id.photoBtn);
registerForContextMenu(photo);

컨텍스트 메뉴 보여주기

등록된 View에서 롱 클릭 이벤트 발생시 컨텍스트 메뉴를 보여주기 위해서는 onCreateContextMenu() 콜백 메서드를 오버라이딩 해야 합니다.

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

컨텍스트 메뉴 이벤트 처리

컨텍스트 메뉴 선택시 이벤트 처리는 앞에서 살펴본 App Bar의 옵션 메뉴와 동일 합니다. onContextItemSelected() 메서드를 오버라이딩하고 선택된 메뉴 아이템에 따라 처리를 분기하도록 구현 합니다.

Contextual action bar

상황에 따라서는 View 근처에 컨텍스트 메뉴를 보여주는 것이 아니라 Action bar 에 컨텍스트 메뉴를 제공할 수 있습니다. 이 경우 contextual action mode 에서만 View 를 롱클릭 할때 보여지게 됩니다.

구현 과정은 플로팅 컨텍스트 메뉴와 유사하지만 ActionMode 를 구현하고 이를 통해 액션바에 등록하거나 메뉴 선택 이벤트를 처리하는 것이 다릅니다.

메뉴 리소스 생성

먼저 Action Bar 에 나타나기 때문에 아이콘을 추가해 줍니다.

menu_cab.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:id="@+id/cab_photo"
        android:icon="@android:drawable/ic_menu_gallery"
        android:title="photo" />
    <item
        android:id="@+id/cab_avatar"
        android:icon="@drawable/ic_launcher_foreground"
        android:title="avatar" />
    <item
        android:id="@+id/cab_reset"
        android:icon="@android:drawable/ic_delete"
        android:title="reset" />
</menu>

롱클릭 리스너 등록

Action bar 에 컨텍스트 메뉴를 보여주기 원하는 View의 롱클릭 이벤트 리스너를 등록 합니다.

private ActionMode mActionMode;

photo.setOnLongClickListener(new View.OnLongClickListener() {
    @Override
    public boolean onLongClick(View v) {
        /*
        if(mActionMode != null) return false;
        mActionMode = MainActivity.this.startActionMode(mActionModeCallback);
        v.setSelected(true);
        */
        return true;
    }
});

ActionMode 콜백 메서드 구현

ActionMode가 활성화 되었을 경우 Action Bar에 메뉴를 등록 하기 위한 콜백 메서드 입니다.

public ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
    @Override
    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
        MenuInflater inflater = mode.getMenuInflater();
        inflater.inflate(R.menu.menu_cab, menu);
        return true;
    }

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

    @Override
    public void onDestroyActionMode(ActionMode mode) {
        mActionMode = null;
    }
}

이벤트 처리

메뉴 아이템이 선택 되었을때 호출되는 이벤트 콜백 메서드는 mActionModeCallback 메서드의 오버라이딩 메서드중 onActionItemClicked() 에서 처리 합니다.

@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
    switch(item.getItemId()) {
        case R.id.cab_photo: toastMsg("Add Photo");break;
        case R.id.cab_avatar: toastMsg("Add Avatar");break;
        case R.id.cab_reset: toastMsg("Reset to default");break;
        default:return false;
    }
    return true;
}

팝업 메뉴

팝업 메뉴는 버튼등 View 에 고정되어 특정 이벤트에 동작하는 모달(modal)메뉴 입니다. 기본적으로 onClick 속성을 이용해 미리 만들어진 메뉴 xml 파일을 inflate 하는 형식입니다.

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

물론 각 메뉴 아이템의 이벤트 처리는 앞에서와 동일 합니다.

팝업메뉴와 관련해서는 https://developer.android.com를 참고해 주세요



다이얼로그와 피커

다이얼로그는 액티비티의 진행 중간에 화면위 혹은 화면을 가득 채워 나타나는 대화상자를 말합니다. 경고 혹은 네, 아니오와 같은 확인이나 로그인 사용자 선택, 벨소리 선택등 사용자로부터 입력을 받기 위해 사용하는 위젯 입니다.

화면 구성은 텍스트와 버튼 혹은 라디오 버튼 이나 달력, 시계 등 다양하게 가능 하며 기본적으로 Dialog 클래스를 사용하고 있습니다. 커스텀 다이얼로그 클래스를 만드는 것이 아니라면 일반적으로 다음의 세가지 서브 클래스 중 하나를 사용하면 됩니다.

Alert Dialog 사용

경고 다이얼로그는 사용자로 부터 무언가 확인을 받기 위해 사용하며 텍스트와 함께 보통 수락/거절 버튼으로 구성 됩니다.

Alert Dialog Build

경고 다이얼로그는 빌더 패턴을 사용하고 있으므로 필요한 속성을 조합하는 형식으로 손쉽게 다이얼로그를 구성할 수 있습니다.

AlertDialog.Builder myAlertBuilder = new AlertDialog.Builder(MainActivity.this);
myAlertBuilder.setTitle(R.string.alert_title);
myAlertBuilder.setMessage(R.string.alert_message);

Alert Dialog 버튼 동작 처리

myAlertBuilder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
      public void onClick(DialogInterface dialog, int which) {
        // 이벤트 처리 코드 구현
      }
});

myAlertBuilder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
      public void onClick(DialogInterface dialog, int which) {
        // 이벤트 처리 코드 구현
      }
});

Alert Dialog 표시

화면에 대화상자를 표시하려면 show() 메시지를 호출하면 됩니다.

myAlertBuilder.show();

Date Time Picker 사용

날짜나 시간을 선택받기 위한 피커의 경우 DatePickerDialog, TimePickerDialog 클래스를 사용할 수 있습니다. 사용 방법은 비교적 간단하며 각각의 클래스 인스턴스를 생성후 날짜 혹은 시간 선택값을 받아오기 위한 이벤트 핸들러를 등록해 주면 됩니다.

DatePickerDialog

특정 이벤트 발생시 DatePickerDialog 객체를 생성하고 setOnDateSetListener() 를 구현해 선택된 날짜 정보를 처리 합니다.

이때 기본으로 날짜를 지정하려면 dialog.getDatePicker().updateDate();를 사용하고 minDate() ,maxDate() 를 이용해 선택가능한 범위를 지정할수도 있습니다.

DatePickerDialog dialog = new DatePickerDialog(this);
dialog.setOnDateSetListener(new DatePickerDialog.OnDateSetListener() {
    public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
        birthDay.setText(year + ". " + month + ". " + dayOfMonth);
    }
});
dialog.show();

TimePickerDialog

DatePickerDialog 와 다르게 객체 생성시 이벤트 리스너와 기본 시간값을 함께 전달해야 합니다. 그외 나머지는 다른 Dialog 와 유사합니다.

TimePickerDialog dialog = new TimePickerDialog(MainActivity.this, new TimePickerDialog.OnTimeSetListener() {
    @Override
    public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
        // 시간 선택시 이벤트 처리
    }
},0,0,false);
dialog.show();

실습 및 참고자료

실습: Option menu, Context menu 및 다이얼로그와 피커등이 적용된 회원정보 등록 앱

2.3 에서 만든 회원등록 앱을 확장해서 구현 합니다.

본 예제는 다음과 같은 내용들을 학습하기 위한 내용으로 구성 됩니다.

프로젝트 준비

실행 결과

따라하기 및 코드 설명

유튜브 동영상 강좌를 참고해 주세요


참고자료