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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Android Studio

Android Studioは、 Google社によって開発された、 Androidのネイティブアプリケーション開発に特化した統合開発ツールです。

Q&A

1回答

1315閲覧

ダイアログの上にダイアログを表示し、リスナーを使用して値をフラグメントに送りたい

aruko

総合スコア47

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Android Studio

Android Studioは、 Google社によって開発された、 Androidのネイティブアプリケーション開発に特化した統合開発ツールです。

0グッド

0クリップ

投稿2022/12/27 09:03

前提

ダイアログの上にダイアログを表示し、リスナーを使用して値をフラグメントに送りたいです。
今までViewModelを使って行っていましたが、リスナーを使う方法に変えたいです。

実現したいこと

①フラグメント表示
②フラグメント上のボタンクリックでダイアログ_0表示(1つ目のダイアログ)
③ダイアログ0上のボタンクリックでダイアログ_1表示(2つ目のダイアログ)
④ダイアログ1上のボタンクリックで値をフラグメントに送る

③までできています。④を行うと、エラーになり、画面が消えます。

エラーメッセージ

java.lang.NullPointerException: Attempt to invoke interface method 'void tmaruko.okura.test1.SampleDialogFragment$SampleDialogListener.send_receive2(androidx.fragment.app.DialogFragment)' on a null object reference

MainActivity.java

java

