在Fragments中保存和恢复ListView(livedata)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在Fragments中保存和恢复ListView(livedata)相关的知识,希望对你有一定的参考价值。
我正在尝试制作Todo应用程序。我已经成功地在片段中实现了livesata和listview(片段默认来自项目快速入门模板)。我无法解决的问题是保存那些待办事项,以便他们在再次启动应用程序时仍然存在。
在堆栈和博客上浏览了大量的答案并阅读了整个生命周期,但我仍然没有得到它。我终于放弃了,这就是我最终得到的代码(不工作):
FragmentLifeCycle保存listOfToDoThings的“状态”
class FragmentLifeCycle : Fragment() {
private var state: Parcelable? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("Lifecycle Info", "onCreate()")
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
Log.d("Lifecycle Info", "onCreateView()")
return inflater.inflate(R.layout.activity_main, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
Log.d("Lifecycle Info", "onActivityCreated()")
}
override fun onResume() {
super.onResume()
if (state != null) {
Log.i("Lifecycle Info", "onResume finally works")
listOfToDoThings.onRestoreInstanceState(state)
}
Log.d("Lifecycle Info", "onResume()")
}
override fun onPause() {
state = listOfToDoThings.onSaveInstanceState()
super.onPause()
Log.d("Lifecycle Info", "onStop()")
}
}
抛出nullpointer:
空对象引用上的'android.os.Parcelable android.widget.ListView.onSaveInstanceState()'
Main_Activity清除了大量评论不起作用的解决方案:
class MainActivity : AppCompatActivity(){
private var mSectionsPagerAdapter: SectionsPagerAdapter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
// Create the adapter that will return a fragment for each of the three
// primary sections of the activity.
mSectionsPagerAdapter = SectionsPagerAdapter(supportFragmentManager)
// Set up the ViewPager with the sections adapter.
container.adapter = mSectionsPagerAdapter
val fragmentManager = this.supportFragmentManager
val fragmentTransaction = fragmentManager.beginTransaction()
val fragmentLifeCycle = FragmentLifeCycle()
fragmentTransaction.add(R.id.container, fragmentLifeCycle, "Lifecycle Fragment")
fragmentTransaction.commit()
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.menu_main, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
val id = item.itemId
if (id == R.id.action_settings) {
return true
}
return super.onOptionsItemSelected(item)
}
/**
* A [FragmentPagerAdapter] that returns a fragment corresponding to
* one of the sections/tabs/pages.
*/
inner class SectionsPagerAdapter(fm: FragmentManager) : FragmentPagerAdapter(fm) {
override fun getItem(position: Int): Fragment {
// getItem is called to instantiate the fragment for the given page.
// Return a PlaceholderFragment (defined as a static inner class below).
return PlaceholderFragment.newInstance(position + 1)
}
override fun getCount(): Int {
// Show 3 total pages.
return 4
}
}
/**
* A placeholder fragment containing a simple view.
*/
class PlaceholderFragment : Fragment(), Renderer<TodoModel> {
private lateinit var store: TodoStore
override fun render(model: LiveData<TodoModel>) {
model.observe(this, Observer { newState ->
listOfToDoThings.adapter = TodoAdapter(requireContext(), newState?.todos ?: listOf())
})
}
private fun openDialog() {
val options = resources.getStringArray(R.array.filter_options).asList()
requireContext().selector(getString(R.string.filter_title), options) { _, i ->
val visible = when (i) {
1 -> Visibility.Active()
2 -> Visibility.Completed()
else -> Visibility.All()
}
store.dispatch(SetVisibility(visible))
}
}
private val mapStateToProps = Function<TodoModel, TodoModel> {
val keep: (Todo) -> Boolean = when(it.visibility) {
is Visibility.All -> {_ -> true}
is Visibility.Active -> {t: Todo -> !t.status}
is Visibility.Completed -> {t: Todo -> t.status}
}
return@Function it.copy(todos = it.todos.filter { keep(it) })
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val rootView = inflater.inflate(R.layout.fragment_main, container, false)
rootView.section_label.text = getString(R.string.section_format, arguments?.getInt(ARG_SECTION_NUMBER))
@SuppressLint("SetTextI18n")
when(arguments?.getInt(ARG_SECTION_NUMBER)) {
1 -> rootView.section_name.text = "Daily Life"
2 -> rootView.section_name.text = "Work and College"
3 -> rootView.section_name.text = "Visits"
4 -> rootView.section_name.text = "Shop"
}
store = ViewModelProviders.of(this).get(TodoStore::class.java)
store.subscribe(this, mapStateToProps)
// Add task and then reset editText component
rootView.addNewToDo.setOnClickListener {
store.dispatch(AddTodo(editText.text.toString()))
editText.text = null
}
rootView.filter.setOnClickListener{ openDialog() }
// Press to change status of task
rootView.listOfToDoThings.adapter = TodoAdapter(requireContext(), listOf())
rootView.listOfToDoThings.setOnItemClickListener { _, _, _, id ->
store.dispatch(ToggleTodo(id))
}
// Hold to delete task
rootView.listOfToDoThings.setOnItemLongClickListener { _, _, _, id ->
store.dispatch(RemoveTodo(id))
true
}
return rootView
}
companion object {
/**
* The fragment argument representing the section number for this
* fragment.
*/
private val ARG_SECTION_NUMBER = "section_number"
/**
* Returns a new instance of this fragment for the given section
* number.
*/
fun newInstance(sectionNumber: Int): PlaceholderFragment {
val fragment = PlaceholderFragment()
val args = Bundle()
args.putInt(ARG_SECTION_NUMBER, sectionNumber)
fragment.arguments = args
return fragment
}
}
}
}
不确定它是否有用,但这就是TodoStore.it的样子:
class TodoStore : Store<TodoModel>, ViewModel(){
private val state: MutableLiveData<TodoModel> = MutableLiveData()
// Start with all tasks visible regardless of previous state
private val initState = TodoModel(listOf(), Visibility.All())
override fun dispatch(action: Action) {
state.value = reduce(state.value, action)
}
private fun reduce(state: TodoModel?, action: Action): TodoModel {
val newState= state ?: initState
return when(action){
// Adds stuff upon creating new todo
is AddTodo -> newState.copy(
todos = newState.todos.toMutableList().apply {
add(Todo(action.text, action.id))
}
)
is ToggleTodo -> newState.copy(
todos = newState.todos.map {
if (it.id == action.id) {
it.copy(status = !it.status)
} else it
} as MutableList<Todo>
)
is SetVisibility -> newState.copy(
visibility = action.visibility
)
is RemoveTodo -> newState.copy(
todos = newState.todos.filter {
it.id != action.id
} as MutableList<Todo>
)
}
}
override fun subscribe(renderer: Renderer<TodoModel>, func: Function<TodoModel, TodoModel>) {
renderer.render(Transformations.map(state, func))
}
}
如果我理解正确,您需要在应用程序中添加持久层。加载ListView时尝试使用Room Database。 SavedInstanceState有一些限制,不应该用于保存大量数据或复杂对象。
希望这有帮助。
如果您需要保存用户在listView中的位置,请在onSaveInstanceState()
的fragment
方法中仅保存Int中的Int。如果你想在listView中保存数据,你不需要这样做,因为Android已经这样做了,你只需要在onActivityCreated
中放入loadData(你的代码初始化数据并将适配器设置到listView),只是恢复在onViewStateRestored()
的位置。
以上是关于在Fragments中保存和恢复ListView(livedata)的主要内容,如果未能解决你的问题,请参考以下文章
使用 Fragments 和 ViewPager 一段时间后,Android 应用程序崩溃