플랫폼 API에서 제공하는 클래스로, 데이터를 키-값 형태로 저장할 때 사용한다.
간단한 데이터를 저장하는데 유용하며, 내장 메모리의 앱 폴더에 XML 파일로 저장된다.
// 액티비티의 데이터 저장
val sharedPref = getPreferences(Context.MODE_PRIVATE)
// 앱 전체의 데아터 저장
val sharedPref = getSharedPreferences("my_prefs", Context.MODE_PRIVATE)
getPreferences() 함수는 자동으로 이를 호출한 액티비티이름.xml 파일이 생성되고 데이터가 저장된다.
getSharedPreferences()는 첫 번째 매개변수로 작성한 이름의 파일로 데이터가 저장된다.
// 프리퍼런스에 데이터 저장
sharedPref.edit().run {
putString("data1", "hello")
putInt("data2", 10)
commit()
}
// 데이터 가져오기
val data1 = sharedPref.getString("data1", "world")
val data2 = sharedPref.getInt("data2", 10)
위치는 data / data / 패키지명 / shared_prefs 안에 저장된다.
앱의 설정 화면은 액티빝, 사용자 이벤트 처리, 공유된 프리퍼런스 등을 이용해서 구현한다.
화면이나 설정한 데이터를 저장하는 형태는 비슷하므로, 많은 앱에서는 설정 화면을 자동으로 만들어 주는 API를 이용한다.
AndroidX의 Preference를 이용할 것을 권장하고 있다.
이를 사용하려면 빌드 그래들 파일에 아래의 라이브러리를 dependencies로 선언해야 한다.
implementation ('androidx.preference:preference:1.2.0') {
// 충돌 문제로 아래 2개의 모듈을 제외한다.
exclude group: 'androidx.lifecycle', module:'lifecycle-viewmodel'
exclude group: 'androidx.lifecycle', module:'lifecycle-viewmodel-ktx'
}
res/xml 디렉터리에 설정과 관련된 XML 파일을 만들어야 한다.
// 설정 XML 파일
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<SwitchPreferenceCompat
android:key="notifications"
android:title="Enable message notifications"/>
<Preference
android:key="feedback"
android:title="Send feedback"
android:summary="Report technical issues or suggest new features"/>
</PreferenceScreen>
설정 XML파일을 적용할 프래그먼트 클래스
class MySettingFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.settings, rootKey)
}
}
액티비티에서 프래그먼트 출력
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/frag"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.example.test17.MySettingFragment"/>
</FrameLayout>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:key="a_category"
android:title="A Setting">
<SwitchPreferenceCompat
android:key="a1"
android:title="A - 1 Setting" />
<SwitchPreferenceCompat
android:key="a2"
android:title="A - 2 Setting" />
</PreferenceCategory>
<PreferenceCategory
android:key="B_category"
android:title="B setting">
<SwitchPreferenceCompat
android:key="b1"
android:title="B - 1 Setting"/>
</PreferenceCategory>
</PreferenceScreen>
카테고리를 나누어서 항목별 출력이 가능하다.
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<Preference
android:key="a"
android:summary="A setting summary"
android:title="A Setting"
app:fragment="com.example.test17.ASettingFragment"/>
<Preference
android:key="b"
android:summary="B setting summary"
android:title="B Setting"
app:fragment="com.example.test17.BSettingFragment"/>
</PreferenceScreen>
Preference 태그를 이용해 설정 화면을 분할했다면 액티비티에서 PreferenceFragmentCompat.OnPreferenceStartFragmentCallback 인터페이스를 구현하고 onPreferenceStartFragment() 함수를 재정의 해서 작성해야 한다.
뒤로가기 버튼을 눌렀을 때 이전 설정 화면이 나오지 않는 문제가 발생하기 때문이다.
이러한 과정이 복잡하다면 메인 설정 화면에서 인텐트를 이용해 하위 설정 화면을 띄우는 방법으로 구현 가능하다.
즉, 설정 화면의 항목을 사용자가 클릭했을 때 다른 액티비티를 실행하는 기능을 XML 설정만으로 구현할 수 있다.
<Preference
app:key="activity"
app:title="Launch activity>
<intent
android:targetClass="com.example.test17.SomeActivity"
android:targetPackage="com.example.test17">
<extra
android:name="example_key"
android:value="example_value" />
</intent>
</Preference>
사용자가 설정 항목을 클릭한 순간의 이벤트를 처리하거나 설정 항목 옆에 나타나게 하는 방법
즉 코드에서 설정을 제어하는 방법이다.
글 입력받기
<EditTextPreference
android:key="id"
android:title="ID 설정"
app:isPreferenceVisible="false"/>
// 설정값을 코드에서 바꾸기
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.settings, rootKey)
val idPreference: EditTextPreference? = findPreference("id")
idPreference?.isVisible = true
idPreference?.summary = "code summary"
idPreference?.title = "code title"
}
EditTextPreference나 ListPreference는 설정값을 summary 속성에 자동으로 지정해야 할 수도 있다.
이때 SimpleSummaryProvider를 사용한다.
설정 XML예
<EditTextPreference
android:key="id"
android:title="ID 설정" />
<ListPreference
android:key="color"
android:title="색상 선택"
android:entries="@array/my_color"
android:entryValues="@array/my_color_values" />
사용자가 입력한 값이나 선택한 값을 summary에 자동으로 지정하려면 SimpleSummaryProvider를 이용한다.
val idPreference: EditTextPreference? = findPreference("id")
val colorPreference: ListPreference? = findPreference("color")
idPreference?.summaryProvider =
EditTextPreference.SimpleSummaryProvider.getInstance()
colorPreference?.summaryProvider =
ListPreference.SimpleSummaryProvider.getInstance()
사용자가 값을 설정하지 않았으면 Not set이라는 문자열이 출력된다.
값을 지정한 경우 해당 값으로 summary가 변경된다.
// 이벤트 핸들러 지정
idPreference?.setOnPreferenceClickListener { preference ->
...
true
}
프리퍼런스를 이용하면설정한 내용이 XML 파일로 저장된다.
설정값을 가져올 때는 PreferenceManager.getDefaultSharedPreferences() 함수를 이용한다.
설정 XML
<EditTextPreference
app:key="id"
app:title="ID 설정" />
사용자가 입력한 값은 id 키값으로 저장된다.
// 설정값 가져오기
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(activity)
val id = sharedPreferences.getString("id", "")
각 프리퍼런스 객체마다 이벤트 핸들러를 직접 지정하여 이벤트를 처리하는 Preference.OnPreferenceChangeListener를 이용하는 방식
// 프리퍼런스를 이용한 이벤트 처리
idPreference?.setOnPreferenceChangeListener { preference, newValue ->
Log.d(TAG, "onCreatePreferences: ")
true
}
모든 설정 객체의 변경을 하나의 이벤트 핸들러에서 처리하는 SharedPreferences.OnSharedPreferenceChangeListener를 이용하는 방식
register / unregsiter을 해야한다.
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
Log.d(TAG, "onSharedPreferenceChanged: ")
}
override fun onResume() {
super.onResume()
preferenceManager.sharedPreferences?.registerOnSharedPreferenceChangeListener(this)
}
override fun onPause() {
super.onPause()
preferenceManager.sharedPreferences?.unregisterOnSharedPreferenceChangeListener(this)
}