LoginSignup
10
11

More than 5 years have passed since last update.

Preferenceのsummary下に画像イメージを表示する

Posted at

Androidアプリで設定系画面を作る場合、PreferenceActivityやPreferenceFragmentを使うと、見た目が統一された画面を簡単に実装できる。

この場合、TitleSummary でそのPreferenceの説明を記載できるが、いずれも文字列情報であるため、画像データなんかを使って、もっと分かりやすく補足したいことがある。

pic1.png

以下、2つの方法を試してみた。

なお、Preference右側にあたるチェックボックスなどが表示される領域については、android:widgetLayoutを指定することでカスタマイズ可能。こちらは秀逸なHowTo記事がいくつも投稿されているので、そちらを参考に。

(アプローチ1) Customレイアウトを使う

android:widgetLayoutと似たようなやり方で、Preference全体についてはandroid:layout を指定することでカスタマイズができる。

まず、Preference全体のレイアウトファイルを用意する。
一から作ると大変なので、Androidのソースコードからpreferenece.xmlを見つけてきて引用。

res/layout/custom_preference.xml
<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:paddingEnd="?android:attr/scrollbarSize"
    android:paddingRight="?android:attr/scrollbarSize"
    android:background="?android:attr/selectableItemBackground" >

    <!-- Move to below.
    <ImageView
        android:id="@+android:id/icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        />
    -->

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="15dip"
        android:layout_marginLeft="15dip"
        android:layout_marginEnd="6dip"
        android:layout_marginRight="6dip"
        android:layout_marginTop="6dip"
        android:layout_marginBottom="6dip"
        android:layout_weight="1">

        <TextView android:id="@+android:id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:singleLine="true"
            android:textAppearance="?android:attr/textAppearanceLarge"
            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="4" />

        <!-- Insert here -->
        <ImageView
            android:id="@+android:id/icon"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@android:id/summary"
            android:layout_alignStart="@android:id/summary"
            android:layout_alignLeft="@android:id/summary" />

    </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:gravity="center_vertical"
        android:orientation="vertical" />

</LinearLayout>

標準Preferenceでは、左側にアイコン画像の領域がある。今回は簡単のため、このImageViewをSummary領域の下に移動させてみる。もちろん、icon領域を残しつつ、新たにImageViewを追加することもできる。

Preference XMLは以下のような感じ。
android:layoutに先のレイアウトファイルを指定する。

res/xml/pref_example.xml
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >

    <ListPreference
        android:defaultValue="180"
        android:entries="@array/pref_sync_frequency_titles"
        android:entryValues="@array/pref_sync_frequency_values"
        android:key="sync_frequency"
        android:negativeButtonText="@null"
        android:positiveButtonText="@null"
        android:layout="@layout/custom_preference"
        android:icon="@android:drawable/star_on"
        android:title="@string/pref_title_sync_frequency" />

          : (略)

</PreferenceScreen>

なお、icon領域を移動しただけなので、表示したい画像リソースは android:icon で指定できるが、ImageViewを別で新規に追加する場合、attrやらstyleableで独自プロパティを定義することで、XML上から表示画像の指定もできる。

出来栄えはこんな感じ。

pic2.png

画像はうまく表示できたが、なにやら周りとフォントの大きさや表示位置が揃っていない。

今回、PreferenceのレイアウトファイルをAndroidのソースコードから引用したのだが、こうしたレイアウトファイルは機種ごとにカスタマイズされることがあるようで、この手法だと全機種で見た目を統一するのは難しそう。(やるなら、アプリ内のすべてのPreferenceのandroid:layoutを同じものに指定しなおす、など)

(アプローチ2) 独自Preferenceクラスを定義する

一旦、仕切り直し。

他のPreferenceとの見た目の調和を崩さないため、独自Preferenceクラスを定義し、ロードされたPreferenceのLayoutにImageViewを後から差し込んでみる。

以下のような独自Preferenceクラスを定義する。

package com.example.custompreferencesample;

