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

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

ただいまの
回答率

88.92%

FragmentStatePagerAdapterのgetPageTitleがNULLになる

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 181

sanapapa

score 30

FragmentStatePagerAdapterのnotifyDataSetChanged()の使い方がおかしいのか、getPageTitleが最新分のみNULLになります。。

ViewPagerのAdapterとしてFragmentStatePagerAdapterを使用しています。
FragmentStatePagerAdapterにはFragmentのArrayListを使用しており、
モデルはViewModelを、双方向バインドのため更新用にMutableLiveData、参照用にLiveDataで実装しております。(ここも使用方法に自信がないです)

実現したい処理は下記の通りで
①とあるEditTextのOnClickイベントで入力値に基づきViewModelを作成
②作成されたViewModelをFragmentのArrayListにadd
③AdapterのnotifyDataSetChangedでアダプターへ更新通知
④ViewPagerのsetCurrentItemを使って作成された最新のページへジャンプ
というものです。

この処理で作成された最新のViewModelのメンバ変数の一つを使ってFragmentStatePagerAdapterのoverrideされたgetPageTitleで返しているのですが、その中で最新のページタイトルのみNullになってしまいます。

以下稚拙なソースですが貼り付けます。(長くなるので確実に不要であろう処理は削除して貼ります。)

メインのFragment①のFragmentStatePagerAdapter作成時

entryViewPager.setPageTransformer(true, ZoomOutPageTransformer())
adapter = EntryPagerAdapter(childFragmentManager, fragmentList)
entryViewPager.adapter = adapter

メインのFragment①のOnClickイベント内抜粋

// ページのバインドアイテムを作成
val mEntry = EntryViewModel()
mEntry.setPalletNo(fragmentList.count() + 1)
// ページに追加
fragmentList.add(EntryDataFragment(mEntry))
// 反映
adapter.notifyDataSetChanged()
// 新しいページに遷移
entryViewPager.setCurrentItem(fragmentList.count() - 1, true)

各ページのフラグメント(EntryDataFragment)のバインド定義部

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = EntryDataFragmentBinding.inflate(inflater, container, false)
        binding.entry = mEntry
        binding.lifecycleOwner = this
        return binding.root
    }

ViewModel(EntryViewModel)

class EntryViewModel : ViewModel() {

    private var mPalletNo = MutableLiveData<Int>()
    var palletNo: LiveData<Int> = mPalletNo
    fun setPalletNo(value: Int) { mPalletNo.postValue(value) }
    fun getPalletNo(): Int { return mPalletNo.value!! }

}

FragmentStatePagerAdapter(EntryPagerAdapter)

class EntryPagerAdapter(
    private val fm: FragmentManager,
    private val fragmentList: List<EntryDataFragment>
) :
    FragmentStatePagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {

    /**
     * Return the Fragment associated with a specified position.
     */
    override fun getItem(position: Int): Fragment {
        return fragmentList[position]
    }

    /**
     * Return the number of views available.
     */
    override fun getCount(): Int {
        return fragmentList.size
    }

    override fun getPageTitle(position: Int): CharSequence? {
        return fragmentList[position].mEntry.palletNo.value.toString()
    }

    override fun getItemPosition(`object`: Any): Int {
        return PagerAdapter.POSITION_NONE
    }

}

上記ソースのうち、FragmentStatePagerAdapterのgetPageTitleでデバッグしたところ最新のページのみnullを返却しています。
つまり、
1ページ目作成時:1ページ目=null
2ページ目作成時:1ページ目=値あり、2ページ目=null
3ページ目作成時:1ページ目=値あり、2ページ目=値あり、3ページ目=null
といった状態です。

下記がgetPageTitleのデバッグ時のスクショです。
最新以外の場合(正常に取得できている)
イメージ説明
最新の場合(正常に取得できていない)
イメージ説明
上のスクショの通り、正常に取得できていない場合はmPendingDataに値が入っています。
(最新のものだけ情報が保留されている?)

当然TabLayoutのタブタイトルも同様にNULLと表示されてしまいます。
何か勘づかれた方、何卒ご指摘のほどよろしくお願いします。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

checkベストアンサー

0

ざっと見た感じ、EntryViewModel::setPalletNoにて、MutableLiveData::postValueで値をpostしているのが原因ですね。postはすぐに値を更新するわけではなく次のUIスレッドのループで値が更新されるよう処理を後回しにするものですので、getPageTitleの時点ではpalletNoの値はまだnullのままになっています。

もしかしたらLiveDataを使用することでgetPageTitleの値も自動的に更新されるとお考えかもしれませんが、LiveDataの値監視にはobserveメソッドを使う必要がありますから、現状のソースでは自動更新はされません。

解決方法としては、

  1. LiveDataを使うのをやめる(値の更新が必要でない場合)
  2. LiveDataをobserveしてEntryPagerAdapterを更新する

のいずれかになると思います。


余談

ViewModelはAndroid Architecture ComponentsのViewModelを利用していますか?だとしたら、ViewModelProvider(もしくはktx)を経由せずにViewModelを生成するコードはなるべく避けた方がよいです。(以下のコード)

val mEntry = EntryViewModel()
EntryDataFragment(mEntry)

ViewModelが本来提供する機能を使えないことになるので、予期せぬ挙動の原因となりえます。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2020/08/07 00:24

    遅くなり申し訳ございません、仰る通りInt型に変えてしまえば正常に動作いたしました!
    ありがとうございました。

    キャンセル

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

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

関連した質問

同じタグがついた質問を見る