Android appcompact : 액션바

timothy jeong·2021년 11월 8일
0

Android with Kotlin

목록 보기
22/69

액션바

액티비티의 구성 요소인'액션바(ActionBar)'는 화면 위쪽에 타이틀 문자열이 출력되는 영역을 의미한다.

이 액션바 부분을 제외한 영역을 '콘텐츠'라고 부른다. 즉, 액티비티가 출력되는 전체 창은 액션바 영역과 콘텐츠 영역으로 구분된다.액션바는 타이틀, 네이게이션 아이콘, 액션 아이템, 오버플러 메뉴 등 다양한 요소를 출력할 수 있다. 콘텐츠 영역에는 setContentView() 함수가 출력하는 내용이 출력된다.

액션바 색상 설정

어떤 설정도 하지 않고 안드로이드 앱을 실행하면 앱의 테마가 적용된 액션바가 나타난다. 이러한 테마 스타일은 res/values 디렉터리의 themes.xml 파일에 선언되어 있다. 기본 상태의 thems.xml 은 아래와 같은 모습이다.

<resources xmlns:tools="http://schemas.android.com/tools">
    <!-- Base application theme. -->
    <style name="Theme.Book" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
        <!-- Primary brand color. -->
        <item name="colorPrimary">@color/purple_500</item>
        <item name="colorPrimaryVariant">@color/purple_700</item>
        <item name="colorOnPrimary">@color/white</item>
        <!-- Secondary brand color. -->
        <item name="colorSecondary">@color/teal_200</item>
        <item name="colorSecondaryVariant">@color/teal_700</item>
        <item name="colorOnSecondary">@color/black</item>
        <!-- Status bar color. -->
        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
        <!-- Customize your theme here. -->
    </style>
</resources>

style name="Theme.Book" 는 Them.{ProjectName} 으로 결정된다. res/values 디렉터리 하단에 구성된 Theme 은 메니페스트 파일에서 앱의 테마로 설정된다. 아래는 메니페스트 파일에서 해당하는 부분을 나타낸다.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.testmodule">
  <!-- about permissions -->
  <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Book">
        <activity
                  ...
        </activity>
    </application>
</manifest>

application 태그에서 android:them= 을 통해 테마를 설정한다. 이러한 흐름으로 themes.xml 에 설정된 테마 스타일이자동으로 앱의 액티비티에 테마로 설정되고, 이 테마 파일에 선언된 색상이 액티비티에 적용된다.

themes.xml 의 <style name="Theme.Book" parent = "Theme.MaterialComponents.DayNight.DarkActionBar"> 부분을 보면 테마 파일이 Theme.MaterialComponents.DayNight.DarkActionBar 를 상속받아 작성된다는 것을 알 수 있다. 머티리얼 디자인에서 테마의 속성을 상속한 것인데, 여기서 상속은 속성들이 의미하는 것은 다음과 같다.

  • colorPrimary(주 색상) : 액션바와 버튼의 백그라운드 색상
  • colorOnPrimary : 주색상이 적용된 요소 위에 있는 요소(글자 등)에 적용되는 색상, 전경색(foreground color) 이라고 한다.
  • colorSecondary(부 색상) : 스위치 등의 활성 상태를 나타낸다.
  • colorOnSecondary : 상동
  • android:statusBarColor : 앱 영억 위, 안드로이드 시스템에서 관리하는 상태바(알림, 베터리, 네트워크 정보)의 색상
  • colorPrimaryVariant, colorSecondaryVariant : 각 요소의 그림자 색상을 나타낸다.

이상의 설정값들을 변경함으로써 액션바의 색상을 변경할 수 있다.

colorPrimary 와 colorSecondary 는 디폴트로 앱을 구성하는 화면 요소들에 적용되는 만큼, 브랜드를 표현하는 색상이라고 할 수 있다.

액션바 숨기기

테마를 만들때 Theme.MaterialComponents.DayNight.NoActionBar 를 상속받으면 액션바가 나오지 않는다. 혹은 item 에 다음 요소를 추가하면 액션바를 숨길 수 있다.

        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>

업 버튼 설정

