• スポンサードリンク

設定画面にPreferenceを継承していないViewを表示する

Android アプリ

Androidの設定画面にViewを表示する方法を調べてみた。何を当たり前な事をって言わないでよ?

設定画面の本来の目的である設定変更のためであれば、Preferenceを継承したクラスを使ってxmlを定義して読み込ませばいいんだけど、設定を変更するためじゃない目的、例えば設定値の説明用に単純にテキストを表示したいとか、タッチした時にヘルプとかライセンスを表示したいとかって出来るんじゃろか?と思って調べてみた。

元々のPreferenceクラスだと、タイトルとサマリー(+状態を表すView)だけが見えてる状態だけど、例えばListPreferenceだとかEditTextPreferenceだと設定内容を表示するためにサマリーを使うことが多いし、あまり詳しい説明は載せれないよね。
なので、Preferenceの側に別の説明用のテキストを表示したりあるいはタッチすると別画面で詳細を表示するなんて事が出来ると便利かも?って事ね。

ネットをウロウロしても見つかるのはあくまでも設定項目をどう表示するかとかテーマを適用する方法とかのカスタマイズがほとんど…設定画面なんだからあたりまえだけど(^_^;) なのでAPIレファレンスを見ながら色々試してみた。

設定画面を作る

設定画面を作る方法は幾つかありますが、現在推奨されているのはPreferenceFragmentを使う方法です。詳しくはWebで(^o^)/ あっここもWebだった(笑) でもネット上には非推奨になったPreferenceActivityを使う記事が山ほどあるので引っかからないでね(^^)

それはさておき、PreferenceFragmentを使う方法自体も更にいくつかあるんですが…今回は一番簡単なxmlで設定画面を定義する方法で紹介します。

てな感じのxmlを作ります。

ちょっと気を付けないといけないのは、layoutリソースではないってこと。
resフォルダ下のxmlフォルダ内に入れる必要があります(xmlフォルダが無ければ作ってくだされ)。例えばresフォルダ下のxmlフォルダ内にpreference.xmlと言う名前で入れると
「R.xml.preference」と言う名前でリソースIDを参照できます。

後はPreferenceActivity#onCreateで、#addPreferencesFromResourceを呼び出して設定すると出来上がり。お手軽ぅ〜(^^)

Preferenceを継承してないViewを表示する

さてさてここからが本題。設定画面定義用のxml内で使用できるのはPreferenceを継承したクラスのみです。でもPreferenceと関係ないViewを使用する方法があるのです。じゃじゃぁ〜ん。

とりあえず、簡単なテキスト表示をしてみましょう。まず表示用のViewを定義するために適当なレイアウトリソースを作っておきます。

今回は特に何のひねりもなくテキストを表示するだけなのでTextViewを使ってます。でもテキストの長さは不明で画面サイズも不明なのでとりあえずScrollView内に放り込んでおきました。ここではnotice文字列リソースを表示内容にしていますが、テキストであれば大抵は頑張って表示してくれることでしょう。ImageViewにしてdrawableリソースを割り振ればイメージ表示も出来ます。凝った書式とかにしたいのであればWebViewにhtmlを割り当ててればいいかな?
ちなみに、レイアウト内にボタンを貼り付けてクリックした時の動作を設定したりするのは別途コード側の対応が必要になりますので今回の記事からは除外です。

で、設定画面内にどっかぁ〜んと表示するのであれば、

です。あっPreferenceCategoryの代わりにCheckBoxPreferenceとかでも大丈夫でした。でもまぁ根拠なくPreferenceCategoryかPreferenceScreenが無難かなぁと思ったり。

見出しにタイトルを付けたいのであればレイアウトにidが「android:id=”@android:id/title”」のTextViewを含める必要があります。サマリーとかも一緒の感じね。

例えば先ほどのレイアウトリソースにタイトル用のTextViewを追加するとこんな感じになります。

タイトルは設定画面定義用のxml内で指定します。

もしかしたら、どっかぁ〜んと表示するのではなく、タイトルだけ表示していてタッチすると別画面で表示したいなんて要望もあるかも。そういう時はこんな風にすると出来ました。

はい、PreferenceScreenで一旦括ってあげてその中にandroid:layoutを指定したPreferenceを入れるだけです。この場合にはandroid:layoutを指定しているPreference(ここではPreferenceCategory)ではandroid:titleを指定せずに、代わりに上位のPreferenceScreen側でandroid:titleを指定します。
こうすると、最初の設定画面ではタイトルだけが表示されて、タイトルにタッチするとnotice_layout.xmlで指定したレイアウトが別画面で表示されます。しかも別画面で表示される際のウインドウタイトルに自動的にタイトルが表示されます。

