본문 바로가기
Android

[Doit 깡샘의 안드로이드 앱 프로그래밍 with 코틀린] 정리 17 - 앱 설정 화면 만들기

by 들풀민들레 2022. 2. 28.
본 글은 [Doit 깡샘의 안드로이드 앱 프로그래밍 with 코틀린 - 이지스퍼블리싱 (2022)] 의 내용을 발췌한 것입니다.

좀더 자세한 내용은 책 혹은 인강을 통해 확인해 주세요.

 

 

 

 

 

대부분 앱은 여러 가지 사용 환경을 설정하는 기능을 제공합니다. 앱의 설정 화면은 액티비티와 사용자 이벤트 처리 그리고 공유된 프리퍼런스 등을 이용해서 구현하지만, 화면이나 설정한 데이터를 저장하는 형태는 거의 비슷합니다. 따라서 많은 앱에서는 설정 화면을 자동으로 만들어 주는 API를 이용합니다. 이 API를 이용하면 개발자가 설정 항목을 정의한 XML만 만들어서 적용하면 됩니다. 그러면 설정 화면과 사용자 이벤트, 데이터 저장까지 자동으로 구현됩니다.


플랫폼 API에서 이처럼 앱의 설정 기능을 자동화해주는 API는 많았지만 안드로이드 10 버전(API 레벨 29)부터 모두 deprecated되었습니다. 그리고 AndroidX의 Preference를 이용할 것을 권장하고 있습니다. AndroidX의 Preference는 앱에서 설정 기능을 제공할 때 이용하는 제트팩의 API입니다.

 

AndroidX의 Preference를 이용하려면 빌드 그래들 파일에 다음과 같은 라이브러리를 dependencies로 선언해야 합니다.

 

implementation 'androidx.preference:preference-ktx:1.1.1'

프리퍼런스 이용 방법


프리퍼런스를 이용해 앱에 설정 기능을 제공하려면 가장 먼저 res/xml 디렉터리에 설정과 관련된 XML 파일을 만들어야 합니다. 설정 XML 파일은 루트 태그가 <PreferenceScreen>이어야 합니다. 그리고 이 태그 하위에 <SwitchPreferenceCompat>, <Preference> 등의 태그를 이용해 설정 항목을 준비합니다.

 

<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
    <SwitchPreferenceCompat
        app:key="notifications"
        app:title="Enable message notifications" />
    <Preference
        app:key="feedback"
        app:title="Send feedback"
        app:summary="Report technical issues or suggest new features" />
</PreferenceScreen>

사용자가 설정 화면에서 설정한 값은 내부적으로 공유된 프리퍼런스를 이용해 키-값 형태로 저장됩니다. 이때 각 설정 항목의 key 속성값이 데이터의 키가 됩니다. 예를 들어 사용자가 <SwitchPreferenceCompat> 태그에 해당하는 설정 항목을 enable로 지정했다면 자동으로 저장되는 데이터는 notifications을 키로 하고 값은 true가 됩니다. 그리고 title 속성은 설정 화면에 출력되는 문자열입니다.


이렇게 만든 XML 파일을 코드에서 적용해야 하는데 이때 PreferenceFragmentCompat 클래스를 이용합니다. 즉, PreferenceFragmentCompat을 상속받은 프래그먼트로 설정 화면을 준비합니다.

 

class MySettingFragment : PreferenceFragmentCompat() {
    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
    	setPreferencesFromResource(R.xml.settings, rootKey)
    }
}

PreferenceFragmentCompat을 상속받은 프래그먼트 클래스는 onCreatePreferences() 함수를 재정의해서 작성하며, 이 함수에서 setPreferencesFromResource()를 이용해 앞에서 만든 설정 XML 파일을 전달합니다.
이제 이 프래그먼트를 액티비티에서 출력하면 되는데 이는 일반적인 액티비티에서 프래그먼트를 이용하는 방법과 차이가 없습니다.

 

<fragment xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    class="com.example.test17.MySettingFragment" />

이로써 설정 XML 파일을 참조해 설정 화면이 나오고 사용자가 설정한 내용이 자동으로 저장됩니다.


설정 화면 구성


설정 항목이 많으면 관련 있는 것끼리 묶거나 설정 화면을 여러 개로 나눌 수도 있습니다. 이때에 <PreferenceCategory>와 <Preference> 태그를 사용합니다. 먼저 <PreferenceCategory> 태그를 이용하면 한 화면에 보이는 항목끼리 구분 지어 출력할 수 있습니다.

 