1public class MainActivity extends AppCompatActivity { 2 3 @Override 4 protected void onCreate(Bundle savedInstanceState) { 5 super.onCreate(savedInstanceState); 6 setContentView(R.layout.activity_main); 7 8 // Activityにフラグメントをセット 9 getSupportFragmentManager() 10 .beginTransaction() 11 .add(R.id.fragment_sample, new SampleFragment()) 12 .commit(); 13 } 14}

SampleFragment.java

java

1// インターフェースを実装するためにimplementsする 2public class SampleFragment extends Fragment implements SampleDialogFragment.SampleDialogListener { 3 4 private Button button; 5 6 @Override 7 public View onCreateView(LayoutInflater inflater, ViewGroup container, 8 Bundle savedInstanceState) { 9 View view = inflater.inflate(R.layout.fragment_sample, container, false); 10 11 // アクションバーにタイトルをセット 12 MainActivity activity = (MainActivity) getActivity(); 13 activity.setTitle("ダイアログコールバックサンプル"); 14 15 // ボタン要素をレイアウトから取得 16 button = view.findViewById(R.id.bt_show_dialog); 17 button.setText("ダイアログ表示する"); 18 19 // ボタンクリックされたときの処理 20 button.setOnClickListener(new View.OnClickListener() { 21 @Override 22 public void onClick(View view) { 23 // フラグメントマネージャーを取得 24 FragmentManager fragmentManager = getActivity().getSupportFragmentManager(); 25 // SampleDialogFragmentのインスタンスオブジェクトを生成 26 SampleDialogFragment dialogFragment = SampleDialogFragment.newInstance(); 27 // SampleDialogFragmentのオブジェクトに呼び出し元のSampleFragmentオブジェクトをセット 28 dialogFragment.setTargetFragment(SampleFragment.this, 0); 29 // ダイアログを表示 30 dialogFragment.show(fragmentManager, ""); 31 } 32 }); 33 return view; 34 } 35 36 // コールバックされて実行される処理 37 @Override 38 public void send_receive(DialogFragment dialog) { 39 // ボタンに表示されている文字を変更 40 button.setText("dialog_0からきたよ"); 41 } 42 43 @Override 44 public void send_receive2(DialogFragment dialog) { 45 button.setText("dialog_1からきたよ"); 46 } 47} 48

SampleDialogFragment.java

java

1public class SampleDialogFragment extends DialogFragment { 2 static int no; 3 4 // インスタンスを生成するメソッド 5 public static SampleDialogFragment newInstance() { 6 return new SampleDialogFragment(); 7 } 8 9 // イベントのコールバックを受け取るためのインターフェースを実装 10 public interface SampleDialogListener { 11 void send_receive(DialogFragment dialog); 12 void send_receive2(DialogFragment dialog); 13 } 14 15 // クリックイベント発火を伝えるために使用するインターフェースインスタンスを定義 16 private SampleDialogListener listener; 17 18 // onAttach()で呼び出し元の親フラグメントがインターフェースを実装しているかを検証 19 // onAttach(): フラグメントのライフサイクルで最初に呼ばれるメソッドであり、 20 // フラグメントがアクティビティと関連づけられたときに一度だけ呼び出される。contextには所属親アクティビティが入っている・・・② 21 @Override 22 public void onAttach(Context context) { 23 super.onAttach(context); 24 try { 25 // 親フラグメントにイベントを送信できるように呼び出し元であるSampleFragmentオブジェクトを取得し、 26 // listenerのインスタンスを生成する 27 listener = (SampleDialogListener) getTargetFragment(); 28 } catch (ClassCastException e) { 29 // 親フラグメントがインターフェースを実装していない場合は例外を投げる 30 throw new ClassCastException(getTargetFragment().toString() + "はインターフェースを実装していません"); 31 } 32 } 33 @Override 34 public Dialog onCreateDialog(Bundle savedInstanceState) { 35 AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); 36 switch (no) { 37 case 0: 38 //XMLとの紐付け 39 LayoutInflater inflater = getActivity().getLayoutInflater(); 40 View view = inflater.inflate(R.layout.dialogfragment_sample, null, false); 41 //ダイアログの作成 42 builder.setView(view); 43 //ボタンのリスナー 44 Button setumei_bt_tojiru = view.findViewById(R.id.setumei_bt_tojiru); 45 setumei_bt_tojiru.setOnClickListener(new View.OnClickListener() { 46 @Override 47 public void onClick(View view) { 48 // 処理を親のフラグメントにコールバックする 49 listener.send_receive(SampleDialogFragment.this); 50 dismiss(); 51 } 52 }); 53 Button setumei_bt_aitemu = view.findViewById(R.id.setumei_bt_aitemu); 54 setumei_bt_aitemu.setOnClickListener(new View.OnClickListener() { 55 @Override 56 public void onClick(View v) { 57 DialogFragment dialog = new SampleDialogFragment(); 58 no = 1;//アイテムダイアログに進む 59 dialog.show(getParentFragmentManager(), ""); 60 } 61 }); 62 break; 63 case 1:inflater=LayoutInflater.from(getActivity());//アイテム使用するかダイアログ 64 view=inflater.inflate(R.layout.dialogfragment_sample2,null); 65 //ダイアログの作成 66 builder.setView(view); 67 builder.setPositiveButton("つかう", new DialogInterface.OnClickListener() { 68 @Override 69 public void onClick(DialogInterface dialogInterface, int i) { 70 // 処理を親のフラグメントにコールバックする 71 listener.send_receive2(SampleDialogFragment.this);★ここがエラーになる 72 dismiss(); 73 } 74 }); 75 break; 76 } 77 AlertDialog dialog = builder.create(); 78 dialog.setCanceledOnTouchOutside(false); 79 //ダイアログ以外の画面をタップしてもダイアログは消えない 80 return dialog; 81 } 82}

試したこと

②(1つ目のダイアログ)でコールバックを行うとできます。
ということは、ダイアログを2つ重ねることで、フラグメントとのつながりが切れてしまうのではないかと思います。
教えていただきたいです。よろしくお願いします。

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ダイアログを2つ重ねることで、フラグメントとのつながりが切れてしまうのではないか

SampleDialogFragment クラスの使い方が、1つ目を出す時と2つ目を出す時で違うのが原因と思います。
発生している例外は、「 listener が null だ」というものです。1つ目を出す際に行っていることと同じことを2つ目を出す際にもしなければならないのではないでしょうか。


以下は FragmentResultListener/FragmentResult によるものです。
DialogFragment は 2 つの異なるレイアウトを 1 つのクラスで扱う意味がありませんので 2 つに分けています。(適切な名前が分かりませんので 0/1 としています)
構造的には Dialog0 から直に Dialog1 を表示するのでは無く、 Dialog0 のどのボタンでも一旦 SimpleFragment に戻って必要なら Dialog1 を表示するようにしています。

SampleFragment.java

java

1import android.os.Bundle; 2import android.view.*; 3import android.widget.Button; 4 5import androidx.annotation.*; 6import androidx.fragment.app.*; 7 8public class SampleFragment extends Fragment { 9 private static final String DIALOG_REQUEST_KEY = "dialog_request_key"; 10 11 SampleFragment() { 12 super(R.layout.fragment_sample); 13 } 14 15 @Override 16 public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 17 MainActivity activity = (MainActivity)requireActivity(); 18 activity.setTitle("ダイアログコールバックサンプル"); 19 20 FragmentManager fragmentManager = getChildFragmentManager(); 21 22 Button button = view.findViewById(R.id.bt_show_dialog); 23 button.setText("ダイアログ表示する"); 24 button.setOnClickListener(v -> SampleDialog0Fragment.newInstance(DIALOG_REQUEST_KEY).show(fragmentManager, null)); 25 26 fragmentManager.setFragmentResultListener(DIALOG_REQUEST_KEY, getViewLifecycleOwner(), (requestKey, result) -> { 27 if(result.getBoolean("showDialog1", false)) { 28 SampleDialog1Fragment.newInstance(DIALOG_REQUEST_KEY).show(fragmentManager, null); 29 return; 30 } 31 String text = result.getString("text"); 32 button.setText(text); 33 }); 34 } 35}

SampleDialog0Fragment.java

java

1import android.app.*; 2import android.os.Bundle; 3import android.view.*; 4import android.widget.Button; 5 6import androidx.fragment.app.DialogFragment; 7 8public class SampleDialog0Fragment extends DialogFragment { 9 private static final String ARGS_REQUEST_KEY = "args_request_key"; 10 11 public static SampleDialog0Fragment newInstance(String requestKey) { 12 SampleDialog0Fragment fragment = new SampleDialog0Fragment(); 13 Bundle args = new Bundle(); 14 args.putString(ARGS_REQUEST_KEY, requestKey); 15 fragment.setArguments(args); 16 return fragment; 17 } 18 19 @Override 20 public Dialog onCreateDialog(Bundle savedInstanceState) { 21 String requestKey = getArguments().getString(ARGS_REQUEST_KEY); 22 23 View view = LayoutInflater.from(getContext()).inflate(R.layout.dialogfragment_sample, null, false); 24 25 Button closeButton = view.findViewById(R.id.setumei_bt_tojiru); 26 closeButton.setOnClickListener(v -> { 27 Bundle result = new Bundle(); 28 result.putString("text", "dialog_0からきたよ"); 29 getParentFragmentManager().setFragmentResult(requestKey, result); 30 dismiss(); 31 }); 32 33 Button itemButton = view.findViewById(R.id.setumei_bt_aitemu); 34 itemButton.setOnClickListener(v -> { 35 Bundle result = new Bundle(); 36 result.putBoolean("showDialog1", true); 37 getParentFragmentManager().setFragmentResult(requestKey, result); 38 dismiss(); 39 }); 40 41 AlertDialog dialog = new AlertDialog.Builder(getContext()) 42 .setView(view) 43 .create(); 44 dialog.setCanceledOnTouchOutside(false); //ダイアログ以外の画面をタップしてもダイアログは消えない 45 return dialog; 46 } 47}

SampleDialog1Fragment.java

java

1import android.app.*; 2import android.os.Bundle; 3import android.view.*; 4 5import androidx.fragment.app.DialogFragment; 6 7public class SampleDialog1Fragment extends DialogFragment { 8 private static final String ARGS_REQUEST_KEY = "args_request_key"; 9 10 public static SampleDialog1Fragment newInstance(String requestKey) { 11 SampleDialog1Fragment fragment = new SampleDialog1Fragment(); 12 Bundle args = new Bundle(); 13 args.putString(ARGS_REQUEST_KEY, requestKey); 14 fragment.setArguments(args); 15 return fragment; 16 } 17 18 @Override 19 public Dialog onCreateDialog(Bundle savedInstanceState) { 20 String requestKey = getArguments().getString(ARGS_REQUEST_KEY); 21 22 View view = LayoutInflater.from(getContext()).inflate(R.layout.dialogfragment_sample2, null, false); 23 24 AlertDialog dialog = new AlertDialog.Builder(getContext()) 25 .setView(view) 26 .setPositiveButton("つかう", (d, i) -> { 27 Bundle result = new Bundle(); 28 result.putString("text", "dialog_1からきたよ"); 29 getParentFragmentManager().setFragmentResult(requestKey, result); 30 }) 31 .create(); 32 dialog.setCanceledOnTouchOutside(false); //ダイアログ以外の画面をタップしてもダイアログは消えない 33 return dialog; 34 } 35}

投稿2022/12/27 09:46

編集2023/01/02 09:37
jimbe

総合スコア12648

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

aruko

2022/12/27 23:37

jimbeさん、ありがとうございます。 // フラグメントマネージャーを取得 FragmentManager fragmentManager = getActivity().getSupportFragmentManager(); // SampleDialogFragmentのインスタンスオブジェクトを生成 SampleDialogFragment dialogFragment = SampleDialogFragment.newInstance(); // SampleDialogFragmentのオブジェクトに呼び出し元のSampleFragmentオブジェクトをセット dialogFragment.setTargetFragment(SampleFragment.this, 0); // ダイアログを表示 dialogFragment.show(fragmentManager, ""); のことだと思い、2つ目のダイアログを作る場所に入れたり、2つ目のダイアログのボタンクリックのところに入れたりしてみましたが、実行するとエラーになってしまいます。 詳しく教えていただけるとありがたいです。
jimbe

2022/12/28 07:01 編集

まず、例外の原因をはっきりさせることが必要と思います。 回答しました通りかどうか、つまり例外が発生した行で listener が null かどうかは確認されましたでしょうか。 そこで null が確認されれば、なぜ listener が null なのか、 listener への設定は onAttach メソッドでしかやっていないのですから、 onAttach が実行されているかどうかや代入元が null を返していないかを調べる等、いきなり直そうと推測だけでアレコレ弄るのでは無く、刑事が犯人の行動を追っていくように、一つ々々順に証拠を得ながら辿ってみてください。 なお、 setTargetFragment(https://developer.android.com/reference/androidx/fragment/app/Fragment#setTargetFragment(androidx.fragment.app.Fragment,int)) は既に非推奨になっていますので、 setFragmentResultListener(https://developer.android.com/reference/androidx/fragment/app/FragmentManager#setFragmentResultListener(java.lang.String,androidx.lifecycle.LifecycleOwner,androidx.fragment.app.FragmentResultListener)) 等の利用に変更することもお考えになっては如何でしょうか。
aruko

2023/01/02 10:43

jimbeさん、返事が遅くなりすみません。setFragmentResultはコード自体とその意味は簡単でしたが、androidstudioをアップデートさせたことでエラーになったため、アンインストール再インストール+日本語化をしていてもたつきました。buildgradleの問題もあるようでなかなか進みませんでしたが、あれこれ試しているうちにsetFragmentResultListenerで値を送れるようになり、ダイアログから直接フラグメントに送るものはsetFragmentResultに変えつつあるところです。先ほど新たに提示していただいて申し訳ありません。まだ内容を細かく見ていませんが、取りあえずお礼です。ありがとうございます!
jimbe

2023/01/02 11:02

ちょっと時間がありましたので、コードを付けるにもどうするかととりあえず FragmentResult で書いてから TargetFragment にしてみようとしたものです。 FragmentResult に修正されているのでしたら TargetFragment 版はもう必要そうではないですかね^^; DialogFragment から DialogFragment を開こうとすると、 FragmentResultListener にコールバックを登録する際の ViewLifeCycleOwner を得るタイミングが難しいです。出来無くは無いですが、見た目どうしてそうなのかが分かり難くなってしまいます。 ですので、ダイアログ間の関係に寄りますが、サンプルコードでは 1 つ目から戻って 2 つ目という形にしました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問