Android
断片
サーチ…
前書き
フラグメントとその相互通信メカニズムの紹介
構文
void onActivityCreated(Bundle savedInstanceState)//フラグメントのアクティビティが作成され、このフラグメントのビュー階層がインスタンス化されたときに呼び出されます。
void onActivityResult(int requestCode、int resultCode、Intent data)//前回のstartActivityForResult(Intent、int)呼び出しの結果を受け取ります。
void onAttach(Activity activity)//このメソッドはAPIレベル23では非推奨です。代わりにonAttach(Context)を使用してください。
void onAttach(コンテキストコンテキスト)//フラグメントが最初にそのコンテキストにアタッチされたときに呼び出されます。
void onAttachFragment(Fragment childFragment)//フラグメントがこのフラグメントの子としてアタッチされたときに呼び出されます。
void onConfigurationChanged(Configuration newConfig)//コンポーネントの実行中にデバイスの設定が変更されたときに、システムによって呼び出されます。
void onCreate(Bundle savedInstanceState)//フラグメントの初期作成を行うために呼び出されます。
View onCreateView(LayoutInflaterインフレータ、ViewGroupコンテナ、Bundle savedInstanceState)//フラグメントがユーザインタフェースビューをインスタンス化するように呼び出されます。
void onDestroy()//フラグメントが使用されなくなったときに呼び出されます。
void onDestroyView()onCreateView(LayoutInflater、ViewGroup、Bundle)によって以前に作成されたビューがフラグメントから切り離されたときに呼び出されます。
void onDetach()//フラグメントがそのアクティビティにアタッチされなくなったときに呼び出されます。
void onInflate(アクティビティアクティビティ、AttributeSet attrs、Bundle savedInstanceState)//このメソッドはAPIレベル23で廃止されました。代わりにonInflate(Context、AttributeSet、Bundle)を使用してください。
void onInflate(コンテキストコンテキスト、AttributeSet attrs、Bundle savedInstanceState)//ビューレイアウトインフレーションの一部としてフラグメントが作成されるときに呼び出されます。通常は、アクティビティのコンテンツビューを設定します。
void onPause()//フラグメントがもう再開されなくなったときに呼び出されます。
void onResume()//フラグメントがユーザーに表示され、アクティブに実行されているときに呼び出されます。
void onSaveInstanceState(Bundle outState)//現在の動的状態を保存するようにフラグメントを要求するために呼び出され、後でそのプロセスの新しいインスタンスで再構築できます。
void onStart()//フラグメントがユーザーに表示されたときに呼び出されます。
void onStop()//フラグメントが開始されなくなったときに呼び出されます。
void onViewStateRestored(Bundle savedInstanceState)//保存されたすべての状態がフラグメントのビュー階層に復元されたときに呼び出されます。
備考
フラグメントは、アクティビティーのビヘイビアまたはユーザー・インターフェースの一部を表します。 1つのアクティビティで複数のフラグメントを結合して、マルチペインのUIを構築し、複数のアクティビティでフラグメントを再利用することができます。フラグメントは、独自のライフサイクルを持ち、独自の入力イベントを受け取り、アクティビティが実行されている間に追加または削除できる(「サブアクティビティ」のようなもの)アクティビティのモジュラセクションと考えることができます異なる活動における再利用)。
コンストラクタ
すべてのフラグメントは空のコンストラクタを持つ必要があるため、アクティビティの状態を復元するときにインスタンス化できます。フラグメントが再インスタンス化されたときにこれらのコンストラクタが呼び出されることはないため、サブクラスにパラメータを持つ他のコンストラクタがないことを強くお勧めします。その代わりに、引数はsetArguments(Bundle)で呼び出し元から提供され、後でgetArguments()でFragmentによって取得できます。newInstance()パターン
パラメータを持つフラグメントコンストラクタを作成することは可能ですが、Androidはフラグメントを再作成するときに内部的に引数のないコンストラクタを呼び出します(たとえば、Android独自の理由でそれらが復元された場合など)。このため、パラメータを持つコンストラクタを使用することはお勧めできません。
期待されるフラグメント引数が常に存在するようにするには、静的なnewInstance()
メソッドを使用してフラグメントを作成し、新しいインスタンスを作成するときに使用できる任意のパラメータをバンドルに入れることができます。
import android.os.Bundle;
import android.support.v4.app.Fragment;
public class MyFragment extends Fragment
{
// Our identifier for obtaining the name from arguments
private static final String NAME_ARG = "name";
private String mName;
// Required
public MyFragment(){}
// The static constructor. This is the only way that you should instantiate
// the fragment yourself
public static MyFragment newInstance(final String name) {
final MyFragment myFragment = new MyFragment();
// The 1 below is an optimization, being the number of arguments that will
// be added to this bundle. If you know the number of arguments you will add
// to the bundle it stops additional allocations of the backing map. If
// unsure, you can construct Bundle without any arguments
final Bundle args = new Bundle(1);
// This stores the argument as an argument in the bundle. Note that even if
// the 'name' parameter is NULL then this will work, so you should consider
// at this point if the parameter is mandatory and if so check for NULL and
// throw an appropriate error if so
args.putString(NAME_ARG, name);
myFragment.setArguments(args);
return myFragment;
}
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Bundle arguments = getArguments();
if (arguments == null || !arguments.containsKey(NAME_ARG)) {
// Set a default or error as you see fit
} else {
mName = arguments.getString(NAME_ARG);
}
}
}
今、活動の中で:
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
MyFragment mFragment = MyFragment.newInstance("my name");
ft.replace(R.id.placeholder, mFragment);
//R.id.placeholder is where we want to load our fragment
ft.commit();
このパターンは、必要なすべての引数が作成時にフラグメントに確実に渡されるようにするためのベストプラクティスです。フラグメントを破棄して後で再作成すると、自動的に状態が復元されますが、 onSaveInstanceState(Bundle)
実装を提供する必要があります。
バックスタックとスタティックファブリックパターンを使用したフラグメント間のナビゲーション
まず最初に最初のFragment
を追加する必要があります。これは、アクティビティのonCreate()
メソッドで行います。
if (null == savedInstanceState) {
getSupportFragmentManager().beginTransaction()
.addToBackStack("fragmentA")
.replace(R.id.container, FragmentA.newInstance(), "fragmentA")
.commit();
}
次に、バックスタックを管理する必要があります。最も簡単な方法は、すべてのFragmentTransactionsで使用される、アクティビティで追加された関数を使用することです。
public void replaceFragment(Fragment fragment, String tag) {
//Get current fragment placed in container
Fragment currentFragment = getSupportFragmentManager().findFragmentById(R.id.container);
//Prevent adding same fragment on top
if (currentFragment.getClass() == fragment.getClass()) {
return;
}
//If fragment is already on stack, we can pop back stack to prevent stack infinite growth
if (getSupportFragmentManager().findFragmentByTag(tag) != null) {
getSupportFragmentManager().popBackStack(tag, FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
//Otherwise, just replace fragment
getSupportFragmentManager()
.beginTransaction()
.addToBackStack(tag)
.replace(R.id.container, fragment, tag)
.commit();
}
最後に、 onBackPressed()
をオーバーライドして、バックスタックで使用可能な最後のフラグメントから戻るときにアプリケーションを終了する必要があります。
@Override
public void onBackPressed() {
int fragmentsInStack = getSupportFragmentManager().getBackStackEntryCount();
if (fragmentsInStack > 1) { // If we have more than one fragment, pop back stack
getSupportFragmentManager().popBackStack();
} else if (fragmentsInStack == 1) { // Finish activity, if only one fragment left, to prevent leaving empty screen
finish();
} else {
super.onBackPressed();
}
}
アクティビティでの実行:
replaceFragment(FragmentB.newInstance(), "fragmentB");
活動外の実行( MainActivity
が私たちの活動であると仮定します):
((MainActivity) getActivity()).replaceFragment(FragmentB.newInstance(), "fragmentB");
バンドルを使用して、アクティビティからフラグメントにデータを渡す
すべてのフラグメントに空のコンストラクタ(つまり、入力引数を持たないコンストラクタメソッド)が必要です。したがって、作成するフラグメントにデータを渡すには、 setArguments()
メソッドを使用する必要がありsetArguments()
。このメソッドは、データを格納するバンドルを取得し、そのバンドルを引数に格納します。その後、このBundleは、フラグメントのonCreate()
およびonCreateView()
コールバックで取り出すことができます。
アクティビティ:
Bundle bundle = new Bundle();
String myMessage = "Stack Overflow is cool!";
bundle.putString("message", myMessage );
FragmentClass fragInfo = new FragmentClass();
fragInfo.setArguments(bundle);
transaction.replace(R.id.fragment_single, fragInfo);
transaction.commit();
断片:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
String myValue = this.getArguments().getString("message");
...
}
コールバック・インタフェースを使用してアクティビティにイベントを戻す
フラグメントをアクティビティーからイベントに送信する必要がある場合、可能な解決策の1つは、コールバック・インターフェースを定義し、ホスト・アクティビティーがそれを実装することを要求することです。
例
フラグメントのボタンがクリックされたときにアクティビティにコールバックを送信する
まず、コールバックインタフェースを定義します。public interface SampleCallback {
void onButtonClicked();
}
次のステップは、このコールバックをフラグメントに割り当てることです。
public final class SampleFragment extends Fragment {
private SampleCallback callback;
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof SampleCallback) {
callback = (SampleCallback) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement SampleCallback");
}
}
@Override
public void onDetach() {
super.onDetach();
callback = null;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.sample, container, false);
// Add button's click listener
view.findViewById(R.id.actionButton).setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
callback.onButtonClicked(); // Invoke callback here
}
});
return view;
}
}
最後に、アクティビティでコールバックを実装します。
public final class SampleActivity extends Activity implements SampleCallback {
// ... Skipped code with settings content view and presenting the fragment
@Override
public void onButtonClicked() {
// Invoked when fragment's button has been clicked
}
}
フラグメント間の遷移をアニメーション化する
フラグメント間の遷移をアニメーション化するために、またはあなたが使用してフラグメントを示すかに隠れてのプロセスアニメーション化するFragmentManager
作成するFragmentTransaction
。
単一のFragmentTransaction
場合、アニメーションを実行する方法は2つあります。標準アニメーションを使用することも、独自のカスタムアニメーションを提供することもできます。
標準アニメーションは、 FragmentTransaction.setTransition(int transit)
呼び出し、 FragmentTransaction
クラスで使用可能な事前定義された定数のいずれかを使用して指定します。執筆時点では、これらの定数は次のとおりです。
FragmentTransaction.TRANSIT_NONE
FragmentTransaction.TRANSIT_FRAGMENT_OPEN
FragmentTransaction.TRANSIT_FRAGMENT_CLOSE
FragmentTransaction.TRANSIT_FRAGMENT_FADE
完全なトランザクションは次のようになります。
getSupportFragmentManager()
.beginTransaction()
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.replace(R.id.contents, new MyFragment(), "MyFragmentTag")
.commit();
カスタムアニメーションは、 FragmentTransaction.setCustomAnimations(int enter, int exit)
またはFragmentTransaction.setCustomAnimations(int enter, int exit, int popEnter, int popExit)
いずれかを呼び出して指定します。
enter
とexit
アニメーションは、バックスタックからフラグメントをポップすることを伴わないFragmentTransaction
に対して再生されます。 popEnter
とpopExit
バックスタックからの断片をポップするときのアニメーションが再生されます。
次のコードでは、フラグメントを1つスライディングして別のスライスをスライディングして置き換える方法を示します。
getSupportFragmentManager()
.beginTransaction()
.setCustomAnimations(R.anim.slide_in_left, R.anim.slide_out_right)
.replace(R.id.contents, new MyFragment(), "MyFragmentTag")
.commit();
XMLアニメーション定義はobjectAnimator
タグを使用します。 slide_in_left.xmlの例は、 次のようになります。
<?xml version="1.0" encoding="utf-8"?>
<set>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:propertyName="x"
android:valueType="floatType"
android:valueFrom="-1280"
android:valueTo="0"
android:duration="500"/>
</set>
フラグメント間の通信
フラグメント間のすべての通信はアクティビティ経由で行わなければなりません。フラグメントはアクティビティなしでは互いに通信できません 。
その他のリソース
このサンプルでは、 message
を送信したり受信したりするためのSenderFragment
とReceiverFragment
2つのフラグメント(この場合は単純なString)をそれぞれホストするMainActivity
があります。
SenderFragment
のボタンは、メッセージを送信するプロセスを開始します。 ReceiverFragment
内のTextViewは、メッセージが受信されると更新されます。
MainActivityのスニペットには、重要なコード行を説明するコメントがあります:
// Our MainActivity implements the interface defined by the SenderFragment. This enables
// communication from the fragment to the activity
public class MainActivity extends AppCompatActivity implements SenderFragment.SendMessageListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
/**
* This method is called when we click on the button in the SenderFragment
* @param message The message sent by the SenderFragment
*/
@Override
public void onSendMessage(String message) {
// Find our ReceiverFragment using the SupportFragmentManager and the fragment's id
ReceiverFragment receiverFragment = (ReceiverFragment)
getSupportFragmentManager().findFragmentById(R.id.fragment_receiver);
// Make sure that such a fragment exists
if (receiverFragment != null) {
// Send this message to the ReceiverFragment by calling its public method
receiverFragment.showMessage(message);
}
}
}
MainActivity
のレイアウトファイルは、 MainActivity
内の2つのフラグメントをホストします。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
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="com.naru.fragmentcommunication.MainActivity">
<fragment
android:id="@+id/fragment_sender"
android:name="com.naru.fragmentcommunication.SenderFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
tools:layout="@layout/fragment_sender" />
<fragment
android:id="@+id/fragment_receiver"
android:name="com.naru.fragmentcommunication.ReceiverFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
tools:layout="@layout/fragment_receiver" />
</LinearLayout>
SenderFragment
、インタフェース公開しSendMessageListener
役立ちますMainActivity
でボタンとき知っSenderFragment
クリックされたし。
以下は、重要なコード行を説明するSenderFragment
コードスニペットです。
public class SenderFragment extends Fragment {
private SendMessageListener commander;
/**
* This interface is created to communicate between the activity and the fragment. Any activity
* which implements this interface will be able to receive the message that is sent by this
* fragment.
*/
public interface SendMessageListener {
void onSendMessage(String message);
}
/**
* API LEVEL >= 23
* <p>
* This method is called when the fragment is attached to the activity. This method here will
* help us to initialize our reference variable, 'commander' , for our interface
* 'SendMessageListener'
*
* @param context
*/
@Override
public void onAttach(Context context) {
super.onAttach(context);
// Try to cast the context to our interface SendMessageListener i.e. check whether the
// activity implements the SendMessageListener. If not a ClassCastException is thrown.
try {
commander = (SendMessageListener) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString()
+ "must implement the SendMessageListener interface");
}
}
/**
* API LEVEL < 23
* <p>
* This method is called when the fragment is attached to the activity. This method here will
* help us to initialize our reference variable, 'commander' , for our interface
* 'SendMessageListener'
*
* @param activity
*/
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// Try to cast the context to our interface SendMessageListener i.e. check whether the
// activity implements the SendMessageListener. If not a ClassCastException is thrown.
try {
commander = (SendMessageListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ "must implement the SendMessageListener interface");
}
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
// Inflate view for the sender fragment.
View view = inflater.inflate(R.layout.fragment_receiver, container, false);
// Initialize button and a click listener on it
Button send = (Button) view.findViewById(R.id.bSend);
send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Sanity check whether we were able to properly initialize our interface reference
if (commander != null) {
// Call our interface method. This enables us to call the implemented method
// in the activity, from where we can send the message to the ReceiverFragment.
commander.onSendMessage("HELLO FROM SENDER FRAGMENT!");
}
}
});
return view;
}
}
SenderFragment
のレイアウトファイル:
<?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="match_parent"
android:gravity="center"
android:orientation="vertical">
<Button
android:id="@+id/bSend"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SEND"
android:layout_gravity="center_horizontal" />
</LinearLayout>
ReceiverFragment
はシンプルで、シンプルなパブリックメソッドを公開してTextViewを更新します。 MainActivity
からメッセージを受け取ると、 ReceiverFragment
このパブリックメソッドがSenderFragment
れます
ReceiverFragment
コードスニペットは、重要なコード行を説明するコメント付きです:
public class ReceiverFragment extends Fragment {
TextView tvMessage;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
// Inflate view for the sender fragment.
View view = inflater.inflate(R.layout.fragment_receiver, container, false);
// Initialize the TextView
tvMessage = (TextView) view.findViewById(R.id.tvReceivedMessage);
return view;
}
/**
* Method that is called by the MainActivity when it receives a message from the SenderFragment.
* This method helps update the text in the TextView to the message sent by the SenderFragment.
* @param message Message sent by the SenderFragment via the MainActivity.
*/
public void showMessage(String message) {
tvMessage.setText(message);
}
}
ReceiverFragment
のレイアウトファイル:
<?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="match_parent"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/tvReceivedMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Waiting for message!" />
</LinearLayout>