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

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

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

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

Android Studio

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

Q&A

解決済

1回答

410閲覧

【AndroidStudio(Java)】Fragment間での値を受け渡し/ViewModelからの値の受け取り方

liptoon

総合スコア7

Java

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

Android Studio

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

0グッド

0クリップ

投稿2024/03/07 21:38

編集2024/03/13 08:37

実現したいこと

いつもお世話になっております。Javaを最近学習し始めAndroidアプリを作成中なのですが、Fragment間での値の受け渡し方法について困っています。
MainActivityに紐づくMainFragmentでページビューによってReceiveFragmentとBlankFragmentがスワイプで表示される仕組みにしており、ReceiveFragmentから遷移したPassFragmentで入力した値をボタンクリックによって保存、ReceiveFragmentへ受け渡したいと思っています。

発生している問題・分からないこと

エラーから、ViewModelがnullとのことでどうしたら値の取得がうまくいくのか悩んでいます。
そもそもViewModelの使い方がまちがっているのでしょうか・・・
お手数ですがどうぞご教授よろしくお願いいたします。

該当のソースコード

Java

1package com.websarva.wings.android.sampleapp; 2 3import android.os.Bundle; 4import androidx.annotation.NonNull; 5import androidx.annotation.Nullable; 6import androidx.fragment.app.Fragment; 7import androidx.fragment.app.FragmentManager; 8import androidx.fragment.app.FragmentTransaction; 9import androidx.lifecycle.ViewModelProvider; 10import android.view.View; 11import android.widget.Button; 12import android.widget.TextView; 13 14 15public class PassFragment extends Fragment { 16 17 //ViewModelProviderでViewModelを取得 18 MainViewModel viewModel = new ViewModelProvider(this).get(MainViewModel.class); 19 20 //予算名取得用 21 private TextView etBudgetNameV; 22 23 public PassFragment() { 24 super(R.layout.fragment_pass); 25 } 26 27 @Override 28 public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 29 super.onViewCreated(view, savedInstanceState); 30 31 //予算名受け渡しのためビューをあらかじめ取得 32 etBudgetNameV=view.findViewById(R.id.etBudgetName); 33 34 //保存ボタンにリスナ設定 35 Button btSave=view.findViewById(R.id.btSave); 36 btSave.setOnClickListener(new btSaveClickListener()); 37 } 38 39 40 //保存ボタンのリスナ 41 private class btSaveClickListener implements View.OnClickListener{ 42 @Override 43 public void onClick(View view) { 44 45 //画面遷移時に引き渡すBundleオブジェクトを定義 46 Bundle bundle=new Bundle(); 47 48 //入力ありならviewModelに引き継ぎデータを格納 49 if(etBudgetNameV.getText().toString().length()!=0){ 50 viewModel.budgetName.add(etBudgetNameV.getText().toString()); 51 //フラグメントマネージャーを取得 52 FragmentManager clfManager=getParentFragmentManager(); 53 //フラグメントトランザクションの開始 54 FragmentTransaction clfTransaction=clfManager.beginTransaction(); 55 //フラグメントトランザクションが正しく動作するように設定 56 clfTransaction.setReorderingAllowed(true); 57 //現在の表示内容をバックスタックに追加 58 clfTransaction.addToBackStack("createList"); 59 //リスト作成画面フラグメントをMainFragmentに置き換え 60 clfTransaction.replace(R.id.fragmentMainContainer,MainFragment.class,bundle); 61 //フラグメントトランザクションのコミット 62 clfTransaction.commit(); 63 } 64 } 65 } 66}

Java

1package com.websarva.wings.android.sampleapp; 2 3import android.os.Bundle; 4 5import androidx.annotation.NonNull; 6import androidx.annotation.Nullable; 7import androidx.fragment.app.Fragment; 8import androidx.fragment.app.FragmentManager; 9import androidx.fragment.app.FragmentTransaction; 10import androidx.lifecycle.ViewModelProvider; 11 12import android.view.LayoutInflater; 13import android.view.View; 14import android.view.ViewGroup; 15import android.widget.ArrayAdapter; 16import android.widget.ImageButton; 17import android.widget.ListView; 18import android.widget.SimpleAdapter; 19 20import java.util.ArrayList; 21import java.util.HashMap; 22import java.util.List; 23import java.util.Map; 24 25public class ReceiveFragment extends Fragment { 26 27 private MainViewModel viewModel; 28 29 //予算の入力数 30 private int inputNumber; 31 //予算名リスト 32 private List<Map<String,String>> budgetList; 33 private Map<String,String> budgetMap; 34 35 36 public ReceiveFragment() { 37 super(R.layout.fragment_receive); 38 } 39 40 @Override 41 public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 42 super.onViewCreated(view, savedInstanceState); 43 44 //ボタンを取得 45 ImageButton ibAddButton =view.findViewById(R.id.ibAdd); 46 //戻るボタンにリスナを登録 47 ibAddButton.setOnClickListener(new ibAddClickListener()); 48 49 //ListViewオブジェクトを取得 50 ListView lvBudgetV =view.findViewById(R.id.lvBudget); 51 52 53 //受け渡されたデータ取得 54 //ViewModelProviderでViewModelを取得 55 viewModel = new ViewModelProvider(this).get(MainViewModel.class); 56 //予算の入力数を取得 57 inputNumber=viewModel.budgetName.size(); 58 //budgetNameに入力があればデータ取得 59 if(inputNumber != 0){ 60 //ListViewオブジェクトに表示するリストデータ用Listオブジェクトを作成 61 budgetList = new ArrayList<Map<String, String>>(); 62 for(int i=0 ; i<inputNumber ; i++){ 63 //項目データを格納するMapオブジェクトを用意 64 budgetMap= new HashMap<String,String>(); 65 budgetMap.put("budgetName",viewModel.budgetName.get(i)); 66 budgetList.add(budgetMap); 67 } 68 //アダプタオブジェクトを生成 69 ArrayAdapter budgetListAdapter=new ArrayAdapter(requireContext(),android.R.layout.simple_list_item_1,budgetList); 70 //リストビューにアダプタを設定 71 lvBudgetV.setAdapter(budgetListAdapter); 72 } 73 } 74 75 //ボタンをクリックしたときのリスナクラス 76 private class ibAddClickListener implements View.OnClickListener{ 77 @Override 78 public void onClick(View view){ 79 80 //引継ぎデータをまとめて格納できるBundleオブジェクトを作成 81 Bundle bundle=new Bundle(); 82 83 84 //フラグメントマネージャーを取得 85 FragmentManager lfManager=getParentFragmentManager(); 86 //フラグメントトランザクションの開始 87 FragmentTransaction transaction=lfManager.beginTransaction(); 88 //フラグメントトランザクションが正しく動作するように設定 89 transaction.setReorderingAllowed(true); 90 //現在の表示内容をバックスタックに追加 91 transaction.addToBackStack("List"); 92 //ListFragmentをリスト作成画面フラグメントに置き換え 93 transaction.replace(R.id.fragmentMainContainer,PassFragment.class,bundle); 94 //フラグメントトランザクションのコミット 95 transaction.commit(); 96 } 97 } 98}

Java

1package com.websarva.wings.android.sampleapp; 2 3import androidx.lifecycle.ViewModel; 4 5import java.util.List; 6 7public class MainViewModel extends ViewModel { 8 //予算名 9 public List<String> budgetName; 10 11} 12

試したこと・調べたこと

  • teratailやGoogle等で検索した
  • ソースコードを自分なりに変更した
  • 知人に聞いた
  • その他
上記の詳細・結果

最初はBundleで値を受け渡そうとしたのですが値の受け渡し先(ReceiveFragment)と画面の遷移先(MainFragment)が異なっており方法がわからなかったのと、別のFragmentでも書き換える可能性があり、ライフサイクルの観点でもViewModelを使用するのがよいと考えました。ViewModelを使用し、ネットで検索しながらコードを書いてみたのですが、nullになりうまく取得できませんでした。ViewModelに値が入っているかの確認方法が調べてもわからずこちらに質問させていただきました。

※PassFragmentから取得する値について

質問では簡単のため省略し予算名のみにしてありますが、現在PassFragmentのレイアウトは添付画像のようになっており、最終的には①~⑤の値を受け渡したいと思っております。
①予算名②期間③合計予算④項目(食費、医療費など)⑤項目別予算
④⑤はプラスボタンで追加、ListViewに表示できるようになっており⑤の合計が自動で③に入るようになっています。

イメージ説明

補足

AndroidStudio Giraffe

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

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

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

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

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

jimbe

2024/03/08 04:22

>エラーから、ViewModelがnullとのこと そのエラー文を質問に追加して頂けますか。
guest

回答1

0

ベストアンサー

簡単に言えば、 ViewModel は ViewModelProvider のパラメータとして渡した ViewModelStoreOwner が管理しているモノを共有することで受け渡しを実現します。
ですから同じ ViewModelStoreOwner から得ないと共有出来ません。

提示されている PassFragment も ReceiveFragment も

new ViewModelProvider(this).get(MainViewModel.class);

としており、 this つまり各々が管理している ViewModel を得ていますので、別々のモノを得ています。
両方で同じモノを使いたいのであれば両方で同じ ViewModelStoreOwner を使わなければなりませんので、双方が乗っている(であろう)アクティビティを使います。

Activity/Fragment から ViewModel を得る

(PassFragment はフィールドの初期化で ViewModel を得ていますがアクティビティはその時点では接続されていないので得られませんから ReceiveFragment のように onViewCreated 内等に移動させたうえで) 双方とも

java

1new ViewModelProvider(requireActivity()).get(MainViewModel.class);

とすれば、同じアクティビティから同じモノを得られます。


全体の構造が
Activity内の各フラグメントとViewModelの関係
であれば、 PassFragment はダイアログにしたほうが簡単だと page:0 上で二つのフラグメントを切り替える FragmentContainerView を作るのが自然かと 思います。

MainViewModel.java

java

1import androidx.annotation.NonNull; 2import androidx.lifecycle.*; 3 4import java.util.*; 5 6public class MainViewModel extends ViewModel { 7 private MutableLiveData<List<String>> budgetNameListLiveData = new MutableLiveData<>(new ArrayList<>()); 8 LiveData<List<String>> getBudgetNameList() { 9 //getValue したリストを書き換えられないように、 "Collections.unmodifiableList を返す LiveData" を返す 10 return Transformations.switchMap(budgetNameListLiveData, e -> new MutableLiveData<>(Collections.unmodifiableList(e))); 11 } 12 void addBudgetName(@NonNull String name) { 13 List<String> list = budgetNameListLiveData.getValue(); 14 list.add(name); 15 budgetNameListLiveData.setValue(list); 16 } 17}

Page0Fragment.java (これを MainFragment の ViewPager page:0 に載せる)

java

1import android.os.Bundle; 2import android.view.View; 3 4import androidx.annotation.*; 5import androidx.fragment.app.*; 6 7public class Page0Fragment extends Fragment { 8 Page0Fragment() { 9 super(R.layout.fragment_page0); 10 } 11 12 @Override 13 public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 14 super.onViewCreated(view, savedInstanceState); 15 16 if(savedInstanceState == null) { 17 getChildFragmentManager().beginTransaction() 18 .replace(R.id.fragment_container_view, new ReceiveFragment()) 19 .commit(); 20 } 21 } 22}

res/layout/fragment_page0.xml

xml

1<?xml version="1.0" encoding="utf-8"?> 2<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android" 3 android:id="@+id/fragment_container_view" 4 android:layout_width="match_parent" 5 android:layout_height="match_parent" />

ReceiveFragment.java

java

1import android.os.Bundle; 2 3import androidx.annotation.*; 4import androidx.fragment.app.Fragment; 5import androidx.lifecycle.ViewModelProvider; 6 7import android.view.*; 8import android.widget.*; 9 10import java.util.*; 11 12public class ReceiveFragment extends Fragment { 13 public ReceiveFragment() { 14 super(R.layout.fragment_receive); 15 } 16 17 @Override 18 public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 19 super.onViewCreated(view, savedInstanceState); 20 21 MainViewModel viewModel = new ViewModelProvider(requireActivity()).get(MainViewModel.class); 22 23 Button addButton = view.findViewById(R.id.add_button); 24 addButton.setOnClickListener(v -> transitionToPass()); 25 26 ListView budgetListView = view.findViewById(R.id.budget_listview); 27 Adapter adapter = new Adapter(); 28 //MainViewModel の budgetNameListLiveData が設定 (setValue/postValue) されたら、その値をパラメータとして adapter.setList() を呼ぶように設定 29 viewModel.getBudgetNameList().observe(getViewLifecycleOwner(), adapter::setList); 30 budgetListView.setAdapter(adapter); 31 } 32 33 private void transitionToPass() { 34 @IdRes int containerViewId = ((View)getView().getParent()).getId(); 35 getParentFragmentManager().beginTransaction() 36 .replace(containerViewId, new PassFragment()) 37 .commit(); 38 } 39 40 private static class Adapter extends BaseAdapter { 41 private List<String> list = Collections.emptyList(); 42 43 @SuppressWarnings("notifyDataSetChanged") //IDE での警告を抑制 44 void setList(List<String> list) { 45 this.list = new ArrayList(list); //防御コピー 46 notifyDataSetChanged(); 47 } 48 49 @Override 50 public int getCount() { 51 return list.size(); 52 } 53 54 @Override 55 public Object getItem(int position) { 56 return position; 57 } 58 59 @Override 60 public long getItemId(int position) { 61 return position; 62 } 63 64 @Override 65 public View getView(int position, View convertView, ViewGroup parent) { 66 return (convertView == null ? new ViewHolder(parent) : (ViewHolder)convertView.getTag()) 67 .bind(list.get(position)); 68 } 69 70 private class ViewHolder { 71 private final View itemView; 72 private final TextView text1; 73 74 ViewHolder(ViewGroup parent) { 75 itemView = LayoutInflater.from(parent.getContext()).inflate(android.R.layout.simple_list_item_1, parent, false); 76 itemView.setTag(this); 77 text1 = itemView.findViewById(android.R.id.text1); 78 } 79 80 View bind(String name) { 81 text1.setText(name); 82 return itemView; 83 } 84 } 85 } 86}

res/layout/fragment_receive.xml

xml

1<?xml version="1.0" encoding="utf-8"?> 2<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:app="http://schemas.android.com/apk/res-auto" 4 android:layout_width="match_parent" 5 android:layout_height="match_parent"> 6 7 <ListView 8 android:id="@+id/budget_listview" 9 android:layout_width="0dp" 10 android:layout_height="0dp" 11 app:layout_constraintBottom_toTopOf="@id/add_button" 12 app:layout_constraintEnd_toEndOf="parent" 13 app:layout_constraintStart_toStartOf="parent" 14 app:layout_constraintTop_toTopOf="parent" /> 15 16 <Button 17 android:id="@+id/add_button" 18 android:layout_width="wrap_content" 19 android:layout_height="wrap_content" 20 android:text="ADD" 21 app:layout_constraintBottom_toBottomOf="parent" 22 app:layout_constraintEnd_toEndOf="parent" 23 app:layout_constraintStart_toStartOf="parent" /> 24 25</androidx.constraintlayout.widget.ConstraintLayout>

PassFragment.java

java

1import android.os.Bundle; 2 3import androidx.annotation.*; 4import androidx.fragment.app.*; 5import androidx.lifecycle.ViewModelProvider; 6 7import android.text.*; 8import android.view.View; 9import android.widget.*; 10 11public class PassFragment extends Fragment { 12 public PassFragment() { 13 super(R.layout.fragment_pass); 14 } 15 16 @Override 17 public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 18 super.onViewCreated(view, savedInstanceState); 19 20 MainViewModel viewModel = new ViewModelProvider(requireActivity()).get(MainViewModel.class); 21 22 Button cancelButton = view.findViewById(R.id.cancel_button); 23 cancelButton.setOnClickListener(v -> transitionToReceive()); 24 25 Button saveButton = view.findViewById(R.id.save_button); 26 saveButton.setEnabled(false); 27 28 EditText budgetNameEdit = view.findViewById(R.id.budget_name_edit); 29 //名前の入力状態によって addButton を使えるか使えないかにする (これによって saveButton 押下時に有効/無効の判定が不要になる) 30 budgetNameEdit.addTextChangedListener(new TextWatcher() { 31 @Override 32 public void beforeTextChanged(CharSequence s, int start, int count, int after) { /*no process*/ } 33 @Override 34 public void onTextChanged(CharSequence s, int start, int before, int count) { /*no process*/ } 35 @Override 36 public void afterTextChanged(Editable s) { 37 saveButton.setEnabled(s.length() > 0); 38 } 39 }); 40 41 saveButton.setOnClickListener(v -> { 42 viewModel.addBudgetName(budgetNameEdit.getText().toString()); 43 transitionToReceive(); 44 }); 45 } 46 47 private void transitionToReceive() { 48 @IdRes int containerViewId = ((View)getView().getParent()).getId(); 49 getParentFragmentManager().beginTransaction() 50 .replace(containerViewId, new ReceiveFragment()) 51 .commit(); 52 } 53}

res/layout/fragment_pass.xml

xml

1<?xml version="1.0" encoding="utf-8"?> 2<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:app="http://schemas.android.com/apk/res-auto" 4 android:layout_margin="8dp" 5 android:layout_width="match_parent" 6 android:layout_height="match_parent"> 7 8 <EditText 9 android:id="@+id/budget_name_edit" 10 android:layout_width="0dp" 11 android:layout_height="wrap_content" 12 android:hint="BudgetName" 13 app:layout_constraintBottom_toTopOf="@id/save_button" 14 app:layout_constraintEnd_toEndOf="parent" 15 app:layout_constraintStart_toStartOf="parent" 16 app:layout_constraintTop_toTopOf="parent" /> 17 18 <Button 19 android:id="@+id/cancel_button" 20 android:layout_width="wrap_content" 21 android:layout_height="wrap_content" 22 android:text="CANCEL" 23 android:layout_marginEnd="10dp" 24 app:layout_constraintEnd_toStartOf="@id/save_button" 25 app:layout_constraintTop_toTopOf="@id/save_button" /> 26 27 <Button 28 android:id="@+id/save_button" 29 android:layout_width="wrap_content" 30 android:layout_height="wrap_content" 31 android:text="SAVE" 32 app:layout_constraintBottom_toBottomOf="parent" 33 app:layout_constraintEnd_toEndOf="parent" 34 app:layout_constraintTop_toBottomOf="@id/budget_name_edit" /> 35 36</androidx.constraintlayout.widget.ConstraintLayout>

投稿2024/03/08 04:38

編集2024/03/09 18:54
jimbe

総合スコア12659

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

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

liptoon

2024/03/09 09:27 編集

返信が遅くなってしまい申し訳ございません。ご回答ありがとうございます。ViewModelProviderの第一引数について理解できていなかったので大変助かりました。 > PassFragment はフィールドの初期化で ViewModel を得ていますがアクティビティはその時点では接続 > されていないので得られません これについても修正してみたのですが、エラーメッセージは変わらずでした。(エラーメッセージは追記させていただきます) > PassFragment はダイアログにしたほうが簡単だと思います。 たいへんわかりやすく教えていただいて恐縮なのですが、PassFragmentには他に課題点を簡潔にするために省略している複数の入力欄が存在し、ダイアログにしづらい状態にあります。できればPassFragmentのまま進めたいと考えています。
liptoon

2024/03/09 10:59

字数制限で本文に追記できなかったため以下にエラーメッセージを書きます。 Process: com.websarva.wings.android.sampleapp, PID: 18209 java.lang.NullPointerException: Attempt to invoke interface method 'int java.util.List.size()' on a null object reference at com.websarva.wings.android.sampleapp.ReceiveFragment.onViewCreated(ReceiveFragment.java:57) at androidx.fragment.app.Fragment.performViewCreated(Fragment.java:2987) at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:546) at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:282) at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2189) at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:2100) at androidx.fragment.app.FragmentManager.execSingleAction(FragmentManager.java:1971) at androidx.fragment.app.BackStackRecord.commitNow(BackStackRecord.java:305) at androidx.viewpager2.adapter.FragmentStateAdapter.placeFragmentInViewHolder(FragmentStateAdapter.java:341) at androidx.viewpager2.adapter.FragmentStateAdapter.onViewAttachedToWindow(FragmentStateAdapter.java:276) at androidx.viewpager2.adapter.FragmentStateAdapter.onViewAttachedToWindow(FragmentStateAdapter.java:67) at androidx.recyclerview.widget.RecyclerView.dispatchChildAttached(RecyclerView.java:7556) at androidx.recyclerview.widget.RecyclerView$5.addView(RecyclerView.java:860) at androidx.recyclerview.widget.ChildHelper.addView(ChildHelper.java:107) at androidx.recyclerview.widget.RecyclerView$LayoutManager.addViewInt(RecyclerView.java:8601) at androidx.recyclerview.widget.RecyclerView$LayoutManager.addView(RecyclerView.java:8559) at androidx.recyclerview.widget.RecyclerView$LayoutManager.addView(RecyclerView.java:8547) at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1641) at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1587) at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:665) at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:4134) at androidx.recyclerview.widget.RecyclerView.dispatchLayout(RecyclerView.java:3851) at androidx.recyclerview.widget.RecyclerView.onLayout(RecyclerView.java:4404) at android.view.View.layout(View.java:23693) at android.view.ViewGroup.layout(ViewGroup.java:6413) at androidx.viewpager2.widget.ViewPager2.onLayout(ViewPager2.java:527) at android.view.View.layout(View.java:23693) at android.view.ViewGroup.layout(ViewGroup.java:6413) at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1891) at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1729) at android.widget.LinearLayout.onLayout(LinearLayout.java:1638) at android.view.View.layout(View.java:23693) at android.view.ViewGroup.layout(ViewGroup.java:6413) at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332) at android.widget.FrameLayout.onLayout(FrameLayout.java:270) at android.view.View.layout(View.java:23693) at android.view.ViewGroup.layout(ViewGroup.java:6413) at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332) at android.widget.FrameLayout.onLayout(FrameLayout.java:270) at android.view.View.layout(View.java:23693) at android.view.ViewGroup.layout(ViewGroup.java:6413) at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1891) at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1729) at android.widget.LinearLayout.onLayout(LinearLayout.java:1638) at android.view.View.layout(View.java:23693) at android.view.ViewGroup.layout(ViewGroup.java:6413) at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332) at android.widget.FrameLayout.onLayout(FrameLayout.java:270) at android.view.View.layout(View.java:23693) at android.view.ViewGroup.layout(ViewGroup.java:6413) at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1891) 2024-03-09 18:03:53.417 18209-18209 AndroidRuntime com...sarva.wings.android.sampleapp E at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1729) at android.widget.LinearLayout.onLayout(LinearLayout.java:1638) at android.view.View.layout(View.java:23693) at android.view.ViewGroup.layout(ViewGroup.java:6413) at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
jimbe

