在片段中保存和恢复列表视图(实时数据)



我正在尝试制作一个Todo应用程序。我已经成功地在片段中实现了实时数据和列表视图(片段是项目快速入门模板中的默认值(。我无法解决的问题是保存这些待办事项,以便在再次启动应用程序时它们仍然存在。

堆栈和博客上浏览了大量答案,并阅读了整个生命周期,但我仍然不明白。我终于放弃了,这就是我最终用 atm 得到的(不起作用的(代码:

FragmentLifeCycle保存列表的">状态">ToDoThings

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()")
    }
}

抛出空指针:

'android.os.Parcelable android.widget.ListView.onSaveInstanceState((' on a null object reference

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.kt 的样子:

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))
    }
}

如果我理解正确,您需要向应用程序添加一个持久性层。加载列表视图时尝试使用会议室数据库。SavedInstanceState 有一些限制,它不应用于保存大量数据或复杂对象。

安卓持久性

房间数据库

希望这有帮助。

如果需要保存用户在 listView 中的位置,请仅将 Int 保存在fragment的方法onSaveInstanceState()上的捆绑包中。如果要将数据保存在listView中,则不需要这样做,因为Android已经这样做了,您只需将loadData(初始化数据并为listView设置适配器的代码(放入onActivityCreated中,然后只需恢复onViewStateRestored()中的位置即可。

最新更新