#android.preference ライブラリのサポートが終了
Android は Preference ライブラリ(フレームワーク)を使用することで設定画面を簡単に実装することができていた。
公式サイトのAndroid 10 動作変更点の項目に「android.preference ライブラリのサポート終了」の記載がある。
公式サイト:Android10 動作の変更点: すべてのアプリ
この為、今後の為に既存で Preference ライブラリ(フレームワーク)を使用していた場合は対応した方が良いと考えられる。
#API も deprecated(非推奨)に
サポート終了に伴い公式サイトの PreferenceActivity 等でも deprecated(非推奨)の記載がヘッターに出てきた。
公式サイト:PreferenceActivity
公式サイト:PreferenceCategory
#これまでの Preference ライブラリ(フレームワーク)を使用した実装
PreferenceActivity と PreferenceFragment を組み合わせて作成しているのが一般的。
targetSDK は 29 とする
import android.R
import android.os.Bundle
import android.preference.PreferenceActivity
class MyPreferenceActivity : PreferenceActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
fragmentManager.beginTransaction()
.replace(R.id.content, MyPreferenceFragment()).commit()
}
}
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import android.os.Bundle
import android.preference.PreferenceFragment
class MyPreferenceFragment : PreferenceFragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
addPreferencesFromResource(R.xml.preferences)
}
override fun onResume() {
super.onResume()
preferenceScreen.sharedPreferences.registerOnSharedPreferenceChangeListener(listener)
}
override fun onPause() {
super.onPause()
preferenceScreen.sharedPreferences
.unregisterOnSharedPreferenceChangeListener(listener)
}
private val listener =
OnSharedPreferenceChangeListener { sharedPreferences, key ->
if (key == "KEY") {
// setSummary等行う
}
}
companion object {
private const val PREF_KEY = "KEY"
}
}
読み込むxmlファイルは以下とする
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<!-- シークバーPreference -->
<SeekBarPreference
android:id="@+id/seekbar"
android:key="seek_bar"
android:summary=""
android:title="SeekBarテキスト" />
<!-- チェックボックスPreference -->
<CheckBoxPreference
android:defaultValue="@string/default_text"
android:key="check_box"
android:summary="@string/default_text"
android:title="チェックボックステキスト" />
<!-- テキスト入力を行うPreference -->
<EditTextPreference
android:defaultValue="@string/default_text"
android:key="text"
android:summary="@string/default_text"
android:title="表示するテキスト" />
<!-- リストから選択を行うPreference -->
<ListPreference
android:defaultValue="white"
android:key="screen_color"
android:title="画面の色" />
<!-- スイッチでON/OFF切り替えを行うPreference -->
<SwitchPreference
android:defaultValue="false"
android:key="enable_other_settings"
android:title="その他の設定を有効にする" />
<!-- 複数のPreferenceをカテゴライズしたグループ -->
<PreferenceCategory
android:enabled="false"
android:key="other_settings"
android:title="その他の設定">
<!-- 複数選択可能なListPreference -->
<MultiSelectListPreference
android:key="display_images"
android:title="画像を表示する" />
</PreferenceCategory>
</PreferenceScreen>
マニフェストファイルとテーマ設定は以下とした
<activity
android:name=".MyPreferenceActivity"
android:theme="@style/MyPreferenceTheme" />
<resources>
<style name="MyPreferenceTheme" parent="android:Theme.Holo.Light">
<item name="android:textColor">@color/colorPrimary</item>
</style>
</resources>
#androidx.preference への対応方法
Jetpack の androidx.preference に置き換えることで対応できる。
公式サイト:androidx.preference
gradle に以下を記載する。
dependencies {
// 最新のバージョンを使ってください
// java
implementation 'androidx.preference:preference:1.1.1'
// Kotlin
implementation 'androidx.preference:preference-ktx:1.1.1'
}
公式サイトを参考に Activity と Fragment を修正する。
公式サイト:JetPack Settings
import android.R
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
// androidx.appcompat に変更
class MyPreferenceActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// fragmentManager → supportFragmentManager に変更
supportFragmentManager.beginTransaction()
.replace(R.id.content, MyPreferenceFragment()).commit()
}
}
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import android.os.Bundle
import androidx.preference.PreferenceFragmentCompat
// android.preference → androidx.preference に変更
class MyPreferenceFragment : PreferenceFragmentCompat() {
// interface が変わる
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.preferences)
}
override fun onResume() {
super.onResume()
preferenceScreen.sharedPreferences.registerOnSharedPreferenceChangeListener(listener)
}
override fun onPause() {
super.onPause()
preferenceScreen.sharedPreferences
.unregisterOnSharedPreferenceChangeListener(listener)
}
private val listener =
OnSharedPreferenceChangeListener { sharedPreferences, key ->
if (key == "KEY") {
// setSummary等行う
}
}
companion object {
private const val PREF_KEY = "KEY"
}
}
この実装を実行すると以下の Exception でクラッシュする。
java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.
スタイルのテーマ設定を AppCompat に変更する必要がある。
<!-- ThemeをAppCompatに変更 -->
<resources>
<style name="MyPreferenceTheme" parent="Theme.AppCompat.Light">
<item name="android:textColor">@color/colorPrimary</item>
</style>
</resources>
文字が小さくなって、テキストの左にマージンが出現したが Jetpack には対応できた。
対応前後の表示の差異はマテリアルデザインの影響かと思われる。
左のマージンはアイコンを表示する部分で、アイコンの表示が必要なくてもデフォルトで表示されるようになる為、そのアイコン表示エリアが表示されていると考えられる。
このアイコンエリアは iconSpaceReserved の設定値を false にすることで無くすことができる。
さらに preferences.xml の PreferenceScreen も実は deprecated(非推奨)なので修正する必要がある。android: の定義を app: に変えないと上手く動いてくれないのでこちらも変更する必要がある。
<?xml version="1.0" encoding="utf-8"?>
<!-- androidx.preference に変更 -->
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<!-- シークバーPreference -->
<SeekBarPreference
android:id="@+id/seekbar"
app:iconSpaceReserved="false"
app:key="seek_bar"
app:summary=""
app:title="SeekBarテキスト" />
<!-- チェックボックスPreference -->
<CheckBoxPreference
app:defaultValue="@string/default_text"
app:iconSpaceReserved="false"
app:key="check_box"
app:summary="@string/default_text"
app:title="チェックボックステキスト" />
<!-- テキスト入力を行うPreference -->
<EditTextPreference
app:defaultValue="@string/default_text"
app:iconSpaceReserved="false"
app:key="text"
app:summary="@string/default_text"
app:title="表示するテキスト" />
<!-- リストから選択を行うPreference -->
<ListPreference
app:defaultValue="white"
app:iconSpaceReserved="false"
app:key="screen_color"
app:title="画面の色" />
<!-- スイッチでON/OFF切り替えを行うPreference -->
<SwitchPreference
app:defaultValue="false"
app:iconSpaceReserved="false"
app:key="enable_other_settings"
app:title="その他の設定を有効にする" />
<!-- 複数のPreferenceをカテゴライズしたグループ -->
<PreferenceCategory
app:enabled="false"
app:iconSpaceReserved="false"
app:key="other_settings"
app:title="その他の設定">
<!-- 複数選択可能なListPreference -->
<MultiSelectListPreference
app:iconSpaceReserved="false"
app:key="display_images"
app:title="画像を表示する" />
</PreferenceCategory>
</androidx.preference.PreferenceScreen>
文字サイズとデザインの変更はマテリアルデザインの影響なのでこのままで良いと思う。
参考サイト:Android マテリアルデザイン
ちなみにアイコンも表示可能
<resources>
<style name="MyPreferenceTheme" parent="Theme.AppCompat.Light">
<!-- ベースとなるテキストカラー -->
<item name="android:textColor">@color/colorPrimary</item>
<!-- ナビゲーションのアイコン ※別途リソースサイズ調整必要 -->
<item name="navigationIcon">@mipmap/ic_launcher_round</item>
</style>
</resources>
#カスタマイズ実装例
ここからさらに画面をカスタマイズすることも可能
<style name="MyPreferenceTheme" parent="Theme.AppCompat.Light">
<!-- ベースとなるテキストカラー -->
<item name="android:textColor">@color/colorPrimary</item>
<!-- ベースとなる背景色 -->
<item name="android:windowBackground">#ffdd01</item>
<!-- 各設定の背景色 -->
<item name="android:selectableItemBackground">@drawable/background_red</item>
<!-- ナビケーションバックグラウンドカラー -->
<item name="colorPrimary">#ef8201</item>
<!-- アクションバーバックグラウンドカラー -->
<item name="android:actionBarItemBackground">@drawable/background_red</item>
<!-- ナビゲーションのアイコン ※別途リソースサイズ調整必要 -->
<item name="navigationIcon">@mipmap/ic_launcher_round</item>
<!-- チェックボックス未チェック時の色 -->
<item name="colorControlNormal">#b52872</item>
<!-- チェックボックスチェック時の色 -->
<item name="colorSwitchThumbNormal">#444444</item>
<!-- リスト背景色 -->
<item name="colorBackgroundFloating">#ffffff</item>
<!-- リストダイアログの文字色 -->
<item name="textColorAlertDialogListItem">#121212</item>
<!-- ダイアログテーマ -->
<item name="alertDialogTheme">@style/AlertDialogTheme</item>
<!-- リップル効果色 -->
<item name="colorControlHighlight">@color/colorPrimary</item>
<item name="colorControlActivated">@color/colorPrimary</item>
<!-- popup menuの(チェックボックスを指定してない場合はチェックボックスが含まれる)色 -->
<item name="colorAccent">#000000</item>
<!-- Title -->
<item name="android:textColorPrimary">#940017</item>
<!-- Summary -->
<item name="android:textColorSecondary">#940017</item>
<!-- アプリ外の最上部の色 -->
<item name="colorPrimaryDark">#059b6d</item>
</style>
<style name="AlertDialogTheme" parent="ThemeOverlay.AppCompat.Dialog.Alert">
<!-- ダイアログ選択肢の文字色 -->
<item name="android:textColor">#555555</item>
<!-- ダイアログ選択肢の文字サイズ -->
<item name="android:textSize">15sp</item>
<!-- ダイアログタイトルのテーマ -->
<item name="android:windowTitleStyle">@style/AlertDialogTitleTheme</item>
</style>
<style name="AlertDialogTitleTheme" parent="RtlOverlay.DialogWindowTitle.AppCompat">
<!-- ダイアログタイトルの文字色 -->
<item name="android:textColor">#876543</item>
<!-- ダイアログタイトルの文字サイズ -->
<item name="android:textSize">20sp</item>
</style>
<?xml version="1.0" encoding="utf-8"?>
<!-- 赤っぽい色を指定 -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#cb270d" />
</shape>
#最後に
今回のサポート終了は、おそらくダークテーマの対応できるようにする意図ではなかろうかと思っている。
Android 11 のリリースが近い。アプリを最新の状態にアップデートしておきたい。