既存のアプリに Jetpackのアーキテクチャコンポーネントを使用したナビゲーション を実装しようとしています。
メインフラグメント(ListFragment
)がアイテムのリストである単一のアクティビティアプリがあります。現在、ユーザーがリスト項目をタップすると、2番目のフラグメントがfragmentTransaction.add(R.id.main, detailFragment)
によってスタックに追加されます。したがって、戻るボタンを押すと、DetailFragment
が切り離され、ListFragment
が再び表示されます。
ナビゲーションアーキテクチャでは、これは自動的に処理されます。新しいフラグメントを追加する代わりに、それは replaced なので、フラグメントビューが破棄され、onDestroyView()
が呼び出され、ビューを再作成するために戻るボタンが押されたときにonCreateView()
が呼び出されます。
これは LiveData と ViewModel で使用して、必要以上のメモリの使用を回避するのに適したパターンであることを理解していますが、私の場合、リストのレイアウトが複雑で、また、リストのスクロール位置を保存して、ユーザーがフラグメントを残したのと同じ位置まで再度スクロールする必要があるためです。それは可能ですが、より良い方法が存在するはずです。
フラグメントのプライベートフィールドにビューを「保存」し、onCreateView()
で既に使用している場合は再利用しようとしましたが、アンチパターンのようです。
private View view = null;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (view == null) {
view = inflater.inflate(R.layout.fragment_list, container, false);
//...
}
return view;
}
レイアウトの再膨張を回避するための、他のよりエレガントな方法はありますか?
Googleのイアンレイクは、ビューを変数に保存できると返信しましたと代わりに新しいレイアウトを拡張するを返すだけのインスタンスonCreateView()
の事前保存されたビュー
出典: https://Twitter.com/ianhlake/status/1103522856535638016
Leakcanaryはこれをリークとして示す可能性がありますが、false positive..
以下の実装を通じてフラグメントの永続的なビューを持つことができます
BaseFragment
open class BaseFragment : Fragment(){
var hasInitializedRootView = false
private var rootView: View? = null
fun getPersistentView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?, layout: Int): View? {
if (rootView == null) {
// Inflate the layout for this fragment
rootView = inflater?.inflate(layout,container,false)
} else {
// Do not inflate the layout again.
// The returned View of onCreateView will be added into the fragment.
// However it is not allowed to be added twice even if the parent is same.
// So we must remove rootView from the existing parent view group
// (it will be added back).
(rootView?.getParent() as? ViewGroup)?.removeView(rootView)
}
return rootView
}
}
MainFragment
class MainFragment : BaseFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return getPersistentView(inflater, container, savedInstanceState, R.layout.content_main)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (!hasInitializedRootView) {
hasInitializedRootView = true
setListeners()
loadViews()
}
}
}
私はこのようにしてみましたが、うまくいきました。
ViewModel
by navGraphViewModels
(Live on Navigationスコープ)ViewModel
に保存します// fragment.kt
private val vm by navGraphViewModels<VM>(R.id.nav_graph) { vmFactory }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Restore state
vm.state?.let {
(recycler.layoutManager as GridLayoutManager).onRestoreInstanceState(it)
}
}
override fun onPause() {
super.onPause()
// Store state
vm.state = (recycler.layoutManager as GridLayoutManager).onSaveInstanceState()
}
// vm.kt
var state:Parcelable? = null
自分の目的地を置くだけ。
<action
Android:id="@+id/action_piecesReferenceCount_self"
app:destination="@id/piecesReferenceCount" />
Navigation.findNavController(myview).navigate(R.id.action_piecesReferenceCount_self);