import android.content.Context;
import android.preference.Preference;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;

public class PreferenceEx extends Preference {

    public PreferenceEx(Context context) {
        super(context);
    }

    public PreferenceEx(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public PreferenceEx(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onBindView(View view) {
        super.onBindView(view);

        ViewGroup viewGroup = (ViewGroup) view;

        // Workaround: hide icon image
        ImageView iconView = (ImageView) viewGroup
                .findViewById(android.R.id.icon);
        iconView.setVisibility(View.GONE);

        for (int i = 0; i < viewGroup.getChildCount(); i++) {
            View child = viewGroup.getChildAt(i);
            if (child instanceof RelativeLayout) {
                RelativeLayout relativeLayout = (RelativeLayout) child;

                // Create a new image view.
                ImageView image = new ImageView(getContext());
                image.setImageDrawable(getIcon());
                image.setPadding(20, 20, 0, 0); /* Padding */

                // Insert it into the "RelativeLayout".
                RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
                        ViewGroup.LayoutParams.WRAP_CONTENT,
                        ViewGroup.LayoutParams.WRAP_CONTENT);
                params.addRule(RelativeLayout.BELOW, android.R.id.summary);
                relativeLayout.addView(image, params);
                break;
            }
        }
    }
}

void onBindView(View view)はPreferenceのレイアウトがロードされたときに呼ばれるので、ここでロードされたレイアウトにImageViewを挟み込んでみる。

今回、Summary領域の下に画像を差し込むように、その親であるRelativeLayoutをfor文で探してきて挿入している。(レイアウト構成はpreference.xmlを参考に)

なお、先と同じく、簡単のためandroid:iconで画像を指定できるようにしたいので、Preference左側に表示されるIcon領域はWorkaroundで非表示にしている。

PreferenceのXML側はこんな感じ。

res/xml/pref_xxx.xml
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >

    <com.example.custompreferencesample.PreferenceEx
        android:title="Color setting"
        android:summary="Set a color of right top corner like below figure."
        android:icon="@drawable/setting_right_top"
        android:key="color_right_top" />

          : (略)

</PreferenceScreen>

android:icon="@drawable/setting_right_top"で用意した画像リソースを指定している。

見た目はこんな感じ。

pic3.png

ちょっとわかりにくいが、フォントサイズなどが周りと揃っている。
(上図の"Sync frequency"はアプローチ1の方法で表示したもの)

今回は基本クラスであるPreferenceクラスを継承した独自クラスを定義したが、ListPreferenceなども同じように拡張できる。

extends以外はほとんど同じだがコードは以下。


package com.example.custompreferencesample;

import android.content.Context;
import android.preference.ListPreference;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;

public class ListPreferenceEx extends ListPreference {

    public ListPreferenceEx(Context context) {
        super(context);
    }

    public ListPreferenceEx(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onBindView(View view) {
        super.onBindView(view);

        ViewGroup viewGroup = (ViewGroup) view;

        // Workaround: hide icon image
        ImageView iconView = (ImageView) viewGroup
                .findViewById(android.R.id.icon);
        iconView.setVisibility(View.GONE);

        for (int i = 0; i < viewGroup.getChildCount(); i++) {
            View child = viewGroup.getChildAt(i);
            if (child instanceof RelativeLayout) {
                RelativeLayout relativeLayout = (RelativeLayout) child;

                // Create a new image view.
                ImageView image = new ImageView(getContext());
                image.setImageDrawable(getIcon());
                image.setPadding(20, 20, 0, 0); /* Padding */

                // Insert it into the "RelativeLayout".
                RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
                        ViewGroup.LayoutParams.WRAP_CONTENT,
                        ViewGroup.LayoutParams.WRAP_CONTENT);
                params.addRule(RelativeLayout.BELOW, android.R.id.summary);
                relativeLayout.addView(image, params);
                break;
            }
        }
    }
}

XXXPreferenceクラスごとにいちいちクラス拡張が必要なのがネック。

10
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
11