標準の設定画面をカスタマイズしたい その1

標準の設定画面では、標準のテーマ（`Holo`とか）に沿った見た目になります。ある程度凝ったアプリを作っていると、設定画面だけが妙に浮いた感じに見えてしまって、統一感が失われてしまいます。
ここでは、設定画面の各項目のレイアウトに焦点を当てて、カスタマイズの方法を見ていきます。

## Summary

1. `Preference`の各要素（`PreferenceCategory`とか`PreferenceScreen`とか`CheckBoxPreference`など）は、カスタムのレイアウトを差し込むためのアトリビュートがある
2. 自分で定義したレイアウトの XML が差し込めるので、カスタム View を作ればより凝った画面を作れる

## How to customize

### レイアウトの準備

まずはカスタマイズしたいレイアウトを用意します。

ベースとなるレイアウトは[AOSP](https://github.com/android/platform_frameworks_base/blob/master/core/res/res/layout/preference_holo.xml)にあります。

気をつける点は、設定画面で表示する項目に使う`id`は`android.R`のものを使うようにすることです。

```xml:row_custom_preference.xml

<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2011 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->

<!-- Layout for a Preference in a PreferenceActivity. The
     Preference is able to place a specific widget for its particular
     type in the "widget_frame" layout. -->
<!-- このレイアウトが、一行分のレイアウトになります。Android Lint がいくつかの警告を出すので対応しておきます -->
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:minHeight="?android:attr/listPreferredItemHeight"
    android:gravity="center_vertical"
    android:background="@drawable/my_background"
    android:paddingStart="@dimen/preference_item_padding_side"
    android:paddingLeft="@dimen/preference_item_padding_side"
    android:paddingEnd="?android:attr/scrollbarSize"
    android:paddingRight="?android:attr/scrollbarSize">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:gravity="center"
        android:minWidth="@dimen/preference_icon_minWidth"
        android:orientation="horizontal">
        <!-- アイコン、タイトル、説明、widget の id は android:id のものを使ってしまうのが楽です。 -->
        <ImageView
            android:id="@android:id/icon"
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:layout_gravity="center"
            android:minWidth="48dp"
            android:scaleType="centerInside"
            android:layout_marginEnd="@dimen/preference_item_padding_inner"
            android:layout_marginRight="@dimen/preference_item_padding_inner"/>
    </LinearLayout>

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:paddingEnd="@dimen/preference_item_padding_inner"
        android:paddingRight="@dimen/preference_item_padding_inner"
        android:paddingTop="6dip"
        android:paddingBottom="6dip">

        <TextView
            android:id="@android:id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:singleLine="true"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:ellipsize="marquee"
            android:fadingEdge="horizontal" />

        <TextView
            android:id="@android:id/summary"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@android:id/title"
            android:layout_alignStart="@android:id/title"
            android:layout_alignLeft="@android:id/title"
            android:textAppearance="?android:attr/textAppearanceSmall"
            android:textColor="?android:attr/textColorSecondary"
            android:maxLines="10"
            android:drawableLeft="@drawable/my_drawable"/>

    </RelativeLayout>

    <!-- Preference should place its actual preference widget here. -->
    <LinearLayout
        android:id="@android:id/widget_frame"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:minWidth="@dimen/preference_widget_width"
        android:gravity="center"
        android:orientation="vertical" />

</LinearLayout>
```

`PreferenceCategory`のレイアウトをカスタムする場合は以下の XML にします。元のファイルは[こちら](https://github.com/android/platform_frameworks_base/blob/master/core/res/res/layout/preference_category_holo.xml)。

一部 public ではない dimen の値（コンパイルタイムでエラーになります）がありますが、エディタ上では解決できているので、定義に飛んで値をコピペしています。

```xml:row_preference_category.xml

<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2011 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
  
          http://www.apache.org/licenses/LICENSE-2.0
  
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->

<!-- Layout used for PreferenceCategory in a PreferenceActivity. -->
<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    style="?android:attr/listSeparatorTextViewStyle"
    android:id="@android:id/title"
    android:paddingStart="8dp"
    android:paddingLeft="8dp"
    android:paddingEnd="8dp"
    android:paddingRight="8dp"
    android:drawableLeft="@drawable/my_drawable"/>
```

### レイアウトを差し込む

あとは、好きなところにレイアウトを差し込むだけです。属性は、`android:layout`です。

```xml:pref.xml
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <PreferenceCategory
        android:title="About"
        android:layout="@layout/row_preference_category.xml">
        <Preference
            android:title="Version"
            android:layout="@layout/row_custom_preference"/>
        <CheckBoxPreference
            android:title="Check it out!"
            android:summaryOn="On"
            android:summaryOff="Off"
            android:layout="@layout/row_custom_preference"/>
    </PreferenceCategory>
</PreferenceScreen>
```