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

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

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

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

Android Studio

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

Q&A

解決済

1回答

3238閲覧

Android Studio:RecyclerViewの更新(notify)について

sato_shoma

総合スコア12

Java

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

Android Studio

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

0グッド

0クリップ

投稿2022/03/09 06:00

編集2022/03/21 05:53

前提・実現したいこと

Android StudioでJavaを使ってメモ帳アプリを作っています。メモのデータをSQLiteで保存し、それをRecyclerViewを使って表示する仕様です。メモデータを追加したときにRecyclerViewを更新する必要があるのですが、そこでエラーが出てしまいます。

具体的に言うと、ダイアログの保存ボタンを押したときの処理(.setPositiveButton/onCreateDialog/CreatesNotesDialogFragment.javaの第二引数内)でMainActivity.javaのnotifyAboutrecyclerViewメソッドを呼び出し、そのメソッド中でnotifyItemInsertedを使って更新処理をしています。notifiItemInsertedメソッドを実行しているadapterがnullになっているのがエラーの原因だと思うのですが、MainActivityのonCreate内でadapterインスタンスの生成を行っているのでなぜnullなのか良く分かりません。もしわかる方がいらっしゃれば、ご教授いただければ幸いです。

エラーメッセージ(コメントに該当箇所を記載しておきます)

E/AndroidRuntime: FATAL EXCEPTION: main Process: jp.halo_sf.haloint, PID: 18663 java.lang.NullPointerException: Attempt to invoke virtual method 'void jp.halo_sf.haloint.MainActivity$RecyclerViewAdapter.notifyItemInserted(int)' on a null object reference at jp.halo_sf.haloint.MainActivity.notifyAboutRecyclerView(MainActivity.java:61) //public class MainActivity extends AppCompatActivity { public void notifyAboutRecyclerView(){ adapter.notifyItemInserted(listSize); } } のところです。 at jp.halo_sf.haloint.CreatesNotesDialogFragment.lambda$onCreateDialog$0$jp-halo_sf-haloint-CreatesNotesDialogFragment(CreatesNotesDialogFragment.java:28) //public class CreatesNotesDialogFragment extends DialogFragment {    public Dialog onCreateDialog(Bundle savedInstance) { builder.setPositiveButton(R.string.btn_keep, (dialogInterface, i) -> { new MainActivity().notifyAboutRecyclerView(); }) } } のところです。 at jp.halo_sf.haloint.CreatesNotesDialogFragment$$ExternalSyntheticLambda0.onClick(Unknown Source:4) //CreateNotesDialogFragment の import android.app.Dialog; のところです。 at com.android.internal.app.AlertController$ButtonHandler.handleMessage(AlertController.java:174) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:223) at android.app.ActivityThread.main(ActivityThread.java:8240) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:979)

該当のソースコード

MainActivity.java

