質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.21%

【Android Studio】あるFragmentから別のFragmentに値を渡す方法

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 3,363

y_itsuki

score 5

 前提・実現したいこと

Android Studioおよびjavaを扱うのは初めてで、
webで調べながら見よう見まねで作成しているような状況ですが、
あるFragmentから別のFragmentに値を渡す方法がわからず苦戦しております。
理解が足りていないことは重々承知で、お見苦しいかとは思いますが
ご教示いただけますと幸いです。

FragmentとViewPagerでTabLayoutを作っています。
各Tabの表示内容を別々のFragmentで表示します。

処理の流れは、
・Fragment2(=Tab2)に配置したspinnerで入力内容を選択し
・Fragment2のボタンを押すと、選択したspinnerの内容が
Fragment1(=Tab1)に配置したTableLayoutに書き込まれる。

Fragment2で入力、Fragment1で表示というイメージです。
※本来はfragment1でもう一段回処理して、最終的な履歴をFragment3(Tab3)に
書き込む予定なのですが、今回は割愛します。
フラグメントマネージャーにはFragmentが3つありますが気にしないでください。

 発生している問題・エラーメッセージ

自分で調べた範囲では、bundleを介して値を渡せるということは分かったのですが、
実際にやってみるとnullになってしまいます。

 該当のソースコード

MainActivity.java】

package com.example.MyApp;

import android.support.design.widget.TabLayout;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // スライド用の部品
        ViewPager viewPager = findViewById(R.id.viewpager);
        viewPager.setAdapter(new SampleFragmentPagerAdapter(getSupportFragmentManager(),
                MainActivity.this));
        // 上部にタブをセットする
        TabLayout tabLayout = findViewById(R.id.tabs);
        tabLayout.setupWithViewPager(viewPager);
    }
}
【SampleFragmentPagerAdapter.java】

package com.example.MyApp;

import android.content.Context;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;

public class SampleFragmentPagerAdapter extends FragmentPagerAdapter {
    final int PAGE_COUNT = 3;

    // Tabのタイトルテキスト。サイズやカラーはstyles.xmlで設定する。
    private String tabTitles[] = new String[]{"現在の状況", "予約フォーム"};

    private Context context;

    public SampleFragmentPagerAdapter(FragmentManager fm, Context context) {
        super(fm);
        this.context = context;
    }

    @Override
    public int getCount() {
        return PAGE_COUNT;
    }

    @Override
    public Fragment getItem(int position) {
        // fragmentを切り替える
        switch(position){
            case 0:
                return new PageFragment();
            case 1:
                return new PageFragment2();
            case 2:
                return new PageFragment3();
        }
        return null;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        // Generate title based on item position
        return tabTitles[position];
    }
【PageFragment2.java】

// 入力フォームを表示するTab2

package com.example.MyApp;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.Button;

//Fragmentクラスを継承します
public class PageFragment2 extends Fragment {

    // Fragmentで表示するViewを作成
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);
        // 画面回転時にFragmentの再生成を禁止
        setRetainInstance(true);

        // fragment_page2.xmlのレイアウトをViewとして作成します。
        // fragment_page2.xmlにはspinnerがいくつか並んでいて、
        // 最下部にボタンが配置されている。
        return inflater.inflate(R.layout.fragment_page2, container, false);
    }

    // Viewが生成し終わった時に呼ばれるメソッド
    @Override
    public void onViewCreated(View view, Bundle saveInstanceState){
        super.onViewCreated(view, saveInstanceState);

        // 名前選択用のspinner設定
        final Spinner spinnerName = view.findViewById(R.id.spinner_name);

        // spinnerの文字サイズなど変更できるように
        ArrayAdapter<String> adapterName = new ArrayAdapter<>(getContext(),
                R.layout.spinner_item,
                getResources().getStringArray(R.array.spinner_names));

        // ドロップダウンメニューにするため
         adapterName.setDropDownViewResource(R.layout.spinner_dropdown_item);
        // spinner に adapterをセット
        spinnerName.setAdapter(adapterName);

        // (中略。上記のようなspinnerを複数設定)


        //ボタンを押したときのリスナーを設定
        Button button01 = view.findViewById(R.id.button01);
        button01.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View view){

                // spinnerの選択内容をフラグメント1に渡すためにBundleにセット
                FragmentManager fragmentManager = getFragmentManager();
                FragmentTransaction fragmentTransaction = 
                    fragmentManager.beginTransaction();

                PageFragment fragment = new PageFragment();
                Bundle args = new Bundle();
                args.putString("name_spinner", (String) spinnerName.getSelectedItem());
                fragment.setArguments(args);
                fragmentTransaction.commit();

                // spinnerを初期化
                spinnerName.setSelection(0);

                // (中略。他のspinnerについても同様に設定)
            }
        });
    }
}
【PageFragment.java】

