Сохранение и восстановление ListView (livingata) во фрагментах - PullRequest
0 голосов
/ 28 марта 2019

Я пытаюсь сделать приложение Todo. Я успешно реализовал liveata и listview во фрагментах (фрагменты по умолчанию из шаблона быстрого запуска проекта). Моя проблема, которую я не могу решить, - сохранить эти задачи, чтобы они снова появлялись после запуска приложения.

Просматривал тонны ответов в стеке и блогах и читал обо всем жизненном цикле, но я до сих пор не понимаю. Я наконец сдался, и вот какой (не работающий) код я получаю с помощью atm:

FragmentLifeCycle для сохранения " состояния " из списка спискаOfToDoThings

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 ()' для ссылки на нулевой объект

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

}

Ответы [ 2 ]

1 голос
/ 28 марта 2019

Если я правильно понимаю, вам нужно добавить постоянный слой в ваше приложение.Попробуйте использовать Room Database при загрузке ListView.SavedInstanceState имеет некоторые ограничения, и его не следует использовать для сохранения большого количества данных или сложных объектов.

Постоянство Android

База данных комнаты

Надеюсь, эта помощь.

0 голосов
/ 28 марта 2019

Если вам нужно сохранить позицию, в которой находится пользователь в listView, сохраните только Int в пакете по методу onSaveInstanceState() из fragment. Если вы хотите сохранить данные внутри listView, вам не нужно делать это , потому что Android уже сделал это, вам просто нужно поместить loadData (ваш код, который инициализирует данные и устанавливает адаптер на listView) в onActivityCreated и просто восстановите позицию в onViewStateRestored().

...