簡単だったよぉ\(^o^)/

でも画面レイアウトのガイドラインからは外れてそうだし、DialogPreferenceを継承して表示しろって言われそうなので、適当に使ってね。
個人的には、あまり使わない設定とかを深い階層に入れるのはいいと思うんだけど、よく使う機能や設定まで何か操作する度にピコピコダイアログが出てくるUIって好きじゃないんだよねぇ(縦に長過ぎるのもだけど)。階層が深くなるしその度にOKボタンや「戻る」押したりの一手間が気になるんです。なので場合によってはこういう使い方もありかなぁなんて。

相変わらずマニアックなピンポイントなネタでしたね。
お疲れ様でした。

« »

  • スポンサードリンク

コメント

  • knsk より:

    まずはこんなにも丁寧に解説して頂いて本当にありがとうございます。感激です(:_;)
    ListView内にレイアウトが表示されるというのは、私もHierarchy Viewerで少し見て何となく
    見つけてはいたのですが、捕捉できない原因はそのAdapterにあったのですね。
    カスタムPreferenceも少し考えはしたのですが、ボタン一つのためにもなあ…なんて思っていました。PreferenceFragmentでonCreateViewを継承して、ButtonをAddする方法を試したりもしたのですが、位置がよろしくなかったりして…
    どうにか見た目だけでもButtonにできないかとStackOverFlow等で
    出来る限り調べてはいたのですが、そこでもやはりsakiさんが指摘されているとおり、findPreferenceで取得してリスナーを割り当てるという方法が提案されていたので、
    一番単純なその方法を使わせてもらおうと思います。
    私自身まだまだ初学者なのでとても勉強になりました。ありがとうございました!

  • knsk より:

    ずっと探していたレイアウトで本当に助かりました!
    ただ一つだけ、

    >>ちなみに、レイアウト内にボタンを貼り付けてクリックした時の動作を設定したりするのは別途コード側の対応が必要になりますので今回の記事からは除外です。

    この部分が知りたいのですが、教えていただけませんでしょうか。
    レイアウト内に貼り付けたボタンをPreferenceFragmentやActivityから取得する方法が分かりませんでした…。

    • saki より:

      こんちには。コメントありがとうございます。

      あまり参考にならないかもしれませんが、結論を先にいれば一般的な方法と言うのはなくて簡単には出来ませんキッパリ なので除外ということにしました。
      単純にタッチをした時のイベントを拾うだけなら、普通に#findPreferenceでPrefereceとして取得して#setOnPreferenceClickListenerでリスナーを割り当てればいんじゃないかと。でもレイアウト内に含めたボタン等に個別にイベントハンドラを割り振るのはかなり面倒です。

      自分が中のコードを見た限りでは、PreferenceScreen毎にListViewを生成してその1行ずつに下位のPreference(PreferenceScreenも含む)が生成したViewを表示することでプレファレンスが表示されています。なので個々のPreference項目のViewがいつ生成されていつ破棄されるかはPreferenceScreenのListAdaptor次第…つまり別な言い方をすればPreferenceFragmentやActivityからでは一意にどのViewが思っている位置のViewかは特定できません(ご存知の通りListView/ListAdaptorではViewを再利用しますので)。言うなればPreferenceクラスは必要とされた時にViewを生成初期化&値のハンドリングをするためのファクトリ&ヘルパークラスになります。

      なので、一番確実なのはカスタムViewまたはカスタムViewGroupを作ってその中で(子Viewの生成追加と)イベントハンドラの設定をしてしまうことになるのではないでしょうか。この場合もプレファレンスのキーや初期値はレイアウトのカスタムattributeとして設定できたほうが嬉しいですけどね。ただし標準ではViewGroupの子View(ListViewもViewGroupの眷属です)ではタッチイベントとか拾えません。ViewGroupo#setDescendantFocusabilityあたりをごにょごにょしてってことになります–;しかも階層が深くなると目的のViewを含むListViewを探し当てるだけでも大変(; 😉

      なので複雑なことをするのであれば素直にカスタムPreferenceを作る方が簡単、というか設定画面であまり複雑なことをすんじゃねぇっていうぐるぐる先生の思し召しなのかと。

      そもそも自分がこういうことをしてみよう/調べてみようと思ったのは、例えばライセンス表示や簡単な説明文を設定画面に入れたいだけなのにカスタムPreference作るにゃんてアホじゃね?と思って思ったからなので、調べてみるとタッチした時にFragmentやActivityへ遷移させることはできるらしい。でもViewは入れれへんの?xmlで作ったレイアウトを押し込めないの?ってところで調べたものの、ネット上には情報が無かったからなのです。

      よろしくお願いします。
      saki