업 버튼은 액티비티 화면이 앱의 첫 화면이 아닐 때 이전 화면으로 되돌아가는 기능을 한다. 업 버튼은 액티비티가 등록되는 메니페스트 파일에서 설정하는 방법과 액티비티 코드로 설정하는 방법이 있다.

업버튼은 메니페스트 파일에서 설정하는 방법과 액티비티 코드로 설정하는 방법이 있다.

매니페스트 파일에서 <activity> 태그에 parentActivityName 속성을 등록하는 것만으로도 액티비티 화면에 업버튼이 나온다. 그래서 사용자가 이 버튼을 누르면 이전 화면으로 되돌아간다.

<activity
          android:name=".TwoActivity"
          android:parentActivityName=".MainActivity">
</activity>

때로는 업 버튼을 눌렀을 때 이전 화면으로 되돌아가기 전에 특별한 로직을 실행하고 싶을 때가 있을 것이다. 이때는 액티비티에 onSupportNavigateUp() 함수를 override 한다. 이렇게 재정의하면 사용자가 업 버튼을 누를 때 onSupportNavigateUp() 함수가 자동으로 호출된다.

class MainActivity : AppCompatActivity() {
    override fun onSupportNavigateUp(): Boolean {
        Log.d("hello", "WORLD")
        return super.onSupportNavigateUp()
    }
    ...
}

이상은 매니페스트에서 업버튼이 나오게하고, 액티비티 코드에서 세부적인 처리를 하는 코드였다. 하지만 처음부터 액티비티 코드에서 업버튼이 나오게하는 방법도 있다.

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        supportActionBar?.setDisplayHomeAsUpEnabled(true)

    }

    override fun onSupportNavigateUp(): Boolean {
        Log.d("hello", "WORLD")
        onBackPressed()
        return super.onSupportNavigateUp()
    }

supportActionBar?.setDisplayHomeAsUpEnabled(true) 구문으로 액션바에 업 버튼이 나온다. 이 때로 업 버튼을 호출하면 onSupportNavigateUp() 함수가 자동으로 호출된다. 그런데 매니페스트 파일의 ParentActivityName 속성이 설정되어 있지 않으면 자동으로 이전 화면으로 되돌아가지 않는다. 따라서 앞의 코드처럼 onSupportNativateUp() 함수에서 onBackpressed() 구문등으로 이전 화면으로 되돌아가는 코드를 직접 작성해 줘야야한다.

메뉴 구성

메뉴는 액션바의 중요한 구성 요소로 액티비티 화면에서 사용자 이벤트를 사용할 수 있도록 한다.

액티비티에 메뉴를 추가하면 액션바 오른쪽에 오버플로 버튼(세로 점 3개)이 나타난다. 사용자가 이 오버플로 버튼을 누르면 메뉴(오버플로 메뉴)가 아래로 확장 되어 나타난다. 그리고 오버플로 메뉴 중에서 몇몇은 액션바에 아이콘으로 나오게 할 수 있다. 이를 액션 아이템이라고 한다. 결국 오버플로 메뉴와 액션 아이템 모두 메뉴이다.

액티비티에 메뉴를 추가하고 싶다면 onCreateOptionMenu() 와 onPrepareOptionMenu() 함수를 이용한다. 두 함수는 액티비티의 메뉴를 구성할 때 자동으로 호출되는데, 차이점은 호출되는 시점이 다르다는 것이다.

onCreateOptionMenu() 함수는 액티비티가 실행되면서 처음에 한 번만 호출된다. 그러나 onPrepareOptionMenu() 함수는 액티비티가 실행되면서 한 번 호출된 후 오버플로 메뉴가 나타날 때마다 반복해서 호출된다. 따라서 onCreateOptionMenu() 는 정적인 메뉴를 구성할때, onPrepareOptionMenu() 는 상황에 따라서 동적으로 메뉴 구성을 바꾸고 싶을때 사용한다.

이상의 액티비티 메뉴는 보통 사용자 이벤트를 처리하려고 사용하는 것이므로 대부분 액티비티 내에 정적으로 제공된다. 즉, 대부분 onCreateOptionMenu() 을 이용한다.

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        // menu.add(groupId: Int, itemId: Int, order: Int, title: CharSequence): MenuItem
        val menuItem1: MenuItem? = menu?.add(0,0,0, "item1")
        val menuItem2: MenuItem? = menu?.add(0,1,0, "item2")
        return super.onCreateOptionsMenu(menu)
    }
}