2024/03/09 12:22 編集

>ReceiveFragment.java:57 には何が書いてありますか。 ViewModel の list の size() を呼んでいるのなら、 ViewModel の list にオブジェクトを設定してあるでしょうか? 回答のコードでは、 ViewModel の budgetNameListLiveData には初期値として空のリスト new ArrayList() を設定しています。
jimbe

2024/03/09 12:00

>PassFragmentには他に課題点を簡潔にするために省略している複数の入力欄が存在し、ダイアログにしづらい状態 動作としてはダイアログと同じく ReceiveFragment で add を押したら PassFragment が表示され、 save ( もしくは cancel )が押されたら ReceiveFragment に戻るで正しいのでしたら、 ViewPager の page 0 に当たるフラグメントを作って FragmentContainerView として ReceiveFragment と PassFragment を切り替えて表示する形になると思います。 回答を編集します。
liptoon

2024/03/09 22:11

>> ReceiveFragment.java:57 > には何が書いてありますか。 ViewModel の list の size() を呼んでいます。 やりたいこととしては、ViewModelに初期値を設定しておき、PassFragmentで作った予算名をViewModelのリストに保存、ReceiveFragmentで呼び出したいです。
liptoon

2024/03/09 22:17

> 動作としてはダイアログと同じく ReceiveFragment で add を押したら PassFragment が表示され、 > save ( もしくは cancel )が押されたら ReceiveFragment に戻るで正しいのでしたら 正しいです。 > ViewPager の page 0 に当たるフラグメントを作って FragmentContainerView として > ReceiveFragment と PassFragment を切り替えて表示する形になると思います。 これは、現在PageViewの切替えを行っているMainFragmentでRecieveFragmentとPassFragmentの表示切替えを行うのがあまりよろしくないということでしょうか?
liptoon