1import android.database.Cursor; 2import android.os.Bundle; 3import android.view.LayoutInflater; 4import android.view.View; 5import android.view.ViewGroup; 6import android.widget.TextView; 7 8import androidx.annotation.NonNull; 9import androidx.appcompat.app.AppCompatActivity; 10import androidx.recyclerview.widget.LinearLayoutManager; 11import androidx.recyclerview.widget.RecyclerView; 12 13import com.google.android.material.floatingactionbutton.FloatingActionButton; 14 15import java.util.ArrayList; 16 17public class MainActivity extends AppCompatActivity { 18 private DatabaseHelper _helper; 19 private RecyclerViewAdapter adapter; 20 private int listSize; 21 22 @Override 23 protected void onCreate(Bundle savedInstanceState) { 24 super.onCreate(savedInstanceState); 25 setContentView(R.layout.activity_main); 26 27 //DatabaseHelperの生成処理。 28 _helper = new DatabaseHelper(MainActivity.this); 29 30 //RecyclerViewの設定。 31 RecyclerView rvNote = findViewById(R.id.rvNote); 32 rvNote.setLayoutManager(new LinearLayoutManager(MainActivity.this)); 33 ArrayList<String> title = new ArrayList<>(); 34 ArrayList<String> text = new ArrayList<>(); 35 Cursor cursor = _helper.readData(); 36 while (cursor.moveToNext()) { 37 title.add(cursor.getString(1)); 38 text.add(cursor.getString(2)); 39 } 40 adapter = new RecyclerViewAdapter(title, text); 41 rvNote.setAdapter(adapter); 42 43 //fabAddを押したときの処理。 44 FloatingActionButton fabAdd = findViewById(R.id.fabAdd); 45 fabAdd.setOnClickListener(view -> { 46 CreatesNotesDialogFragment fragment = new CreatesNotesDialogFragment(); 47 fragment.show(getSupportFragmentManager(),"CreatesNotesDialogFragment"); 48 }); 49 } 50 51 @Override 52 protected void onDestroy() { 53 _helper.close(); 54 super.onDestroy(); 55 } 56 57//エラー文に記載されている箇所。 58 public void notifyAboutRecyclerView(){ 59 adapter.notifyItemInserted(listSize); 60 } 61 62 //RecyclerViewのアダプタ。 63 public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.RecyclerListViewHolder> { 64 private final ArrayList<String> title; 65 private final ArrayList<String> text; 66 67 public RecyclerViewAdapter(ArrayList<String> title, ArrayList<String> text) { 68 this.title = title; 69 this.text = text; 70 } 71 72 @NonNull 73 @Override 74 public RecyclerListViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 75 LayoutInflater inflater = LayoutInflater.from(MainActivity.this); 76 View view = inflater.inflate(R.layout.note_list_layout, parent, false); 77 return new RecyclerListViewHolder(view); 78 } 79 80 @Override 81 public void onBindViewHolder(RecyclerListViewHolder holder, int position) { 82 holder._tvNoteTitle.setText(String.valueOf(title.get(position))); 83 holder._tvNoteText.setText(String.valueOf(text.get(position))); 84 } 85 86 @Override 87 public int getItemCount() { 88 listSize = text.size(); 89 return listSize; 90 } 91 92 public class RecyclerListViewHolder extends RecyclerView.ViewHolder { 93 public TextView _tvNoteTitle; 94 public TextView _tvNoteText; 95 96 public RecyclerListViewHolder(View itemView) { 97 super(itemView); 98 _tvNoteTitle = itemView.findViewById(R.id.tvNoteTitle); 99 _tvNoteText = itemView.findViewById(R.id.tvNoteText); 100 } 101 } 102 } 103}

CreatesNotesDialogFragment.java

1import android.app.AlertDialog; 2//エラー文に記載されている箇所。 3import android.app.Dialog; 4import android.os.Bundle; 5import android.view.LayoutInflater; 6import android.view.View; 7import android.widget.EditText; 8 9import androidx.annotation.NonNull; 10import androidx.fragment.app.DialogFragment; 11 12public class CreatesNotesDialogFragment extends DialogFragment { 13 14 @NonNull 15 @Override 16 public Dialog onCreateDialog(Bundle savedInstance) { 17 AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); 18 LayoutInflater inflater = requireActivity().getLayoutInflater(); 19 View customView = inflater.inflate(R.layout.dialog_layout, null); 20 builder.setView(customView) 21 .setPositiveButton(R.string.btn_keep, (dialogInterface, i) -> { 22 EditText etNoteTitle = customView.findViewById(R.id.etSetNoteTitle); 23 String noteTitle = etNoteTitle.getText().toString(); 24 EditText etNoteText = customView.findViewById(R.id.etSetNoteText); 25 String noteText = etNoteText.getText().toString(); 26 new DatabaseHelper(getContext()).addData(noteTitle, noteText); 27//エラー文に記載されている箇所。 28 new MainActivity().notifyAboutRecyclerView(); 29 }) 30 .setNegativeButton(R.string.btn_cxl, (dialogInterface, i) -> { 31 }); 32 return builder.create(); 33 } 34}

試したこと

原因が全く分からないため特にありません。インターネットでいろいろ調べてみましたが思うような答えが得られなかったので質問しました。

補足情報

使用ツールの環境
Android Studio Bumblebee | 2021.1.1 Patch 2
Build #AI-211.7628.21.2111.8193401, built on February 17, 2022
ランタイム・バージョン: 11.0.11+9-b60-7590822 amd64
VM: OpenJDK 64-Bit Server VM by Oracle Corporation
Windows 10 10.0
GC: G1 Young Generation, G1 Old Generation
Memory: 1280M
Cores: 8
Registry: external.system.auto.import.disabled=true
Non-Bundled Plugins: dev.polek.adbwifi (1.2.4), org.jetbrains.kotlin (211-1.6.10-release-923-AS7442.40)

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

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

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

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

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

guest

回答1

0

ベストアンサー

例外に関係無く、そもそも
new MainActivity().notifyAboutRecyclerView();
というのは意味がありません。
オブジェクトはただ作れば良いものではありません。クラスは同じでもそこから作ったオブジェクトは別々の存在であり、例えば車の設計図は同じでもそれを元に作った複数の車は別々の存在であることと同じです。
「ダイアログを表示したアクティビティのメソッドを実行する」というのであれば、「ダイアログを表示したアクティビティ」を得て、メソッドを呼ぶ必要があります。
そしてそれは既にコード内で行っています。

※データベース関係は省いています。
※クラス名 (Adapter 関係)やメソッド名・役割(ダイアログからのコールバック)を少し変えています

MainActivity.java

java

1import android.os.Bundle; 2import android.view.*; 3import android.widget.*; 4 5import androidx.annotation.NonNull; 6import androidx.appcompat.app.AppCompatActivity; 7import androidx.recyclerview.widget.*; 8 9import java.util.*; 10 11public class MainActivity extends AppCompatActivity { 12 private NoteListAdapter adapter; 13 14 @Override 15 protected void onCreate(Bundle savedInstanceState) { 16 super.onCreate(savedInstanceState); 17 setContentView(R.layout.activity_main); 18 19 //RecyclerViewの設定。 20 RecyclerView rvNote = findViewById(R.id.rvNote); 21 rvNote.setLayoutManager(new LinearLayoutManager(MainActivity.this)); 22 adapter = new NoteListAdapter(); 23 rvNote.setAdapter(adapter); 24 25 //データベースからデータを読み込みアダプタにセット 26 //(省略) 27 //仮に直接追加 28 adapter.addNote(new Note("111", "AAA")); 29 adapter.addNote(new Note("222", "BBB")); 30 31 //fabAddを押したときの処理。 32 Button fabAdd = findViewById(R.id.fabAdd); 33 fabAdd.setOnClickListener(view -> { 34 CreatesNotesDialogFragment fragment = new CreatesNotesDialogFragment(); 35 fragment.show(getSupportFragmentManager(),"CreatesNotesDialogFragment"); 36 }); 37 } 38 39 public void addNote(Note note){ 40 //データベースに note の内容を追加 41 //_helper.addData(note.title, note.text) ? 42 43 //アダプタにも追加(notify は addNode 内で行われるのでココではそれは意識しない) 44 adapter.addNote(note); 45 } 46 47 //RecyclerViewのアダプタ 48 public class NoteListAdapter extends RecyclerView.Adapter<NoteListAdapter.NoteViewHolder> { 49 private final List<Note> noteList = new ArrayList<>(); 50 51 void addNote(Note note) { 52 int position = noteList.size(); 53 noteList.add(note); 54 notifyItemInserted(position); 55 } 56 57 @NonNull 58 @Override 59 public NoteViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 60 View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.note_list_layout, parent, false); 61 return new NoteViewHolder(view); 62 } 63 64 @Override 65 public void onBindViewHolder(NoteViewHolder holder, int position) { 66 Note note = noteList.get(position); 67 holder.tvTitle.setText(note.title); 68 holder.tvText.setText(note.text); 69 } 70 71 @Override 72 public int getItemCount() { 73 return noteList.size(); 74 } 75 76 public class NoteViewHolder extends RecyclerView.ViewHolder { 77 final TextView tvTitle; 78 final TextView tvText; 79 80 public NoteViewHolder(View itemView) { 81 super(itemView); 82 tvTitle = itemView.findViewById(R.id.tvNoteTitle); 83 tvText = itemView.findViewById(R.id.tvNoteText); 84 } 85 } 86 } 87}

CreatesNotesDialogFragment.java

java

1import android.app.AlertDialog; 2import android.app.Dialog; 3import android.os.Bundle; 4import android.view.LayoutInflater; 5import android.view.View; 6import android.widget.EditText; 7 8import androidx.annotation.NonNull; 9import androidx.fragment.app.DialogFragment; 10 11public class CreatesNotesDialogFragment extends DialogFragment { 12 @NonNull 13 @Override 14 public Dialog onCreateDialog(Bundle savedInstance) { 15 View customView = LayoutInflater.from(getContext()).inflate(R.layout.dialog_layout, null); 16 return new AlertDialog.Builder(getContext()) 17 .setView(customView) 18 .setTitle("ノート追加") 19 .setPositiveButton("add", (dialogInterface, i) -> { 20 EditText etNoteTitle = customView.findViewById(R.id.etSetNoteTitle); 21 String noteTitle = etNoteTitle.getText().toString(); 22 EditText etNoteText = customView.findViewById(R.id.etSetNoteText); 23 String noteText = etNoteText.getText().toString(); 24 //ダイアログはあくまでデータを入力することだけを行う. 25 //(データベースを気にする必要は無くなる.) 26 ((MainActivity)requireActivity()).addNote(new Note(noteTitle, noteText)); 27 }) 28 .setNegativeButton("cancel", (dialogInterface, i) -> { 29 }) 30 .create(); 31 } 32}

Note.java

java

1public class Note { 2 final String title; 3 final String text; 4 5 Note(String title, String text) { 6 this.title = title; 7 this.text = text; 8 } 9}

投稿2022/03/09 07:08

編集2022/03/09 11:50
jimbe

総合スコア12648

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

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

sato_shoma

2022/03/09 07:27 編集

回答していただき、ありがとうございます。追加の質問で恐縮ですが、すでにコード内で行っているとおっしゃっているのはどこの部分のことでしょうか?また、どのようにすればnotifyAdoutRecyclerviewメソッドを正常に実行できるでしょうか?お手数わずらわせますが、もし良ければどちらかだけでも良いので教えていただけませんか?見当違いな質問でしたらすみません。
jimbe

2022/03/09 07:43

コードを作っていましたら入れ違いになってしまいました。 getActivity()/requireActivity() のことです。
jimbe

2022/03/09 07:58 編集

蛇足になるかもしれませんが、 > notifyItemInsertedを使って更新処理をしています ということですが、データベースにデータは追加しているものの RecyclerViewAdapter が保持するリストは更新されていませんので、 notifyItemInserted を行っても表示は変わらないのではないでしょうか。
sato_shoma

2022/03/09 08:06 編集

教えてくださった通りにコードを変えたらエラーが出なくなりました。本当にありがとうございます。しかし、依然としてRestViewの更新はできないままです(更新はできているのかもしれませんが表示される画面に部品が追加されません)。もしかしたら画面の再表示など別の作業も行う必要があるのですかね?よろしければその辺のことも教えてくださるとありがたいです。
sato_shoma

2022/03/09 09:55 編集

https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q1480204279 このサイトで(adapter).add(data1);のように追加コマンドを書き~と書かれているのですが、これはRecyclerView.Adapterでaddメソッドを作って何か処理を行わないといけないということでしょうか?だとすると、どんな処理を記述すればよいのでしょう?これはListViewの話なのでもしかしたら関係ないかも知れませんが。 追記 思い当たる節があったので追記します。onCreate内ではRecyclerView.Adapteをnewする際に引数としてSQLiteのデータをArrayListに変換したものを渡していますが、今回の更新作業ではそのデータがアダプターに渡っていないということはないでしょうか?
sato_shoma

2022/03/09 08:27 編集

すみません、新しいコメントを読まずにコメントしてました。 >RecyclerViewAdapter が保持するリストは更新されていませんので これはどうすれば更新できるのでしょうか?notifyItemInsertedはRecyclerView.Adapterのメソッドなのでこれで更新されるものかと思ってました。本当に何も知らなくてすみません。
jimbe

2022/03/09 09:48

RecyclerView と Adapter の関係を整理してご理解頂く必要があるでしょう。 RecyclerView はデータを表示します。Adapter はデータを保持します。 RecyclerView は自身が「(再)表示する必要がある」と判断した場合(最初に画面を表示する時や、他のアプリによって裏に回された状態から再び表に戻った時)に Adapter の getItemCount, onCreateViewHolder や onBindViewHolder 等を呼び出して View を取得して表示します。 一方 Adapter のデータが更新された場合は Adapter から RecyclerView へイベントを通知して、先のように再表示を行ってもらいます。 そのイベント通知をするメソッドの一つが notifyItemInserted です。notifyItemInserted はイベントを通知するだけで、データ自体がどうであるかは無関係です。 ですので、アダプタの管理する(=getItemCount などが返す)データが変わっていなければいくらイベントを通知しても表示は変わりませんし、データが変わったとしてもイベントを通知しなければ画面は変わりません。 ご提示のコードでは RecyclerViewAdapter のフィールド title と text がデータの実体を管理しています。(余談ですが、 title と text の組でクラスを作った方が分かり易いでしょう。) ダイアログからデータベースを更新したら、同時にその title と text にも同じデータを追加した上でイベントを発行すれば、画面表示が更新されることになります。 ではどうやって title と text にデータを追加するかというと、その方法の一つが「(adapter).add(data1);のように追加コマンドを書き~」という Adapter にセッターを作って追加する方法です。(ListView も View と Adapter の関係性は RecyclerView と同じです。) 他に、 Adapter を作り直して RecyclerView に再度設定するという方法もあります。(RecyclerView は アダプタが新しくなると再表示を行います。) MainActivity の onCreate でデータベースから一連のデータを取得して Adapter を作り RecyclerView に setAdapter していますが、同じことを notifyAboutRecyclerView メソッド内で行うということです。(また蛇足ですが、 Cursor は使用後は閉じたほうが良いと思います。)
sato_shoma

2022/03/09 10:00

何から何まで本当にありがとうございます。アダプターとデータ保持についてよく理解できました。教えていただいたことを踏まえてコードを書き直し、問題なければjimbe様をベストアンサーとして解決済みとさせていただきます。
sato_shoma

2022/03/09 10:34 編集

出来ましたー。自分一人じゃ絶対出来ませんでした。本当にありがとうございます。質問して良かったです。最後に三つほど質問してもよいでしょうか? 1) 初めに教えてくださったコードで使っていたrequireActivity()とはどのようなメソッドでしょうか?(非 static method は static コンテキストから参照できません、という問題を解決できたりしますか) 2) > title と text の組でクラスを作った方が分かり易いでしょう これはどういうことですか?本当にアバウトな質問で申し訳ないんですけどどのようなクラスを作ればよいの   か全く見当もつかなくて...お恥ずかしい質問ですみません 3)アダプタにデータを登録する方法でセッターでするのとAdapterを作り直すのでどちらが軽いとかありますか?
jimbe

2022/03/09 11:26

requireActivity() は getActivity() と同じです。が、 Activity が無かった時(new しただけで show する前とかでしょうか)に requireActivity() は例外を発し、 getActivity() は null を返す点が異なります。 static メソッドからはどちらも使えません。アクティビティと(ダイアログ)フラグメントはオブジェクト同士の関係("特定の(ダイアログ)フラグメントオブジェクトは、特定のアクティビティオブジェクトに管理される")ですが、static メソッドは特定のオブジェクトには関係していません。 オブジェクト指向では一緒に管理すべきデータ( title と text は組で扱うものですよね?)は纏めて置くのが通常です。回答をそのようにしたコードに編集しておきます。 セッターと作り直しでどちらが軽いかでは、僅かな違いまでも気にするなら セッターのほうが軽いでしょう。 ただ、データの件数が数百・数千件程度では殆ど変わらないかもしれません。 性能に関する部分はデータ量や実行環境で異なることになりますので、一通り完成して実際に動作させてから気にするつもり位で良いかと思います。
sato_shoma

2022/03/09 12:01

最後まで丁寧に教えてくださり、本当にありがとうございました。今後また質問することがあれば何卒宜しくお願い致します。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問