DrawerLayout

スワイプで横からメニューが出てきて、選択すると画面が切り替わるUIをナビゲーションドロワーなどと呼ぶ。
そのUIを作るためのレイアウトがDrawerLayout。

DrawerLayout  |  Android Developers

strings.xmlで、

メインのアクティビティ側にあるTextViewに"MainContent"、サイドメニューのアイテムとして置くTextViewに"Drawer"とセットする。
activity_main.xmlで、

DrawerLayoutの中には、メインコンテンツ側とサイドメニュー側の2つのレイアウトViewを順に置く決まりになっている模様。

NavigationDrawerをつかって、スライド式のメニューを実装する | TechBooster

ここにある制約どおりに作らないと表示されないこともある。
(サイドメニュー幅240dp以上とか、アイテム高さ48dp以上とか、backgroundで色付けないとTextView見えないとか。)

起動時
起動時

起動時は"MainContent"が出てる。
エミュレータでスワイプ難しいけど、スワイプする。
(マウスで1, 2pxぐらい内側からつまむでいいみたい。画面外からスワイプできない。)

うまく引き出せると、赤い背景の"Drawer"が出る。
うまく引き出せると、赤い背景の"Drawer"が出る。

うまく引き出せると、赤い背景の"Drawer"が出る。
スワイプで戻すか、暗くなった部分を押すとキャンセルなので、

元に戻る
元に戻る

元に戻る。

Sample/android/DrawerLayout/DrawerLayout/src/DrawerLayout at master · bg1bgst333/Sample · GitHub

FragmentStatePagerAdapter

FragmentPagerAdapterは、画面切り替えでViewをattach, detachする。
Viewの状態は維持されているため、メモリを使う。
FragmentStatePagerAdapterは、画面切り替えでadd, removeを使っているので、Viewが破棄される分メモリは使わない。
ただし、Viewの復元には工夫が要る。

FragmentStatePagerAdapter  |  Android Developers

CustomFragment.javaで、

初回生成の時は、はsavedInstanceStateがnullなので、newInstanceの時にsetArgumentsしたテキストをgetArgumentsで取得すればいい。
切り替えで破棄と再生成される時は、onSaveInstanceStateでテキストを保存しておいてるので、savedInstanceStateがnullではないから、そこから取得する。
どっちに来るかログを出してるので、あとで見てみる。
CustomFragmentStatePagerAdapter.javaは、

FragmentStatePagerAdapterから派生させてる以外は変わらない。
MainActivity.javaでは、

CustomFragmentStatePagerAdapterオブジェクトcfspaをセットしてる。

起動時
起動時

起動時。
Zeroだけど、

右隣のOneも初回生成
右隣のOneも初回生成

右隣のOneも初回生成。

スワイプでOneに移動
スワイプでOneに移動

スワイプでOneに移動。

Twoが追加生成
Twoが追加生成

Twoが追加生成。

Twoに移動
Twoに移動

Twoに移動。

Threeが追加生成
Threeが追加生成

Threeが追加生成。

Threeに移動
Threeに移動

Threeに移動。

Fourが生成
Fourが生成

Fourが生成。

Fourに移動
Fourに移動

Fourに移動。

この時は何も起きない
この時は何も起きない

この時は何も起きない。

Threeに戻る
Threeに戻る

Threeに戻る。

保存していたテキストTwoを使ってTwoのViewが復活
保存していたテキストTwoを使ってTwoのViewが復活

保存していたテキストTwoを使ってTwoのViewが復活。

Twoに移動すると
Twoに移動すると

Twoに移動すると、

OneのViewが復活
OneのViewが復活

OneのViewが復活。

Sample/android/FragmentStatePagerAdapter/FragmentStatePagerAdapter/src/FragmentStatePagerAdapter at master · bg1bgst333/Sample · GitHub

FragmentPagerAdapter

スワイプして画面を変えるのはViewPagerやPagerAdapterでやったが、FragmentPagerAdapterにすると画面の単位をViewからFragmentにできる。