2024/03/10 00:35

いただいた回答をもとにいろいろと試してみたいのですが、こちらの知識不足でご回答いただいたコードの解読に時間がかかりそうです。またお返事が遅くなったら申し訳ないです。
jimbe

2024/03/10 04:44

>現在PageViewの切替えを行っているMainFragmentでRecieveFragmentとPassFragmentの表示切替えを行うのがあまりよろしくないということでしょうか *Page:0 内で* Receive と Pass 両フラグメントを切り替える必要があるのなら、 Page:0 内にフラグメントを入れ替えるための器が必要というだけです。 PassFragment がダイアログにするにはちょっと…ということがあったように、どうするのが良いかは部分だけで判断出来るとは限りませんし、そもそも"良い"とはどういうことかも(勿論動作しないのでは駄目ですが)必ずしも決まっているものではありません。 現状私に見えているのは回答の構造の図の形で、 ReceiveFragment と PassFragment の関係を MainFragment が関与する必要が無さそうに思います。 もし PassFragment の戻り先が ReceiveFragment とは限らないから PassFragment 内に "ReceiveFragment" と書きたくないのであれば、 PassFragment に replace するときにバックスタックに保存して PassFragment はそれを復元するようにすると同じ動きになりそうです。("戻る"ボタンでキャンセル扱いになることになるのでそれをどうするかともなりそうですが。) また、 PassFragment がダイアログで無いのはその内容が大きいからということなので、なら画面いっぱいの大きさのダイアログにしてしまうというのもアリかもしれません。 [Android : Full Screen DialogFragment] https://medium.com/geekculture/android-full-screen-dialogfragment-1410dbd96d37
jimbe

