フラグメントから、アクティビティにボタンが押されたことを通知したい
フラグメントから、アクティビティにボタンが押されたことを通知するための、コールバックというしくみに、
それなりの手順が必要ということで、メモしておこうというコーナーです。
まずは、こんなサンプルを用意。
フラグメントのサンプルコードのサンプルコードに、ボタンとテキストを足したものです。
左の黄色の画面はアクティビティ。その右側は、フラグメントで表示。
フラグメント側のボタンを押して、アクティビティの画面の背景色を変えてみようという趣向です。
ボタンを押すと、右側のフラグメントの画面では、ボタンが押されたことがリスナーで受け取れますが、
アクティビティ側に情報を渡す部分のコードが書かれてないので、。
このままでは、アクティビティ側は、ボタンが押されたことを知ることが出来ません。
APIレベル4以後で動くように、サポートライブラリを使用したサンプルです。
フラグメント用XMLファイルは使用していません。
AndroidManifest.xmlは、新規作成状態から変更していません。
FragmentF1.java
package com.example.test10;
import android.support.v4.app.Fragment; //これをインポートすること
//import android.app.Fragment; //これをインポートするとエラーになる
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
public class FragmentF1 extends Fragment{
public FragmentF1() {}
private TextView text1;
private TextView text2;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//レイアウトを作る
LinearLayout layout1 = new LinearLayout(getActivity());
//レイアウトは縦に並ぶ
layout1.setOrientation(LinearLayout.VERTICAL);
//Bundleのデータ読み込み
String moji1 = getArguments().getString("data1");
//-----------------------------------------
//アイコン画像を読み込み
Bitmap img1 = BitmapFactory.decodeResource(
getResources(),R.drawable.ic_launcher);
//アイコン画像を貼り付ける画面
ImageView imgvw1 = new ImageView(getActivity());
//背景色
imgvw1.setBackgroundColor(Color.argb(255, 255, 0, 0));
//画面にアイコン画像をセット
imgvw1.setImageBitmap(img1);
//サイズをセット
LayoutParams para1 = new LayoutParams(200,200);
//画面のサイズをセット
imgvw1.setLayoutParams(para1);
//アイコンを描画した画面を並べる
layout1.addView(imgvw1);
//-----------------------------------------
//テキストを貼り付ける
text1 = new TextView(getActivity());
//テキストをセット
text1.setText(moji1);
//テキストを並べる
layout1.addView(text1);
//-----------------------------------------
//ボタンをつくる
Button button1 = new Button(getActivity());
//ボタンに文字を表示
button1.setText("Blue");
//ボタンをレイアウトに追加
layout1.addView(button1);
//-----------------------------------------
//ボタンをつくる
Button button2 = new Button(getActivity());
//ボタンに文字を表示
button2.setText("Green");
//ボタンをレイアウトに追加
layout1.addView(button2);
//-----------------------------------------
//テキストを貼り付ける
text2 = new TextView(getActivity());
//テキストをセット
text2.setText("ボタンが押された?");
//テキストを並べる
layout1.addView(text2);
//-----------------------------------------
//ボタンのリスナー
button1.setOnClickListener( new OnClickListener() {
//ボタンが押されたら何かする
@Override
public void onClick(View v) {
//テキストをセット
text1.setText("Blueが押された");
}
});
//ボタンのリスナー
button2.setOnClickListener( new OnClickListener() {
//ボタンが押されたら何かする
@Override
public void onClick(View v) {
//テキストをセット
text1.setText("Greenが押された");
}
});
//-----------------------------------------
return layout1;
}
public void ChangeColor1(String color){
//テキストをセット
text2.setText(color);
}
}
| |
MainActivity.java
package com.example.test10;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.os.Bundle;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
public class MainActivity extends FragmentActivity {
private static final int LAYOUT_ID1 = 777;
private ImageView view1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//----------------------------------------
//親レイアウト1
LinearLayout L_layout1 = new LinearLayout(this);
setContentView(L_layout1);
//横に並ぶ
L_layout1.setOrientation(LinearLayout.HORIZONTAL);
//-----------------------------------------
//アイコン画像をビューに読み込む
Bitmap image1 = BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher);
view1 = new ImageView(this);
view1.setImageBitmap(image1);
//サイズ
LinearLayout.LayoutParams params1 =
new LinearLayout.LayoutParams(200, ViewGroup.LayoutParams.MATCH_PARENT);
view1.setLayoutParams(params1);
//背景を黄色にする
view1.setBackgroundColor(Color.argb(255, 255, 255, 0));
//アイコン画像のビューを親レイアウト1に追加
L_layout1.addView(view1);
//--------------------------------------------
//子レイアウト2
LinearLayout L_layout2 = new LinearLayout(this);
//子レイアウト2の背景を水色にする
L_layout2.setBackgroundColor(Color.argb(255, 0, 255, 255));
//サイズ
LinearLayout.LayoutParams layoutParams2 =
new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
//子レイアウト2を親レイアウト1に追加
L_layout1.addView(L_layout2,layoutParams2);
//--------------------------------------------
//子レイアウト2に、フラグメントを設定するためのID
L_layout2.setId(LAYOUT_ID1);
if ( savedInstanceState == null ) {
//アクティビティが生成された時に1度だけフラグメントに描画
Put_fragment1();
}
}
public void Put_fragment1(){
//FragmentManagerを設定
FragmentManager manager = getSupportFragmentManager();
//Transactionを設定
FragmentTransaction ft = manager.beginTransaction();
//----------------------------------
//フラグメントに貼るデータを作る
FragmentF1 frag1 = new FragmentF1();
Bundle bd1 = new Bundle();
bd1.putString("data1", "フラグメントデータ");
//フラグメントにバンドルを渡す
frag1.setArguments(bd1);
//----------------------------------
//フラグメントに追加する
ft.add(LAYOUT_ID1, frag1);
ft.commit();
}
}
| |
AndroidManifest.xml
アクティビティに、ボタンが押されたことを通知する
まず、FragmentF1.javaに変更を加えていきます。
class FragmentF1に、インターフェイスとして、
「onFragmentButtonClickedListener」「onFragmentButton1」を用意します。
名前は適当でいいです。
FragmentF1.java
//ボタンの押されたことを通知
public interface onFragmentButtonClickedListener {
public void onFragmentButton1(int button);
}
| |
「listener」を冒頭に追加します。
public class FragmentF1 extends Fragment{
public FragmentF1() {}
private TextView text1;
private TextView text2;
private onFragmentButtonClickedListener listener;
| |
ボタンのリスナーのコードの中で、「listener.onFragmentButton1」を呼びます。
ボタンが押されたことがアクティビティにコールバックされます。
//ボタンのリスナー
button1.setOnClickListener( new OnClickListener() {
//ボタンが押されたら何かする
@Override
public void onClick(View v) {
//テキストをセット
text1.setText("Blueが押された");
listener.onFragmentButton1(1);
}
});
//ボタンのリスナー
button2.setOnClickListener( new OnClickListener() {
//ボタンが押されたら何かする
@Override
public void onClick(View v) {
//テキストをセット
text1.setText("Greenが押された");
listener.onFragmentButton1(2);
}
});
| |
「onAttach」を追加して、中身を記述します。
「onAttach」は、フラグメントがアクティビティと関連付けられた時に、一度だけ呼ばれます
//Activityに関連付けされた時に一度だけ呼ばれる
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
listener = (onFragmentButtonClickedListener)activity;
}
| |
「Activity」でエラーが出るので、
「import android.app.Activity;」をインポートで追加します。
import android.app.Activity;
| |
アクティビティ側から、ボタンが押されたことの通知を受けとる
次に、MainActivity.javaに変更を加えていきます。
1行目に、「implements onFragmentButtonClickedListener」を追加します。
MainActivity.java
public class MainActivity extends FragmentActivity implements onFragmentButtonClickedListener{
| |
すると、エラーになるはずですので、eclipseの指示に従い、
「onFragmentButtonClickedListener」を、FragmentF1から、インポートします。
import com.example.test10.FragmentF1.onFragmentButtonClickedListener;
| |
すると、また、エラーになるはずですので、eclipseの指示に従い、
FragmentF1.onFragmentButtonClickedListener.onFragmentButton1(int)を実装します。
eclipseのエラーメッセージから「実装されてないメソッドの追加」を選択します。
以下のメソッドが自動生成されたはずです。
@Override
public void onFragmentButton1(int button) {
// TODO 自動生成されたメソッド・スタブ
}
| |
ここで、フラグメントでボタンが押されたことがコールバックされます。
中身を次のように記述します。
「ChangeColor1」で、アクティビティからフラグメントに、情報を送り返しています。
@Override
public void onFragmentButton1(int button) {
// TODO 自動生成されたメソッド・スタブ
//フラグメントマネージャーで、フラグメントを取得
FragmentManager manager = getSupportFragmentManager();
FragmentF1 frag2 = (FragmentF1)manager.findFragmentByTag("fragment_tag1");
switch(button){
case 1:
//背景を青色にする
view1.setBackgroundColor(Color.argb(255, 0,0, 255));
//フラグメントのテキストを書き換える
frag2.ChangeColor1("青色になった");
break;
case 2:
//背景を緑色にする
view1.setBackgroundColor(Color.argb(255, 0, 255, 0));
//フラグメントのテキストを書き換える
frag2.ChangeColor1("緑色になった");
break;
}
}
| |
アクティビティからフラグメントに情報を送り返すために、
フラグメントマネージャーを使っています。
フラグメントマネージャーが、フラグメントを識別できるようにタグを付けます。
「ft.add(LAYOUT_ID1, frag1);」を、「ft.add(LAYOUT_ID1, frag1,"fragment_tag1");」に書き換えます。
//フラグメントにタグ付きで追加する
ft.add(LAYOUT_ID1, frag1,"fragment_tag1");
| |
以上で、完成です。
できあがり
フラグメントのボタンを押すと、アクティビティ画面の背景色が変わるようになりました。
画面を回転させると、アクティビティは初期化されて黄色の背景に戻ります。
ハマったポイント。
アクティビティからフラグメントに情報を渡すとき、最初、FragmentManagerを使わず、普通のsetterとして、
FragmentF1 frag1 = new FragmentF1();
frag1.ChangeColor1("青色になった");
| |
とか、記述してみましたが、いろいろと問題がありました。
まず、「frag1」をstaticにしないと落ちます。
さらに、画面回転させたときに、フラグメントが再生成されるようで、「ChangeColor1()」が利かなくなるようです。
ボタンを押しても、「青色になった」「緑色になった」というテキストメッセージの変更ができません。
「FragmentF1.java」の、TextViewを、staticにすると回避できましたが、あまりかっこよくありません。
フラグメントを破棄させない「setRetainInstance(true) 」というものを試してみました。
FragmentF1.javaのonCreateViewに追加します。
その場合、画面回転後でも、ボタンを押した時の「青色になった」「緑色になった」というメッセージの変更が有効になりました。
今回は、フラグメントをタグ付きで追加し、
タグを使って、アクセスすべきフラグメントを特定するという方法を使いました。
なんとなく最も正攻法な気がしましたので。
FragmentF1.java
package com.example.test10;
import android.support.v4.app.Fragment; //これをインポートすること
//import android.app.Fragment; //これをインポートするとエラーになる
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
public class FragmentF1 extends Fragment{
public FragmentF1() {}
private TextView text1;
private TextView text2;
private onFragmentButtonClickedListener listener;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//レイアウトを作る
LinearLayout layout1 = new LinearLayout(getActivity());
//レイアウトは縦に並ぶ
layout1.setOrientation(LinearLayout.VERTICAL);
//Bundleのデータ読み込み
String moji1 = getArguments().getString("data1");
//-----------------------------------------
//アイコン画像を読み込み
Bitmap img1 = BitmapFactory.decodeResource(
getResources(),R.drawable.ic_launcher);
//アイコン画像を貼り付ける画面
ImageView imgvw1 = new ImageView(getActivity());
//背景色
imgvw1.setBackgroundColor(Color.argb(255, 255, 0, 0));
//画面にアイコン画像をセット
imgvw1.setImageBitmap(img1);
//サイズをセット
LayoutParams para1 = new LayoutParams(200,200);
//画面のサイズをセット
imgvw1.setLayoutParams(para1);
//アイコンを描画した画面を並べる
layout1.addView(imgvw1);
//-----------------------------------------
//テキストを貼り付ける
text1 = new TextView(getActivity());
//テキストをセット
text1.setText(moji1);
//テキストを並べる
layout1.addView(text1);
//-----------------------------------------
//ボタンをつくる
Button button1 = new Button(getActivity());
//ボタンに文字を表示
button1.setText("Blue");
//ボタンをレイアウトに追加
layout1.addView(button1);
//-----------------------------------------
//ボタンをつくる
Button button2 = new Button(getActivity());
//ボタンに文字を表示
button2.setText("Green");
//ボタンをレイアウトに追加
layout1.addView(button2);
//-----------------------------------------
//テキストを貼り付ける
text2 = new TextView(getActivity());
//テキストをセット
text2.setText("ボタンが押された?");
//テキストを並べる
layout1.addView(text2);
//-----------------------------------------
//ボタンのリスナー
button1.setOnClickListener( new OnClickListener() {
//ボタンが押されたら何かする
@Override
public void onClick(View v) {
//テキストをセット
text1.setText("Blueが押された");
listener.onFragmentButton1(1);
}
});
//ボタンのリスナー
button2.setOnClickListener( new OnClickListener() {
//ボタンが押されたら何かする
@Override
public void onClick(View v) {
//テキストをセット
text1.setText("Greenが押された");
listener.onFragmentButton1(2);
}
});
//-----------------------------------------
return layout1;
}
public void ChangeColor1(String color){
//テキストをセット
text2.setText(color);
}
//ボタンの押されたことを通知
public interface onFragmentButtonClickedListener {
public void onFragmentButton1(int button);
}
//Activityに関連付けされた時に一度だけ呼ばれる
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
listener = (onFragmentButtonClickedListener)activity;
}
}
| |
MainActivity.java
package com.example.test10;
import com.example.test10.FragmentF1.onFragmentButtonClickedListener;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.os.Bundle;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
public class MainActivity extends FragmentActivity implements onFragmentButtonClickedListener {
private static final int LAYOUT_ID1 = 777;
private ImageView view1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//----------------------------------------
//親レイアウト1
LinearLayout L_layout1 = new LinearLayout(this);
setContentView(L_layout1);
//横に並ぶ
L_layout1.setOrientation(LinearLayout.HORIZONTAL);
//-----------------------------------------
//アイコン画像をビューに読み込む
Bitmap image1 = BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher);
view1 = new ImageView(this);
view1.setImageBitmap(image1);
//サイズ
LinearLayout.LayoutParams params1 =
new LinearLayout.LayoutParams(200, ViewGroup.LayoutParams.MATCH_PARENT);
view1.setLayoutParams(params1);
//背景を黄色にする
view1.setBackgroundColor(Color.argb(255, 255, 255, 0));
//アイコン画像のビューを親レイアウト1に追加
L_layout1.addView(view1);
//--------------------------------------------
//子レイアウト2
LinearLayout L_layout2 = new LinearLayout(this);
//子レイアウト2の背景を水色にする
L_layout2.setBackgroundColor(Color.argb(255, 0, 255, 255));
//サイズ
LinearLayout.LayoutParams layoutParams2 =
new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
//子レイアウト2を親レイアウト1に追加
L_layout1.addView(L_layout2,layoutParams2);
//--------------------------------------------
//子レイアウト2に、フラグメントを設定するためのID
L_layout2.setId(LAYOUT_ID1);
if ( savedInstanceState == null ) {
//アクティビティが生成された時に1度だけフラグメントに描画
Put_fragment1();
}
}
public void Put_fragment1(){
//FragmentManagerを設定
FragmentManager manager = getSupportFragmentManager();
//Transactionを設定
FragmentTransaction ft = manager.beginTransaction();
//----------------------------------
//フラグメントに貼るデータを作る
FragmentF1 frag1 = new FragmentF1();
Bundle bd1 = new Bundle();
bd1.putString("data1", "フラグメントデータ");
//フラグメントにバンドルを渡す
frag1.setArguments(bd1);
//----------------------------------
//フラグメントに追加する
//ft.add(LAYOUT_ID1, frag1);
//フラグメントにタグ付きで追加する
ft.add(LAYOUT_ID1, frag1,"fragment_tag1");
ft.commit();
}
@Override
public void onFragmentButton1(int button) {
// TODO 自動生成されたメソッド・スタブ
//フラグメントマネージャーで、フラグメントを取得
FragmentManager manager = getSupportFragmentManager();
FragmentF1 frag2 = (FragmentF1)manager.findFragmentByTag("fragment_tag1");
switch(button){
case 1:
//背景を青色にする
view1.setBackgroundColor(Color.argb(255, 0,0, 255));
//フラグメントのテキストを書き換える
frag2.ChangeColor1("青色になった");
break;
case 2:
//背景を緑色にする
view1.setBackgroundColor(Color.argb(255, 0, 255, 0));
//フラグメントのテキストを書き換える
frag2.ChangeColor1("緑色になった");
break;
}
}
}
| |