FragmentPagerAdapter  |  Android Developers

strings.xmlで、

Fragmentの中のTextViewにとりあえず仮でCustomFragmentと入れてる。
custom_fragment_main.xmlで、

TextView。
activity_main.xmlで、

ViewPagerを置く。
CustomFragment.javaで、

newInstanceで渡された文字列を"text"をキーに登録。
onCreateViewの時にそれをTextViewにセット。
CustomFragmentPagerAdapter.javaで、

getItemでページ番号ごとに"Zero"から"Four"までの文字列をセットしたCustomFragmentを生成してそれを返す感じ。
MainActivity.javaで、

viewPagerにCustomFragmentPagerAdapterのcfpaをセット。
ここまで見てわかるように、ActivityやFragmentは、SupportLibraryの方を使う。

起動時はZero
起動時はZero

起動時はZero。
右にスワイプすると、

One
One

One。
さらに右にスワイプすると、

Two
Two

Two。
さらにスワイプで、

Three
Three

Three。
最後に、

Four
Four

Four。

Sample/android/FragmentPagerAdapter/FragmentPagerAdapter/src/FragmentPagerAdapter at master · bg1bgst333/Sample · GitHub

DialogFragment.dismissAllowingStateLoss

FragmentTransaction.commitAllowingStateLossで、バックグラウンドに置いておく前にFragmentの状態を保存しておいて、その間に非同期処理などを経てFragment状態変更をしてしまうと例外が発生がしてしまうことがあった。
DialogFragmentにおいても、バックグラウンドに置いておいた状態で、非同期処理後にダイアログ閉じようとすると、例外が起きる。
DialogFragment.dismissAllowingStateLossで、それを抑止できる。

DialogFragment  |  Android Developers

CustomAsyncTask.javaで、

package com.bgstation0.android.sample.dialogfragment_;

import android.content.Context;
import android.os.AsyncTask;
import android.widget.Toast;

public class CustomAsyncTask extends AsyncTask<Integer, Integer, String> {	// 非同期処理に渡すパラメータがInteger, 進捗状態がInteger, 非同期処理の結果がStringのCustomAsyncTask.

	// メンバフィールドの定義
	Context context;	// Context型context
	CustomDialogFragment cdf;	// CustomDialogFragment型cdf
	
	// コンストラクタ
	public CustomAsyncTask(Context context){
		this.context = context;	// this.contextメンバに格納.
	}
	
	// コンストラクタ(フラグメントも受け取る.)
	public CustomAsyncTask(Context context, CustomDialogFragment cdf){
		this(context);
		this.cdf = cdf;	// this.cdfメンバに格納.
	}

	// 非同期処理
	@Override
	protected String doInBackground(Integer... params){
		// オブジェクトの宣言
		String ret = "";	// 戻り値の文字列retに""をセット.
		// tryで囲む.
		try{
			Thread.sleep(10000);	// 10秒休止
			if (params.length > 0){	// paramsの要素数が1つでもある.
				ret = "params[0] = " + params[0];	// retに0番目の引数.
			}
			else{
				ret = "params[0] = nothing";	// retにnothing.
			}
		}
		catch (Exception ex){
			ret = "excaption";	// retにexception.
		}
		return ret;
	}

	// UIスレッドの更新
	@Override
	protected void onPostExecute(String result){
		// cdfの非表示.
		try{
			cdf.dismiss();	// cdf.dismissdで非表示.
			Toast.makeText(context, "cdf.dismiss() success!", Toast.LENGTH_LONG).show();	// "cdf.dismiss() success!"とToast表示.
		}
		catch (Exception ex){
			Toast.makeText(context, ex.toString(), Toast.LENGTH_LONG).show();	// ex.toString()をToast表示.
		}
	}
	
}

まず、そのままdismissでやると、

起動時
起動時

起動時。
Button1押して、

ダイアログ表示
ダイアログ表示