2024/03/11 07:54 編集

>コードの解読に時間がかかりそう どの辺りで引っかかっておられるのかに依りますが、View(Activity/Fragment) と ViewModel の関係において LiveData を用いるのは、 GUI においてアプリ側が *受け身* になることと同じです。 例えば Button はアプリが随時「押された?」と調べるのでは無く『ボタンが押されたらこれを実行してね』というモノ(OnClickListener)をボタンに登録しておいて押されたら OS に呼んで貰いますね。 それと同じように View の表示でも『データが更新されたらこれを実行してね』というモノ(Observer)を LiveData に登録しておいて、更新されたら呼んでもらうのです。 この LiveData をフラグメント間で共有できる ViewModel で用いることで、表示側フラグメントは LiveData に(再)表示する Observer を登録しておき、入力側フラグメントでその LiveData の値を更新すると、表示側の Observer が実行されて表示が更新されるという流れになります。 この時重要なのは、 LiveData は Observer を登録した時にも自身の現在の値で Observer を実行してくれることです。つまり、表示側は(初期値を LiveData に設定しておくことで)初期表示も(データの更新による)再表示も Observer の登録一つで出来ることになります。
liptoon

2024/03/12 09:07

引き続きご回答ありがとうございます! > 現状私に見えているのは回答の構造の図の形で、 ReceiveFragment と PassFragment の関係を MainFragment が関与する必要が無さそう たしかに、現状関与の必要がないのでpage0を挟んでおこうと思います。 >どの辺りで引っかかっておられるのかに依りますが すみません;もっと基礎的なJavaの文法(メソッド参照など)からでした・・・LiveDataの使い方やViewHolder関連なども全く理解できていなかったので、今回書いていただいたコードを調べることでかなり勉強になりました。ありがとうございました。
liptoon