<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
    <PreferenceCategory
        app:key="a_category"
        app:title="A Setting">
        <SwitchPreferenceCompat
            app:key="a1"
            app:title="A - 1 Setting" />
        <SwitchPreferenceCompat
            app:key="a2"
            app:title="A - 2 Setting" />
    </PreferenceCategory>
    <PreferenceCategory
        app:key="B_category"
        app:title="B Setting">
        <SwitchPreferenceCompat
            app:key="b1"
            app:title="B - 1 Setting" />
    </PreferenceCategory>
</PreferenceScreen>

설정 항목이 더 많을 때는 화면을 여러 개로 분리하는 방법도 있습니다. 예를 들어 A와 B라는 설정 화면으로 분리한다면 설정 XML과 프래그먼트를 2개씩 만들어야 합니다. 그리고 이 A, B 설정 화면을 포함하는 메인 설정 XML을 작성합니다. 메인 설정 XML에서 각 설정 화면은 <Preference> 태그로 지정합니다. 그러면 메인 설정 화면에서 사용자가 항목을 클릭할 때 fragment 속성에 지정한 설정 화면으로 전환합니다.

 

<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
    <Preference
        app:key="a"
        app:summary="A Setting summary"
        app:title="A Setting"
        app:fragment="com.example.test17.ASettingFragment" />
    <Preference
        app:key="b"
        app:summary="B Setting summary"
        app:title="B Setting"
        app:fragment="com.example.test17.BSettingFragment" />
</PreferenceScreen>

<Preference> 태그를 이용해 설정 화면을 분할했다면 액티비티에서 PreferenceFragmentCom pat.OnPreferenceStartFragmentCallback 인터페이스를 구현하고 onPreferenceStart Fragment() 함수를 재정의해서 작성해야 합니다. 이 함수를 정의하지 않아도 설정 화면은 분할되지만 뒤로가기 버튼을 눌렀을 때 이전 설정 화면이 나오지 않는 문제가 발생합니다.


또한 설정 화면이 바뀔 때마다 액티비티의 액션바에 출력되는 제목을 바꿀 수도 있습니다. onPreferenceStartFragment()는 설정 화면이 바뀔 때마다 호출되는 함수이므로 이 함수에 작성하면 됩니다.

 

class SettingActivity : AppCompatActivity(),
    PreferenceFragmentCompat.OnPreferenceStartFragmentCallback {
    (... 생략 ...)
    override fun onPreferenceStartFragment(caller: PreferenceFragmentCompat,
        pref: Preference): Boolean {
        // 새로운 프래그먼트 인스턴스화
        val args = pref.extras
        val fragment = supportFragmentManager.fragmentFactory.instantiate(
            classLoader,
            pref.fragment)
        fragment.arguments = args
        supportFragmentManager.beginTransaction()
            .replace(R.id.setting_content, fragment)
            .addToBackStack(null)
            .commit()
        return true
    }
}

만약 설정 화면이 복잡하다면 메인 설정 화면에서 인텐트를 이용해 하위 설정 화면을 띄우는 방법으로 구현할 수 있습니다. 이 작업은 코드에서 처리할 수 있지만 설정 XML 등록만으로도 가능합니다. 즉, 설정 화면의 항목을 사용자가 클릭했을 때 다른 액티비티를 실행하는 기능을 XML 설정만으로 구현할 수 있습니다.

 

<Preference
    app:key="activity"
    app:title="Launch activity">
    <intent
        android:targetClass="com.example.test17.SomeActivity"
        android:targetPackage="com.example.test17" />
</Preference>

<Preference> 태그 하위에 <intent> 태그로 설정 화면을 지정하면 사용자가 이 항목을 클릭했을 때 해당 설정 화면이 실행됩니다. 이때 다음처럼 엑스트라 정보를 포함할 수도 있습니다.

 

<intent
    android:targetClass="com.example.test17.SomeActivity"
    android:targetPackage="com.example.test17">
    <extra
        android:name="example_key"
        android:value="example_value" />
</intent>

<intent> 태그 하위에 <extra> 태그로 인텐트에 포함해서 전달할 엑스트라 데이터를 설정하면 됩니다. 또한 위 코드처럼 명시적 인텐트 정보뿐만 아니라 암시적 인텐트 정보도 설정할 수 있습니다.

 

<intent
    android:action="android.intent.action.VIEW"
    android:data="http://www.google.com" />