ダイアログ表示。
ここのButton1押して、

10秒後にダイアログ閉じて非表示成功
10秒後にダイアログ閉じて非表示成功

10秒後にダイアログ閉じて非表示成功。
これはいいんだけど、次の場合について考える。

ダイアログ表示中にButton1押す
ダイアログ表示中にButton1押す

ダイアログ表示中にButton1押す。
そして、10秒経たないうちに素早くホームボタンを押す。

10秒経つと例外が出てしまう
10秒経つと例外が出てしまう

10秒経つと例外が出てしまう。

横にしてDialogFragmentを起動する
横にしてDialogFragmentを起動する

横にしてDialogFragmentを起動する。

閉じられていない状態になっちゃってる
閉じられていない状態になっちゃってる

閉じられていない状態になっちゃってる。

ちなみに回転しないで復帰すると閉じられている
ちなみに回転しないで復帰すると閉じられている

ちなみに回転しないで復帰すると閉じられている。

cdf.dismissAllowingStateLossにすると、

例外は起きない
例外は起きない

例外は起きない。
ただ、回転して復帰だとダイアログ表示状態、回転しないと非表示で、ここは変わってなかった。
まあ、tryを囲まずに例外が起きないってことかな。

Sample/android/DialogFragment/dismissAllowingStateLoss/src/DialogFragment at master · bg1bgst333/Sample · GitHub

DialogFragment.onDismiss

DialogFragment.onDismissは、dismiss(非表示)された時に呼ばれる。

DialogFragment  |  Android Developers

CustomDialogFragment.javaで、

onDismiss呼ばれたら、"onDismiss"とToast表示。

起動時
起動時

起動時。
Button押すと、

ダイアログ表示でまたButtonを押す
ダイアログ表示でまたButtonを押す

ダイアログ表示でまたButtonを押すと、

10秒後に非表示になり、Toastで&quot;onDismiss&quot;が表示される。
10秒後に非表示になり、Toastで"onDismiss"が表示される。

10秒後に非表示になり、Toastで"onDismiss"が表示される。

Sample/android/DialogFragment/onDismiss/src/DialogFragment at master · bg1bgst333/Sample · GitHub

DialogFragment.dismiss

DialogFragment.dismissで、ダイアログを非表示にする。

DialogFragment  |  Android Developers

CustomAsyncTask.javaを追加し、

コンストラクタでCustomDialogFragmentオブジェクトcdfを受け取って、10秒経ったら、cdf.dismissを呼んでダイアログを非表示。
CustomDialogFragment.javaは、

CustomDialogFragment上のButton1を押すと、非同期処理が始まって、あとは先程の処理が行われる。

起動時
起動時

起動時。
Button1押して、

ダイアログが出るのでここでもButton1を押す
ダイアログが出るのでここでもButton1を押す

ダイアログが出るのでここでもButton1を押す。
で10秒経つと、

このようにダイアログが非表示になる
このようにダイアログが非表示になる

このようにダイアログが非表示になる。

Sample/android/DialogFragment/dismiss/src/DialogFragment at master · bg1bgst333/Sample · GitHub

DialogFragment.onCancel

DialogFragment.onCancelに、ダイアログがキャンセルされた時の処理を書く。

DialogFragment  |  Android Developers

CustomDialogFragment.javaで、

onCreateViewでレイアウトを指定するバージョンに戻してる。
onCancelが呼ばれたら、"onCancel"をToast表示。

起動時
起動時

起動時。
Button押すと、

以前のレイアウトに戻してる
以前のレイアウトに戻してる

以前のTextView1とButton1のレイアウトに戻ってる。
この場合は、ダイアログ外を押すと、キャンセルになる。
キャンセルで、

onCancelが呼ばれたのがわかる
onCancelが呼ばれたのがわかる

onCancelが呼ばれたのがわかる。

Sample/android/DialogFragment/onCancel/src/DialogFragment at master · bg1bgst333/Sample · GitHub