2024/03/12 09:24

> また、 PassFragment がダイアログで無いのはその内容が大きいからということなので、なら画面いっぱいの大きさのダイアログにしてしまうというのもアリかもしれません。 今回、PassFragmentには前回お世話になった日付入力ダイアログやリストビューなども含まれていたためダイアログにすることは諦めてしまいました。せっかくいろいろと教えていただいたのに申し訳ないです。 ですが、おかげさまでLiveDataを使うことでようやく想定していた値の受け渡しができるようになりました。ありがとうございました! もう一つお聞きしたいのですが、受け渡したい値(Listが全部で5つあります)を増やす場合はひとつひとつをMutableLiveDataに保存して今回したような処理(書き換え防止など)を行うものなのでしょうか? 一つ一つ書いているとなんだか煩雑な気がしまして・・・
jimbe

2024/03/12 10:58 編集

>もっと基礎的なJavaの文法(メソッド参照など) a::b とか c->{} とかはそれまでの java の書き方と大分違いますから、慣れないと?が並ぶかもしれません。逆に慣れると c->a.b(c) から new A(){void b(c){{~}} とかが長~く感じてしまって teratail の文字数制限もあって短くしたくなっちゃうんですね。 お伝えするには長くなっても "java らしく" 書いたほうが良いとは思っているのですが…。 >日付入力ダイアログやリストビューなども含まれていたため ダイアログ上からダイアログの表示も出来ますし、リストビューも載せられると思いますが…。 >受け渡したい値(Listが全部で5つあります)を増やす場合はひとつひとつをMutableLiveDataに保存して今回したような処理(書き換え防止など)を行うものなのでしょうか? >一つ一つ書いているとなんだか煩雑な気がしまして・・・ そのリスト群がどのようなタイミングで表示が更新されるのかどう表示されるのかに寄りますが、基本的には個別にしたほうが良いと思います。 ViewModel は View(この場合は フラグメント) の構造に沿ってデータを用意する役割があります。各リストを View で各々 ListView で表示するのなら、その辺りの構造は ReceiveFragment のリストと同じになるでしょう。 そして、各リストに登録するようなダイアログが存在するとすれば、 ReceiveFragment と PassFramgent の構造・関係がそのまま PassFragment とそのダイアログに使えます。 このような、 A と B の関係が B と C の関係にも使えるような構造という、コードレベルと違う "共通化" の視点で考えることもあります。
jimbe

2024/03/13 07:50

また、そもそも扱っているデータがフラグメント間で受け渡しながら使用するオブジェクトとしては大き過ぎるかもしれません。 データが RDB に入っているとすると、 BudgetName の入っているテーブルとそれに紐づく 5 つのリストのテーブルは(正規化しているとすれば)別となるはずです。 それらを一つに纏めた Budget オブジェクトようなものをリストにして RecieveFragment で表示して、 PassFragment へもその Budget オブジェクトごと渡して…というよりも、各テーブルの外部キーであろう BudgetId を PassFragment に渡して、 PassFragment で各リストを読んだり更新したりというほうが良いようにも思えてきます。
liptoon

2024/03/13 08:02

> ダイアログ上からダイアログの表示も出来ますし、リストビューも載せられると思いますが… そうなのですね、調べ不足ですみません; 今詰まっているところが解決して時間ができたら試してみようと思います! > A と B の関係が B と C の関係にも使えるような構造という、コードレベルと違う "共通化" の視点で考えることもあります なるほど、そのような考え方もできるのですね。全体像が見えておらずそこまで考慮できていなかったです。 ーーーーーーーーーーーーーーーーーーーーーーーーーーー この度は(前回もですが)とても分かりやすく教えていただき大変助かりました!色々と質問してしまいお手数おかけしました。 まだまだアプリ完成まで問題が山積みなのでこちらでまた質問させていただくかもしれません。お手すきの際にはご教授いただけると幸いです。ありがとうございました。
liptoon

2024/03/13 08:07

閉めてしまったのですが、すみません、最後のコメントを読めていませんでした・・・ DBを理解できていないのでコメントの解読します、少々お時間頂戴いたします・・・!
liptoon

2024/03/13 09:17 編集

実はデータ管理についても現在悩んでいまして、、、質問の方にPassFragmentの詳細を追記させていただきました。 すみません、前提としてなのですが、私の見通しがかなり甘くデータ管理について考慮できておらず、そもそも今日、アプリ終了後値を保持するのにSQLiteを使うことを知りました。(今回取得する値はアプリ終了後も保持したいと思っています)今疑問に思っていることを書かせていただくと ①Room、DBを使用するならViewModelでの保存は不要になるのか ②> データが RDB に入っているとすると、 BudgetName の入っているテーブルとそれに紐づく 5 つのリストのテーブルは(正規化しているとすれば)別となるはずです。 必要なテーブルは id、予算名、期間、合計予算 で一つ、 id、項目、項目別予算 で一つの計二つと考えていましたが、私の認識が誤っているでしょうか? ③> 各テーブルの外部キーであろう BudgetId を PassFragment に渡して、 PassFragment で各リストを読んだり更新したりというほうが良いようにも思えてきます。 重ね重ね私の説明不足で申し訳ないです。アプリ全体でしたいことを書かせていただきます。 ReceiveFragmentでは予算リスト(ここでは各行に予算名と期間、合計予算を表示)を表示し、リストをタップで他のFragmentに遷移したいと考えています。 その遷移先Fragmentにおいて、PassFragmentで取得した予算名、期間、合計予算、項目、項目別予算を表示、ユーザーが使用した金額を入力することで予算から引かれた残予算が出るようにしたいと思っています。さらに最終的には残予算をスマホのホーム画面へウィジェットで表示するようにしたいと考えています。(ウィジェットタップで残予算画面へ遷移) 元々RecieveFragmentではコンテキストメニューでListの削除、変更ができるようにしたいと思っていたのですが、それが > PassFragment で各リストを読んだり更新したり ということになるのでしょうか・・・? 以上長々とすみません、お手すきのときで構いませんのでどうぞよろしくお願いいたします。
jimbe

2024/03/13 09:58 編集

Room は android 搭載の RDB である SQLite を使いやすく(?)するためのライブラリです。実際に中で動いているのは SQLite です。 ViewModel と RDB の関係は排他ではありません。大まかな構造としては View <-> ViewModel <-> RDB として、View からの要求を ViewModel が RDB を用いて実現するとか、 RDB のデータを ViewModel が表示する形に変換して View に渡すとかという連携関係になります。 5 つのリストというのがどのようなモノなのかは全く私の想像だけですので、お考えのものと違うならやり方もまた異なるでしょう。 どのようなデータが存在し各データがどのような関係を持つのかを判断し、どのようにそれをリレーショナルデータベースのテーブル・レコードで表現するのかというのは、プログラミングとまた違うデータデース設計の世界です。 ざっくりはこんな感じ↓ [データベース(RDB)設計の進め方!] https://qiita.com/ryota_i/items/294281b57cc9783bf2c1 流れで内容が大きくなってしまって本件とどこまで関連するものとして展開するのが良いのかが難しいです。 もしアプリの全体構造でどうするのが良いかということなら、 Q&A でなく意見交換で「このようなデータ群からこのような表示をするにはどのような構造にするか」という意見を求めてみるのが良いかもしれません。
liptoon

2024/03/13 13:14

たしかに、元々の質問の内容とずれてきましたのでとりあえずここはいったん終わりにしてデータベース設計に関してはまた別で質問させていただこうと思います。 ありがとうございました。
jimbe

2024/03/13 18:27

すいません1つだけ確認というか分からない部分を質問させてください。 >必要なテーブルは id、予算名、期間、合計予算 で一つ、 id、項目、項目別予算 で一つの計二つと考えていました ということですが、この2つのテーブルから「5つのリスト」ということは、項目は 5 つ決まっていて(食費、医療費と後3つ?)それ毎のリストになっているという感じでしょうか。
liptoon

2024/03/13 21:18 編集

リスト5つと言っていたのは予算名、期間、合計予算、項目、項目別予算でした。項目と項目別予算はリストが入れ子になっている状態です。 項目と項目別予算はユーザーが自由に入力でき、id、項目、項目別予算でテーブルを作りたいとおもっていました。・・・そういう使い方はできないですかね・・・? 本来ならあらかじめデータベース設計なるものをしてからプログラミングするのですね・・・まずは動かしてみようと思って作り始めてしまいました。なんというか巻き込んでしまって申し訳ないです;
jimbe

2024/03/13 21:51

有難う御座います。 成る程。リストを言われているのはビュー(TextView や EditText、 ListView 等)のことですね。 リストというと行を並べてスクロールする ListView が想像されるので、 5 つも ListView があるなんてどれだけデータがあるのだろうと思って『大きすぎるかも』と書いてしまいました^^; 項目は自由に入力出来るんですね。 ListView なら 10 と言わず 100 でも 1000 でも扱えますし、 RDB の表にも幾らでも入ります。 仕事の場合、入出力の仕様は顧客や協力会社等外部と共有しないとなりませんので、画面仕様やらデータベース仕様やらを決めてからプログラミングに入るという順になります。 しかし個人で自由に作っているのなら、別にどのような順でも構わないでしょう。
liptoon

2024/03/13 22:11

そういうことだったのですね!伝えるというのは難しいですね; いくらでも入るというので安心しました。 ありがとうございます。とりあえず形にすることを目標に試行錯誤していきたいと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.47%

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

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

質問する

関連した質問