[Android] オリジナルのPreferenceを作ってみる
どうも、hide92795です
Androidの設定画面はPreferenceを使えば簡単にできるのですが、
・現在の設定値がわかりづらい
・単位をつけて表示させたい(EditTextPreference)
というのが気にくわないのですww
(まぁ、はじめのはサマリーや別のPreferenceに表示すればいいだけですが、説明文が表示できなくなったり縦に伸びるのは自分的に×なんですよねw)
というわけで、オリジナルのPreferenceを作ってしまおうじゃないかというわけなのです(`・ω・´)
参考サイト:
EditTextPreferenceの拡張2 – HaseLab
http://www.haselab.com/androidpj/?p=370
XML でカスタムView – Android 奔走記
http://weide-dev.blogspot.com/2010/04/xml-customview.html
こんなかんじ
これを改造するのは、これのレイアウトxmlを新しく作って、それを使うように新しく独自のクラスを作成すればおkです
デフォルトのレイアウトxmlは、androidSDKのフォルダの
「\platforms\android-*\data\res\layout\preference_child.xml」
です。
改造するときは、すでにあるViewのIdをいじらないようにしてください
自分が改造したxmlは、追記のほうに書いておきますw(結構長いのでw)
②DialogPreferenceを継承したオリジナルのダイアログを表示するレイアウト&クラスを作成する
・EditTextPreferenceの場合
この場合だと、入力するEditTextの横に単位をつける必要があるので、ダイアログ用のレイアウトxmlを作成します。
(とはいっても、EditTextとTextViewを横に並べるだけなんですけどねw)
custom_edittext_preference.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/layout_root" android:layout_width="fill_parent" android:layout_height="fill_parent" android:baselineAligned="true" android:orientation="horizontal" > <EditText android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_margin="5dip" android:layout_toLeftOf="@+id/unit" android:inputType="number" android:singleLine="true" > <requestFocus /> </EditText> <TextView android:id="@+id/unit" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignBaseline="@+id/text" android:layout_alignBottom="@+id/text" android:layout_alignParentRight="true" android:layout_marginRight="10dip" /> </RelativeLayout> |
EditTextのIDは「text」、TextViewのIDは「unit」になっています
次は、↑のxmlを読み込むようにし、さらに色々と機能を追加させたオリジナルのDialogPreferenceを作ります
CustomEditTextPreference
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
package hide92795.Twit_Time_Android_Pro.View; import hide92795.Twit_Time_Android_Pro.R; import android.content.Context; import android.content.SharedPreferences; import android.content.res.TypedArray; import android.preference.DialogPreference; import android.text.InputFilter; import android.text.InputType; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; import android.widget.EditText; import android.widget.TextView; public class CustomEditTextPreference extends DialogPreference { private static final int DEFAULT_MAX_LENGTH = 5; private String unit = ""; private TextView inputValueTextView; private EditText editText; private int inputType; private int maxLength; private String defaultVal; private String saveVal; private int unitPosition; public CustomEditTextPreference(Context context, AttributeSet attrs) { super(context, attrs); TypedArray tArray = context.obtainStyledAttributes(attrs, R.styleable.CustomPreference); unit = tArray.getString(R.styleable.CustomPreference_unit); defaultVal = tArray.getString(R.styleable.CustomPreference_defaultValue); String unitPosition_s = tArray.getString(R.styleable.CustomPreference_unitPosition); if(unitPosition_s == null){ unitPosition = R.layout.custom_preference_right; }else if(unitPosition_s.equals("right")){ unitPosition = R.layout.custom_preference_right; }else if(unitPosition_s.equals("bottom")){ unitPosition = R.layout.custom_preference_bottom; }else{ unitPosition = R.layout.custom_preference_right; } String argInpType = tArray.getString(R.styleable.CustomPreference_inputType); if (argInpType == null || argInpType.equals("number")) { inputType = EditorInfo.TYPE_CLASS_NUMBER; } else { inputType = InputType.TYPE_NULL; } try { maxLength = Integer.parseInt(tArray.getString(R.styleable.CustomPreference_unit)); } catch (Exception e) { maxLength = DEFAULT_MAX_LENGTH; } } @Override protected View onCreateView(ViewGroup parent) { Context context = super.getContext(); LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); View orgLayout = layoutInflater.inflate(unitPosition, null); inputValueTextView = (TextView) orgLayout.findViewById(R.id.inputValue); setLayoutResource(android.R.id.widget_frame); return orgLayout; } @Override protected void onBindView(View view) { SharedPreferences pref = getSharedPreferences(); if (pref == null) { saveVal = defaultVal; } else { saveVal = pref.getString(getKey(), defaultVal); } updateInputValue(); super.onBindView(view); } @Override protected View onCreateDialogView() { LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); View view = inflater.inflate(R.layout.custom_edittext_preference, null); editText = (EditText) view.findViewById(R.id.text); TextView unitView = (TextView) view.findViewById(R.id.unit); unitView.setText(unit); editText.setInputType(inputType); editText.setFilters(new InputFilter[] { new InputFilter.LengthFilter(maxLength) }); editText.setText(saveVal); return view; } @Override protected void onDialogClosed(boolean positiveResult) { super.onDialogClosed(positiveResult); if (!positiveResult) { } else { saveVal = editText.getText().toString(); if (saveVal == null || saveVal.length() == 0) saveVal = defaultVal; SharedPreferences.Editor editor = getEditor(); editor.putString(getKey(), saveVal); editor.commit(); updateInputValue(); } } private void updateInputValue() { if (saveVal == null || saveVal.length() == 0) saveVal = defaultVal; StringBuilder sb = new StringBuilder(); sb.append(saveVal); sb.append(" "); sb.append(unit); inputValueTextView.setText(sb.toString().trim()); } } |
メソッドの説明
・コンストラクター
xmlで指定された値を各変数に格納します
・onCreateView(ViewGroup parent)
①が生成されるときに呼ばれます
これをオーバーライドして、①で作ったxmlが呼ばれるようにし、設定値を表示させるTextViewを変数に格納します。
layoutInflater.inflate(unitPosition, null)のunitPositionは、コンストラクターで設定されたxmlのリソースIDです。
・onBindView(View view)
①が表示される直前に呼ばれる?
保存されている値を取得し、変数に格納します。
・onCreateDialogView()
ダイアログが生成されるときに呼び出されます。
EditTextは、値を保存する際に使用するので、変数に格納します。
この時に、onBindViewで読み込んだ値を設定します。
他にも、コンストラクターで読み込んだ設定を各部品に割り当てていきます。
・onDialogClosed(boolean positiveResult)
ダイアログが閉じられる時に呼び出されます。
positiveResult = true なら、値を保存します。
・updateInputValue()
①の現在の設定値に表示させる値を更新します。
コンストラクターでxmlで指定された値を~と書きましたが、この部分では
1 2 3 4 5 6 7 8 9 |
<hide92795.Twit_Time_Android_Pro.View.CustomEditTextPreference hide92795:defaultValue="60" hide92795:inputType="number" android:key="key_pre_TweetSpeed_User-defined" hide92795:maxLength="3" android:summary="@string/str_TweetSpeed_UserDefined_sum" android:title="@string/str_TweetSpeed_UserDefined" hide92795:unit="@string/str_SecPerTweet" hide92795:unitPosition="right" /> |
のように書いています。ここで値を指定しています。
見た方はわかると思いますが、普通なら「android:****=”….”」みたいなのだけのはずが、「hide92795:****=”….”」というのが追加されています。
次は、これのやり方です
③xmlに独自の属性を追加する
まず、「\res\values\」フォルダ内に「attrs.xml」を作成します。
attrs.xml
1 2 3 4 5 6 7 8 9 10 11 |
<?xml version="1.0" encoding="utf-8"?> <resources> <!-- name : custom view class name --> <declare-styleable name="CustomPreference"> <attr format="string" name="inputType" /> <attr format="string" name="maxLength" /> <attr format="string|reference" name="unit" /> <attr format="string" name="defaultValue" /> <attr format="string" name="unitPosition" /> </declare-styleable> </resources> |
書式としては、declare-styleable name=”名前”で名前に好きな文字を(別のサイトでは、クラス名にとありましたが、クラス名じゃなくても正常に動作しました(;^ω^))
attr format=”種類” name=”名前”で種類に使用できるリソースの種類、名前にxmlで使用する属性の名前を設定します。
この種類のところには、文字列→string 色→color リソース(xmlで指定するやつ)→reference みたいなのを入力します。複数個ある場合は、「|」で区切ってください。
これを作ったあとに、Preferenceのレイアウトxmlのはじめの部分を
1 2 |
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" xmlns:hide92795="http://schemas.android.com/apk/res/hide92795.Twit_Time_Android_Pro" > |
のように
xmlns:(:の前につくやつ)=”http://schemas.android.com/apk/res/(パッケージ名)”
を追加します。
これによって、「hide92795:inputType=”number”」みたいなのを追加してもエラーが起こらないようになります。
ここで設定した値を取得するには、さっきの②のCustomEditTextPreference.javaのコンストラクターであった
1 2 |
TypedArray tArray = context.obtainStyledAttributes(attrs, R.styleable.CustomPreference); unit = tArray.getString(R.styleable.CustomPreference_unit); |
を使用します。
context.obtainStyledAttributes() の第2引数は、R.styleable.(attrs.xmlのはじめの部分で指定した名前)となります。
R.~なので、クラスが自動生成されるので、eclipseを使っていれば補完機能でわかると思いますww
取得は、TypedArrayクラスにあるget****()メソッドを使用します。
http://developer.android.com/reference/android/content/res/TypedArray.html
引数のint型は、R.styleable.(attrs.xmlのはじめの部分で指定した名前)_(name = “…”で入力した名前) となっています
これもeclipseの補完機能でどれがどれかわかると思いますww
だいぶ長くなりましたねww
久々と言うかここまで長くなったのは初めてかな?w
実は、ListPreferenceも設定値を表示できるように改造したクラスを作ってありますw
ここに書いたものを少しいじればできるようになるので、ここでは省略させて頂きます
コツと言うかヒントと言うか答えと言うか、独自のListPreferenceを作るときに注意する点は
・ListPreferenceを継承させる(単位を追加することはまずないと思うので、ダイアログの部分はそのまま使います)
・↑のおかげで、ダイアログの生成をオーバーライドしなくて済み、自分で値を保存・読み出ししなくておk
・リストで選択されている文字を取得するには、getEntry()
これらをちゃんとソースに反映させれば現在の選択している値がちゃんと表示されるようになるはずです。
一応、これで動いてはいますが、間違っている部分を見つけたら教えて下さいww
ではノシ
以下、①の部分で省略したxmlです
①PreferenceScreenに表示される方を改造する
設定値を下に表示させるxmlレイアウト
custom_preference_bottom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:minHeight="?android:attr/listPreferredItemHeight" android:gravity="center_vertical" android:paddingRight="?android:attr/scrollbarSize"> <RelativeLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="6dip" android:layout_marginLeft="15dip" android:layout_marginRight="6dip" android:layout_marginTop="6dip" android:layout_weight="1" > <TextView android:id="@+android:id/title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:ellipsize="marquee" android:fadingEdge="horizontal" android:singleLine="true" android:textAppearance="?android:attr/textAppearanceLarge" /> <TextView android:id="@+android:id/summary" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@+id/inputValue" android:layout_below="@+id/inputValue" android:maxLines="2" android:textAppearance="?android:attr/textAppearanceSmall" /> <TextView android:id="@+id/inputValue" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@android:id/title" android:layout_below="@android:id/title" android:textAppearance="?android:attr/textAppearanceSmall" android:textColor="@color/AQUA_BLUE" /> </RelativeLayout> <!-- Preference should place its actual preference widget here. --> <LinearLayout android:id="@+android:id/widget_frame" android:layout_width="wrap_content" android:layout_height="fill_parent" android:gravity="center_vertical" android:orientation="vertical" /> </LinearLayout> |
設定値を下に表示させるxmlレイアウト
custom_preference_right.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:minHeight="?android:attr/listPreferredItemHeight" android:gravity="center_vertical" android:paddingRight="?android:attr/scrollbarSize"> <RelativeLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="15dip" 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:ellipsize="marquee" android:fadingEdge="horizontal" android:singleLine="true" android:textAppearance="?android:attr/textAppearanceLarge" /> <TextView android:id="@+android:id/summary" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@android:id/title" android:layout_below="@android:id/title" android:maxLines="2" android:textAppearance="?android:attr/textAppearanceSmall" /> <TextView android:id="@+id/inputValue" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignBottom="@+android:id/title" android:layout_alignParentRight="true" android:textAppearance="?android:attr/textAppearanceSmall" android:textColor="@color/AQUA_BLUE" /> </RelativeLayout> <!-- Preference should place its actual preference widget here. --> <LinearLayout android:id="@+android:id/widget_frame" android:layout_width="wrap_content" android:layout_height="fill_parent" android:gravity="center_vertical" android:orientation="vertical" /> </LinearLayout> |
設定値を表示させるTextViewのIDは「inputValue」、色はxmlで定義済みのAQUA_BLUE(#00BFFF)になっています
コメントはまだありません。