// 入力結果を表示するためのTab1

package com.example.MyApp;

import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.os.Bundle;

//Fragmentクラスを継承します
public class PageFragment extends Fragment {

    //Fragmentで表示するViewを作成
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);
        // 画面回転時にFragmentの再生成を禁止
        setRetainInstance(true);

        //fragment_page.xmlのレイアウトをViewとして作成します。
        // fragment_page.xmlにはTableLayoutとボタンが配置されています。
        return inflater.inflate(R.layout.fragment_page, container, false);
    }

    // Viewが生成し終わった時に呼ばれるメソッド

    @Override
    public void onViewCreated(View view, Bundle saveInstanceState) {
        super.onViewCreated(view, saveInstanceState);

        final TextView name = view.findViewById(R.id.reserve_2c);
        // R.id.reserve_2c は Tbaleのセルのidです。

        // ボタンを押したらBundleから値を受け取り、セルに書き込む。
        // ※ 本当は、PageFragment2.javaでボタンを押した時点でセルへの書き込みまで
        //    おこなうとか、ページがTab2からTab1に切り替わったことを検出して
        //    セルに書き込む、とかにしたかったが、よく理解できなかったので
        //    まずはボタンのOnClickListenerで動かしてみる。

        Button button = view.findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View view) {

                // nullチェック
                Bundle bundle = getArguments();
                if (bundle == null) {
                    name.setText("null");
                } else  {
                    // nullでなければspinnerで選択した内容をセルに書き込む
                    name.setText(bundle.getString("name_spinner"));
                }

            // (中略。その他のspinnerについても同様に値を受け取りセルに書き込みたい)

            }
        });
    }
}


【PageFragment.java】でBundleから値を受け取れたかどうかをnullチェックすると、
nullが返って来て上手く値を受け渡しできずに困っています。

 試したこと

Bundleにセットする時点で問題があるのかを確認するため、
【PageFragment2.java】にて、

final TextView testText = view.findViewById(R.id.test_text);
testText.setText(args.getString("name_spinner"));


を加えてみると、spinnerNameで選択した内容がTextViewで表示されました。

しかし、同じ【PageFragment2.java】でも、

final TextView testText = view.findViewById(R.id.test_text);
Bundle bundle = getArguments();
testText.setText(bundle.getString("name_spinner"));


とするとnullになります。

(これはbundleへのセットは行えているがゲットが上手く
できていないということでしょうか?)

 補足情報(FW/ツールのバージョンなど)

Android Studio 3.0

以上、よろしくお願いいたします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

0

FragmentTransactionのaddやreplace等のメソッドでフラグメントを配置しないと意味がありません。
また、この方法で実装した場合、画面に表示されているフラグメントインスタンスとは別のインスタンスにデータを渡すことになることは理解していますか?