onCreateOptionsMenu() 함수를 override 하면 오버플로 버튼이 나온다. menu.add(groupId: Int, itemId: Int, order: Int, title: CharSequence): MenuItem 에서 두번째 인자 itemId 는 이벤트 처리가 목적으로, 어떤 메뉴가 클릭되었는지 식별할때 사용한다.

이 메뉴를 클릭했을 때의 이벤트 처리를 위해서는 onOptionsItemSelected() 함수를 이용한다. 이 함수의 매개변수는 이벤트가 발생한 메뉴 객체인 MenuItem 이다. MenuItem의 itemId 속성으로 이벤트가 발생한 메뉴 객체의 식별값을 얻어서 이벤트를 처리한다.

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when(item.itemId) {
            0 -> Log.d("OverFlow Menu", "0")
            1 -> Log.d("OverFlow Menu", "1")
        }
        return super.onOptionsItemSelected(item)
    }

리소스로 메뉴 구현

이전 코드에서는 Menu 객체의 add 함수를 통해 만들었지만, 액티비티 메뉴는 정적으로 제공되므로 대부분 리소스 xml 파일로 구성한다. 메뉴를 구성하는 xml 파일은 res 폴더 아래 menu 디렉터리에 만든다.

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/menu_item1"
        android:title="item1"/>

    <item
        android:id="@+id/menu_item2"
        android:title="item2"/>

    <item
        android:id="@+id/menu_action1"
        android:icon="@android:drawable/ic_menu_add"
        android:title="action1"
        app:showAsAction="always"/>

    <item
        android:id="@+id/menu_action2"
        android:icon="@android:drawable/ic_menu_search"
        android:title="action2"
        app:showAsAction="ifRoom"/>
</menu>

app:showAsAction 속성을 넣으면 그 옵션에 따라 해당 아이템은 액션 아이템이 되어 오버플로 버튼을 누르지 않아도 액션바 위에 나오게 된다. 사용되는 옵션은 다음과 같다.

  • never : 항상 오버플로 메뉴로 출력된다.
  • ifRoom : 만약 액션바에 공간이 있다면 액션 아이템으로, 없다면 오버플로 메뉴로 출력한다.
  • always : 항상 액션 아이템으로 출력한다.

이제 이 xml 파일을 액티비티 코드에 적용해줘야한다.

    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
         menuInflater.inflate(R.menu.menu_main, menu)
        return super.onCreateOptionsMenu(menu)
    }

액션 뷰 이용

액션 뷰는 액션바에서 특별한 기능을 제공하며 대표적으로 androidx.appcompact.widget.SearchView 가 있다. 서치 뷰는 액션바에서 검색 기능을 제공한다.

    <item
        android:id="@+id/menu_search"
        android:title="action2"
        app:showAsAction="always"
        app:actionViewClass="androidx.appcompat.widget.SearchView"/>

이제 search 아이콘을 누르면 텍스트를 입력하는 란이 나온다. 코드에서 검색과 관련된 업무를 진행하려면 SearchView 객체를 얻어야 한다. 서치뷰가 메뉴로 제공되므로 SearchView 를 등록한 MenuItem 객체를 얻고 MenuItem 객체에 등록된 SearchView 객체를 구하면 된다.

override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        val inflater = menuInflater.inflate(R.menu.menu_main, menu)
        var menuItem: MenuItem? = menu?.findItem(R.id.menu_search)
        val searchMenu = menuItem?.actionView as SearchView

        searchMenu.setOnQueryTextListener(object: SearchView.OnQueryTextListener {
            override fun onQueryTextChange(newText: String?): Boolean {
                // 검색어 변경 이벤트
                return true
            }

            override fun onQueryTextSubmit(query: String?): Boolean {
                // 검색 버튼을 클릭한 순간 이벤트
                return true
            }
        })
        return super.onCreateOptionsMenu(menu)
    }

var menuItem = menu?.findItem(R.id.menu_search) 메뉴 item 의 id를 통해 MenuItem 객체를 얻는다. 액션뷰는 MenuItem 에 등록된 actionView 프로퍼티로 얻는다.

profile
개발자

0개의 댓글