-
Notifications
You must be signed in to change notification settings - Fork 354
3.03. 続・アプリのレイアウト作成
GitHub Pagesへ移行しましたmixi-inc.github.ioへお願いします。
wikiの方はしばらくしたら消していきます
この章では、レイアウトの作成に関してより発展的な内容を解説します。
参考:Re-using Layouts with | Android Developers
参考:Dialogs | Android Developers
参考:Settings | Android Developers
- レイアウトのインクルード
- ダイアログ
-
設定
- [設定情報を xml に定義](#設定情報を xml に定義)
<include>
タグは別のレイアウトxmlを挿入する機能を提供します。
<include>
を使用することでレイアウトを再利用でき、アプリのコンポーネントが共通化されUIが統一できます。また、修正する際のコストが下がります。レイアウトが複雑でコードが肥大化する場合は、コードの分割に使用することで可読性が向上し、レイアウトの管理や各コンポーネントの配置替えが容易になります。
button_pannel.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/button_1" />
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/button_2" />
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/button_3" />
</LinearLayout>
include_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" >
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:text="@string/include_sample" />
<!-- 別のレイアウトファイルを挿入 -->
<include layout="@layout/button_pannel" />
</LinearLayout>
<include>
にはandroid:id
を設定することができるので、同じレイアウトを include しても個別に管理することができます。
<merge>
タグはレイアウトを合わせる機能を提供します。
通常レイアウトファイルは Viewgroup をルートViewとして配置していますが、<merge>
タグに置き換えることができます。
置き換えたレイアウトを include すると<merge>
タグは無視され、内部のレイアウトが include 先のレイアウトに直接とりこまれることになります。ViewGroup を取り除くことができるため、View の階層が1つ浅くなり描画コストが減ることになります。
merge適用前のレイアウト:button_no_apply_merge.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/button_1" />
</FrameLayout>
merge適用後のレイアウト:button_apply_merge.xml
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android" >
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/button_1" />
</merge>
merge_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" >
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:text="@string/merge_sample" />
<!-- マージされたレイアウトファイルを挿入 -->
<include layout="@layout/button_apply_merge" />
</LinearLayout>
上記レイアウトをそれぞれ実行し、Hierarchy View でレイアウト階層を確認すると以下のようになり、FrameLayoutが取り除かれているのがわかります。
merge前
merge後
Dialog はユーザーに意思決定や情報の入力を促すための小さいウィンドウです。
画面いっぱいに表示するものではなく、何らかの処理を行う前にユーザーへアクションを要求するのに使用します。
この他、進捗状況を知らせる為にも Dialog が使われます。
Dialog の表示には DialogFragment を使用することが推奨されています。 DialogFragment は Android 3.0 から使用出来る機能ですが、Support Library で提供されているため Android 2.x系でも使用することができます。
ユーザへ意思決定を求める Dialog を、AlertDialog と言います。
AlertDialog の構成は以下のようになっています。
- タイトルです。必須ではなくコンテンツ領域が詳細なメッセージなどで埋められている場合などに使用します。
- コンテンツ領域です。メッセージを表示します。また、独自のレイアウトに変更することもできます。
- アクションボタンです。ボタンの数は3つ以下とします。
一方、進捗状況を知らせる Dialog を、ProgressDialog と言います。
それぞれ、Dialog を表示すると、表示領域外がグレーアウトします。
このグレーアウトした領域をタップすると、キャンセル扱いとすることができるようになっています。
以下の手順が必要となります。
- DialogFragment を作成
- コンテンツ領域に独自レイアウトを使用するなら作成
- Dialog の表示処理を行う
独自のDialogFragment
/**
* Dialogを使用して、コンテンツ領域に独自レイアウトは表示するサンプルです。
*/
public static class MyDialogFragment extends DialogFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// 独自のレイアウトをコンテンツ領域表示する場合、ここでViewをinfrateして返却する
return inflater.inflate(R.layout.dialog_content, container, false);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Dialogを生成
Dialog dialog = super.onCreateDialog(savedInstanceState);
// タイトルを設定
dialog.setTitle(R.string.my_dialog_fragment);
return dialog;
}
}
コンテンツ領域に表示する独自レイアウト : dialog_content.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<ImageView
android:id="@+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:src="@drawable/ic_launcher" />
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/imageView1"
android:layout_marginLeft="17dp"
android:layout_marginTop="15dp"
android:layout_toRightOf="@+id/imageView1"
android:text="@string/custom_content" />
</RelativeLayout>
ダイアログの表示
MainActivity.java
DialogFragment myDialogFragment = new MyDialogFragment();
// 引数にFramentManagerとtagを設定します
myDialogFragment.show(getSupportFragmentManager(), "my_dialog_fragment");
/**
* AlertDialogを使用するサンプルです。コンテンツ領域に独自レイアウトは表示しません。
*/
public static class MyAlertDialogFragment extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// AlertDialogはBuilderパターンで生成
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage(R.string.alert_dialog_message)
// OKボタン
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
Toast.makeText(getActivity(), "Positive", Toast.LENGTH_SHORT).show();
}
})
// Cancelボタン
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
Toast.makeText(getActivity(), "Negative", Toast.LENGTH_SHORT).show();
}
});
// Dialogを作成して返却
return builder.create();
}
}
/**
* AlertDialogを使用しつつ、コンテンツ領域に独自レイアウトを表示するサンプルです。
*/
public static class ErrorMyAlertDialogFragment extends DialogFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Dialogと同じように表示したいコンテンツをinfrateして返却するとクラッシュします。
return inflater.inflate(R.layout.dialog_content, container, false);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
return builder.create();
}
}
onCreateDialog() で Dialog オブジェクトを返却する場合、onCreateView() でコンテンツ View を返却しようとするとクラッシュしてしまいます。
回避するには onCreateView() で null
を返却するか、onCreateView() をオーバライドしないようにします。
代わりに、コンテンツ View は builder の setView メソッドで設定します。
設定画面を作成する場合、他のアプリケーションとUI/UXの整合性がとれたインターフェースを提供する必要があります。
Android では設定画面に共通なUI/UXを提供できるようにするために Preference APIs が用意されています。Preference APIs を使用することで他のアプリケーションとの設定画面のUI/UXを統一することができます。
また、設定画面で変更されたデータは自動的に SharedPreferences に保存されます。
そのため、設定情報の保存処理を自身で実装する必要がなくなります。
設定画面を作成するには以下の手順が必要となります。
- 設定画面に表示する情報を xml に定義
- 表示する PreferenceActivity または PreferenceFragment を作成
- xml ファイルはres/xmlディレクトリに格納する必要があります。
- ファイル名は任意で決めることができますが、一般的にpreferences.xmlとすると良いです。
- マルチペインのレイアウトを作成した場合は、Fragment毎に個別のxmlファイルが必要です。
xml ファイルのルートタグは<PreferenceScreen>
とします。
<PreferenceScreen>
に追加された子要素(CheckBoxPreference
、ListPreference
、EditTextPreference
)は、設定画面の内の1つの項目として表示されます。
また、<PreferenceScreen>
内に<PreferenceScreen>
を配置することも可能です。
res/xml/preferences.xml
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
<CheckBoxPreference
android:defaultValue="true"
android:key="sample_checkbox_key"
android:summary="サンプルです"
android:title="チェックボックスのサンプル" />
<ListPreference
android:defaultValue="1"
android:dependency="sample_checkbox_key"
android:dialogTitle="リスト選択ダイアログ"
android:entries="@array/item_entries"
android:entryValues="@array/item_entries_value"
android:key="sample_list_key"
android:title="リストのサンプル" />
<EditTextPreference
android:dialogTitle="エディットテキストダイアログ"
android:key="edit_texit_key"
android:title="エディットテキストのサンプル" />
</PreferenceScreen>
res/values/array.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="item_entries">
<item>アイテム1</item>
<item>アイテム2</item>
<item>アイテム3</item>
</string-array>
<string-array name="item_entries_value">
<item>one</item>
<item>two</item>
<item>three</item>
</string-array>
</resources>
名前 | 説明 |
---|---|
android:key | SharedPreferences に保存する際に使用するキーとなります。要素が PreferenceCategory か PreferenceScreen かintentの呼び出し設定がされている場合は必須ではありません。 |
android:title | 設定画面に表示するタイトルです |
android:defaultValue | システムに SharedPreferences へ初期値を設定するように指定します |
android:entries |
ListPreference で表示するデータリスト |
android:entryValues |
ListPreference で選択したときに保存される値。android:entries と key-value な関係となります |
android:dependency | 他のpreferenceのandroid:keyを設定します。設定したpreferenceが未設定またはOFFの場合はdisableとなります。 |
設定項目がいくつもあった場合、ユーザーは設定したい項目がどこにあるのか見つけるのに苦労してしまいます。
PreferenceCategory
を使用することにより設定項目を関連するものどうしで分けることができ、表示をわかりやすくできます。
res/xml/preferences_category.xml
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- グループ分けされます -->
<PreferenceCategory
android:key="pref_key_storage_settings"
android:title="カテゴリ1" >
<CheckBoxPreference
android:key="category1_checkbox1_key"
android:title="カテゴリ1チェックボックス1" />
<CheckBoxPreference
android:key="category1_checkbox2_key"
android:title="カテゴリ1チェックボックス2" />
</PreferenceCategory>
<!-- グループ分けされます -->
<PreferenceCategory
android:key="pref_key_storage_settings"
android:title="カテゴリ2" >
<CheckBoxPreference
android:key="category2_checkbox1_key"
android:title="カテゴリ2チェックボックス1" />
</PreferenceCategory>
</PreferenceScreen>
グループとは別にPreferenceScreen
内にPreferenceScreen
を配置することで、サブスクリーンを作成して階層的に表示することができます。
res/xml/preferences_subscreen.xml
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- 別階層になります -->
<PreferenceScreen
android:key="subscreen_key"
android:title="サブスクリーン" >
<CheckBoxPreference
android:key="subscreen_checkbox1_key"
android:title="サブスクリーンチェックボックス1" />
<CheckBoxPreference
android:key="subscreen_checkbox2_key"
android:title="サブスクリーンチェックボックス2" />
</PreferenceScreen>
<CheckBoxPreference
android:key="screen_checkbox1_key"
android:title="チェックボックス1" />
</PreferenceScreen>
設定画面の変わりに、別のActivityやWebページを表示することができます。
res/xml/preferences_intent.xml
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- Web表示 -->
<Preference android:title="Intent Web" >
<intent
android:action="android.intent.action.VIEW"
android:data="http://mixi.jp/" />
</Preference>
<!-- Activityを起動 -->
<Preference android:title="Intent Activity" >
<intent
android:targetClass="jp.mixi.sample.preference.SubActivity"
android:targetPackage="jp.mixi.sample.preference" />
</Preference>
</PreferenceScreen>
設定画面を表示するためには PreferenceActivity を継承した Acitivity を作成し、使用します。
PreferenceActivity は設定が更新されると自動的に SharedPreferences に値を保存します。
アプリケーションのターゲットをAndroid3.0以降のものみとする場合はPreferenceFragmentを使用することが推奨されています。
onCreate() 内で addPreferencesFromResource() の引数として表示する設定ファイルを渡します。
SettingsActivity.java
public class SettingsActivity extends PreferenceActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
}
Fragment の onCreate() 内で addPreferencesFromResource() の引数として表示する設定ファイルを渡し、
Activity で Fragment を追加して表示します。
SettingsActivity2.java
public class SettingsActivity2 extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ActivityにFragmentを追加
getFragmentManager().beginTransaction()
.replace(android.R.id.content, new SettingsFragment())
.commit();
}
public static class SettingsFragment extends PreferenceFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
}
}
設定情報が変更されたときのコールバックメソッドが提供されています。
コールバックメソッドを使用することで、変更時に設定情報のサマリーを一緒に変更するといったことが可能となります。
SettingsActivity3.java
/**
* 設定変更時のコールバックサンプルです。
*/
public class SettingsActivity3 extends PreferenceActivity implements OnSharedPreferenceChangeListener {
// EditTextPreference で使用しているkey
private static final String EDIT_TEXT_KEY = "edit_texit_key";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
// 処理対象のPreferenceかをkeyで判定
if (key.equals(EDIT_TEXT_KEY)) {
// keyからPreferenceのインスタンスを取得
EditTextPreference editPref = (EditTextPreference) findPreference(EDIT_TEXT_KEY);
// Preferenceのサマリーを変更
editPref.setSummary(editPref.getText());
}
}
@Override
protected void onResume() {
super.onResume();
// リスナーを登録
getPreferenceScreen().getSharedPreferences()
.registerOnSharedPreferenceChangeListener(this);
}
@Override
protected void onPause() {
super.onPause();
// リスナーを登録解除
getPreferenceScreen().getSharedPreferences()
.unregisterOnSharedPreferenceChangeListener(this);
}
}
- (実習) include を使用したレイアウトを作成し、merge の有り無しで View の階層がどのようになるのかを Hierarchy View で確認してレポートしてください。
- (実習) 下記の画像のような Dialog を表示してください。
- (実習) DialogPractice2 のプロジェクトに、Activity と Fragment が配置されています。TODO コメントの指示通りのプログラムを記述してください。
- (課題) 下記の画像のような Dialog を表示してください。
-
DialogAssignment のスクリーンショット
OKボタンを押した時は入力した情報を SharedPreferences に保存するように実装してください。
- (実習) 下記の画像のような設定画面を実装してください。
- PreferencePractice のスクリーンショット1
-
PreferencePractice のスクリーンショット2
「自動同期の間隔」は「連絡先の同期設定」がONの場合のみ有効にしてください。
「自動同期の間隔」は変更内容を Summary に反映させてください。
Portions of this page are reproduced from work created and shared by the Android Open Source Project and used according to terms described in the Creative Commons 2.5 Attribution License.