データはFragment→Activity→Fragmentの流れで渡しましょう。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/02/23 00:47 編集

    ご回答くださり、ありがとうございます。
    addやreplaceを使うというのは、下記の様に理解したのですが、いかがでしょうか。

    ・メインのレイアウトでフラグメントを配置する場所だけ確保しておき、
     その場所のidをaddやreplaceに渡して対象のフラグメントを配置(置き換え)。
    ・配置されたフラグメント側でbundleから値を受け取って何らかの処理を実行。
    → この場合、今回私の作成したものはメインのレイアウトにはTabLayoutとViewPagerしか
     配置されておりません。
     FragmentPagerAdapterでポジションを検出し、対応するフラグメントを表示しているだけ
     ですので、そもそもaddやreplaceする場所がなく、この方法は向いていないのでは、
     と思い始めました。

    >また、この方法で実装した場合、画面に表示されているフラグメントインスタンスとは別の
    >インスタンスにデータを渡すことになることは理解していますか?
    →よくわかっていなかったのですが、今私がやっていることは、画面に存在していないインスタンスに
     値を渡そうとしている(もしくは新たに生成している?)にもかかわらず、そのインスタンスが
     もとから存在しているかのように扱おうとしているからnullになるということでしょうか?

    キャンセル

  • 2018/02/23 09:47

    > この方法は向いていないのでは、と思い始めました。

    その通りです。Fragmentにデータを渡す方法はたくさんありますが、それらは状況によって使い分ける必要があります。ネットで見つけた最初の方法に飛びついたらだめですよ。
    あなたの採用しようとした方法はFragmentを初回配置する場合に使用する方法です。

    また、インスタンスが違うというのはViewPagerAdapterが作ったフラグメントとあなたが作ったフラグメントは同じクラスから作り出した別のインスタンスです。
    そのため、下記の状況になっています。
    ・画面に配置されたフラグメントインスタンス
    ・新しく作った値を持つフラグメントインスタンス

    キャンセル

  • 2018/02/27 00:02

    お礼が遅くなり申し訳ありません。
    今回は方針が間違っていたということで、色々ヒントを下さりありがとうございます。
    まだ実現したいこと自体は解決していませんが、フラグメントの扱い方自体がよく分かって
    いなかったので、勉強しなおそうと思います。
    今回はご回答、ありがとうございました。

    キャンセル

0

間が空きましたが、自己解決しました。
備忘録 兼 同じことで悩んでいる人に少しでも参考になればと思い、
ここに書いておこうと思います。

処理内容は、
フラグメント2にあるspinner(ここではspinnerName)を選択したあと、ボタンを押すと
選択内容をフラグメント1のTableLayout内のセル(ここではreserve_2c)に書き込む
というものです。

PageFragment2.java】
//Fragmentクラスを継承します
public class PageFragment2 extends Fragment {

    // 中略 最初のコードから変更なし


    // Viewが生成し終わった時に呼ばれるメソッド
    @Override
    public void onViewCreated(View view, Bundle saveInstanceState){
        super.onViewCreated(view, saveInstanceState);

       // 中略 spinnerの処理部分は最初のコードから変更なし

    final TextView reserve_2c_text = getActivity().findViewById(R.id.reserve_2c);
       // R.id.reserve_2cはフラグメント1内の書き込みたいセルのid

    Button button01 = view.findViewById(R.id.button01);

        // button01を押したとき
        button01.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View view){

            // ここでフラグメント1にあるテーブル内のセルに値を書き込んでいる
            reserve_2c_text.setText((String) spinnerName.getSelectedItem());

            }
        });
    }
}


ポイントは、getActivity()でフラグメントが属するアクティビティを取得している点です。
フラグメントは必ずアクティビティに属する形で使用されます。
今回のケースでは、フラグメント1とフラグメント2はどちらも同じアクティビティに属しています。
getActivity()メソッドで両フラグメントが所属するアクティビティを経由することで、
フラグメント2から直接フラグメント1内のidを参照することができるようになります。

※ちなみに
インターネットで「フラグメント 値 受け渡し」と検索すると出て来る情報の
多くが、Bundleにセットした値を受け取るというというものでしたが、
これはyona様のコメントにもある通り、フラグメントを初回配置する場合に使用する方法で、
今回私がやりたかったような既に生成した後のフラグメントに対して値を書き込む場合には
向いていないようです。個人的にはそれを理解するまでに時間がかかってしまいました。。。

以上です。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.21%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる