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

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

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

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Android Studio

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

Kotlin

Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。

Q&A

解決済

1回答

694閲覧

sharedpreferencesでアプリを一度閉じた後もデータを保持し続けたい

gogatu

総合スコア6

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Android Studio

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

Kotlin

Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。

0グッド

0クリップ

投稿2023/11/03 15:05

実現したいこと

sharedpreferencesでデータを保存し、アプリを一度閉じても保存されたデータが読み込まれるようにしたい。

前提

sharedpreferencesでデータを保存しようとしているが、アプリを一度閉じた後にもう一度見てみるとデータが初期化されている。
具体的には駅の名前を保存しようとしている。駅の名前を入力したときにstation1Name変数がnullだったら代入し、変数の中身を表示しているテキストボックスの横にあるデリートボタンを押すと変数の中身をnullに変更するようになっている。それぞれapply()はしているため保存はされているはず。

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

特にエラーは発生していない

該当のソースコード

Kotlin

1package com.example.CompetitionApp 2 3import android.content.Context 4import android.os.Bundle 5import android.view.LayoutInflater 6import android.view.View 7import android.view.ViewGroup 8import androidx.fragment.app.Fragment 9import com.example.CompetitionApp.R 10import com.example.CompetitionApp.databinding.FragmentHomeBinding 11 12private const val ARG_PARAM1 = "param1" 13private const val ARG_PARAM2 = "param2" 14 15class HomeFragment : Fragment() { 16 // TODO: Rename and change types of parameters 17 private var param1: String? = null 18 private var param2: String? = null 19 20 private var _binding: FragmentHomeBinding? = null 21 private val binding get() = _binding!! 22 23 //データ保存の準備 24 val sharedPref = getActivity()?.getSharedPreferences( 25 getString(R.string.preference_file_key), Context.MODE_PRIVATE) 26 val editor = sharedPref ?.edit() 27 28 //変数を定義 29 var station1Name: String? = sharedPref?.getString("Station1Name", "NoData") 30 31 override fun onCreate(savedInstanceState: Bundle?) { 32 super.onCreate(savedInstanceState) 33 arguments?.let { 34 param1 = it.getString(ARG_PARAM1) 35 param2 = it.getString(ARG_PARAM2) 36 } 37 } 38 39 override fun onCreateView( 40 inflater: LayoutInflater, container: ViewGroup?, 41 savedInstanceState: Bundle? 42 ): View { 43 _binding = FragmentHomeBinding.inflate(layoutInflater, container, false) 44 val view = binding.root 45 46 // ボタンクラスのインスタンス作成 47 val btnListener = BtnListener() 48 49 // リスナを設定 50 binding.searchbutton.setOnClickListener(btnListener) 51 52 binding.stationsearch 53 54 if(station1Name != null){ 55 binding.station1.text = station1Name 56 } 57 58 return view 59 } 60 61 //binding 解放 62 override fun onDestroyView() { 63 super.onDestroyView() 64 _binding = null 65 } 66 67 companion object { 68 fun newInstance(param1: String, param2: String) = 69 HomeFragment().apply { 70 arguments = Bundle().apply { 71 putString(ARG_PARAM1, param1) 72 putString(ARG_PARAM2, param2) 73 } 74 } 75 } 76 77 //ボタンが押されたときの設定 78 private inner class BtnListener: View.OnClickListener{ 79 override fun onClick(v: View) { 80 when(v.id){ 81 binding.searchbutton.id -> { 82 //検索ボタンの処理 83 val searchstation: String = binding.stationsearch.getText().toString() 84 //入力がなければ何もしない 85 if(searchstation != ""){ 86 //新しく入力された値を代入 87 if(station1Name == null){ 88 station1Name = searchstation 89 editor?.putString("Station1Name", station1Name) 90 editor?.apply() 91 binding.station1.text = station1Name 92 } 93 } 94 } 95 96 //デリートボタンが押された時の処理 97 binding.station1del.id -> { 98 station1Name = null 99 100 editor?.putString("Station1Name", station1Name) 101 editor?.apply() 102 103 binding.station1.text = getString(R.string.nodata) 104 } 105 } 106 } 107 } 108}

試したこと

データが消えてしまう条件がアプリを閉じたときのみであることを確かめたが、この場合どうすればいいのかは調べても出てこず、全く見当がつかない。

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

・ほかのfragmentに移動して戻ってきた後
・バックグラウンドに行った後
・実行しているエミュレータをスリープにしてもう一度起動した後
以上の3パターンはデータが残っていることを確認している。

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

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

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

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

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

guest

回答1

0

ベストアンサー

そもそも SharedPreference オブジェクト自体を得られてないのではないでしょうか。

val sharedPref = getActivity()?.getSharedPreferences(

Fragment オブジェクトの生成時はまだ Activity とリンクしていませんので、 getActivity() は null を返します。
sharedPref が null なら editor も null になり、 putString も apply も実行されません。

・ほかのfragmentに移動して戻ってきた後
・バックグラウンドに行った後
・実行しているエミュレータをスリープにしてもう一度起動した後

は、 SharedPreference に保存・再生されたのでは無く、 Fragment 自体が残っていて、各 EditText や TextView の値が Fragment によって保存・再生されたのでは無いでしょうか。

※こちらの環境により、パッケージを変えています

kotlin

1package com.teratail.q_rdjw8xeirai92w 2 3import android.content.Context 4import android.os.Bundle 5import android.view.LayoutInflater 6import android.view.View 7import android.view.ViewGroup 8import androidx.fragment.app.Fragment 9import com.teratail.q_rdjw8xeirai92w.databinding.FragmentHomeBinding 10 11class HomeFragment : Fragment() { 12 companion object { 13 //要らないものは消しておく. パラメータは互換性のため. 14 fun newInstance(param1: String, param2: String) = HomeFragment() 15 16 private const val PREFERENCE = "preference_file" //リソースから取る程のものでもなさそう 17 private const val PREF_STATION1_NAME = "Station1Name" 18 private const val CLEAR_VALUE = "NoData" 19 } 20 21 private var _binding: FragmentHomeBinding? = null 22 private val binding get() = _binding!! 23 24 //onCreateView は生成のみ 25 override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { 26 _binding = FragmentHomeBinding.inflate(layoutInflater, container, false) 27 return binding.root 28 } 29 30 //それぞれの処理はこちらに 31 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 32 super.onViewCreated(view, savedInstanceState) 33 34 val sharedPref = requireActivity().getSharedPreferences(PREFERENCE, Context.MODE_PRIVATE) 35 36 //最期の設定値を取り込み(無ければ "NoData") 37 binding.station1.text = sharedPref.getString(PREF_STATION1_NAME, CLEAR_VALUE); 38 39 //検索ボタンの処理 40 binding.searchbutton.setOnClickListener { 41 val searchstation = binding.stationsearch.getText().toString() 42 if(searchstation != ""){ 43 //保存 44 sharedPref.edit().putString(PREF_STATION1_NAME, searchstation).apply() //Editor は随時取り出し 45 binding.station1.text = searchstation 46 } 47 } 48 49 //削除ボタンの処理 50 binding.station1del.setOnClickListener { 51 //クリア 52 sharedPref.edit().remove(PREF_STATION1_NAME).apply() 53 binding.station1.text = CLEAR_VALUE 54 } 55 } 56 57 override fun onDestroyView() { 58 _binding = null 59 super.onDestroyView() 60 } 61}

投稿2023/11/03 16:43

編集2023/11/03 18:43
jimbe

総合スコア12648

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

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

gogatu

2023/11/04 01:38 編集

 jimbe様、詳しい回答をいただき、ありがとうございました。いただいたプログラムを実行した結果、考えていた通りの結果を得ることができ、アプリを閉じた後もデータが保存されていました。  自分なりに元のものといただいたプログラムを比べ、いろいろ実行してみた結果、 private const val PREFERENCE = "preference_file" private const val PREF_STATION1_NAME = "Station1Name" private const val CLEAR_VALUE = "NoData" の部分は他のfragmentでもここで保存したものを使用したいため、もともとのstring.xmlから持ってくる形にしようと思います。  しかし、わからなかったことがいくつかあるため、質問をさせてください。 1, paramに関係すること。 private var param1: String? = null private var param2: String? = null super.onCreate(savedInstanceState) arguments?.let { param1 = it.getString(ARG_PARAM1) param2 = it.getString(ARG_PARAM2) } などの部分を、なぜ fun newInstance(param1: String, param2: String) = HomeFragment() の一行でまとめられるかがわからない。 2, private inner class BtnListener: View.OnClickListener{} 内で記述していたボタンが押されたときの処理について、なぜこれがViewの初期化のためのイベント内で処理できるのかがわからない。 3, onViewCreatedの一番最初の行にある、 super.onViewCreated(view, savedInstanceState) がどのような意味なのかが分からない。  以上の3点について、差し支えなければお時間のある時にご回答いただけると幸いです。よろしくお願いいたします。
jimbe

2023/11/04 04:35 編集

>他のfragmentでもここで保存したものを使用したい なるほど。事情はそれぞれですのでコードは幾らでも弄ってください^^ ただ、他のフラグメントでも使うなら、 private を外して他から定義を使うことも出来ます。 ”NoData” は表示する文字列ですので国際化等の観点からリソースにするのが正解ですが、 Preference の名前等は言ってしまえば他と区別出来れば何でも良い類ですので、リソースにする意味はありません。 (定義を使い回すなら書くのはココでは無く Activity のほうが良さそうですが。) 1 の生成部分ですが、恐らくこの HomeFragment は AndroidStudio でプロジェクトを生成した時のコードを改造されているものと思います。 AndroidStudio でプロジェクトを生成する際に選択する雛形はそれぞれの機能のサンプルも兼ねていて、フラグメントが関係する雛形ではフラグメントの生成に getInstance を使ってパラメータを2つ指定するような形を作り出します。この2つのパラメータは特別フラグメントに必要なわけでは無く、単に『フラグメントの生成時に何かデータを渡したいならこのように出来ます』というサンプルとしてあるだけです。 ですが質問のコードは param1/param2 を使っていません。従ってそれに関する部分を削除しました。 ただし、 getInstance メソッドのパラメータを削除してしまうと getInstance を使っている方も修正しないといけませんから、回答としては呼ぶ側の修正も載せないとコードとして完結しません。しかし質問には呼ぶ側のコードはご提示されていませんので修正・提示することは不可能です。 従って、呼ぶ側の修正はしなくてすむように getInstance メソッドのパラメータだけは残しました。 2 の OnClickListener ですが、これは"定義"であって"処理"ではありません。 これは kotlin の書き方・見え方にも問題があるのですが、 setOnClickListener { ~ } という書き方は setOnClickListener( { ~ } ) と同じ意味で、 kotlin ではパラメータの最後のラムダ式は () の外に書けるという文法(?)があるのです。 { ~ } は(型推論により) View.OnClickListener { ~ } と同じですし、もちろんこれは View.OnClickListener( { ~ } ) のことです。 kotlin ではこのように要らないモノはどんどん省略することで記述量を減らすことが良いとされています。 これは自然言語でも一文で全てを説明するのではなく文脈によって一部を省略したりするのと同じと思いますが、一方で一部だけを見ると何のことを言っているのか分からなくなり易いとも言えると思います。 3 の継承元の呼び出しですが、今回は必ずしも必要ありません。 ですがクラスによっては継承元のクラスでも何か処理をしている場合があり、その場合は override 元のメソッドを呼ばなければなりません。( onDestroyView がそうですね。) 呼ぶ必要が有るか無いかはドキュメントに書いてあったりしますが、一々メソッド毎に確認するよりはいっそ書いておけば問題無いということも言えます。
gogatu

2023/11/04 06:43

ご回答ありがとうございました。 1について、もともと書かれていたコードは消したら何か起こりそうで怖くて消せない...という感じがあり残していましたが、ただのサンプルだとは思っていませんでした...確認不足です。 2について、ラムダ式については持っている書籍で読んだことがありましたが、いまいちよくわからないまま進めていました。省略の仕方がよくわかっていないため、もう一度読み直そうと思います。 3について、クラスと継承についての知識もまだほとんどないに等しいため、勉強していこうと思います。
gogatu

2023/11/04 06:45

jimbe様、質問にも詳しく答えていただき、本当にありがとうございました。
gogatu

2023/11/05 10:06

ありがとうございます! よく読んで理解できるように頑張ります。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問