0

Items in one of my dropdown lists are starting to double themselves after orientation change for some reason.

Here are two pictures: one with the bug (after changing to horizontal and back) and one without it.

Bug picture How it should be

Here's my code:

Activity class:

class ScheduleActivity : AppCompatActivity(), GroupPickerActivity {
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_lessons_list)

    val groupPickerFragment = GroupPickerFragment()

    fragmentManager.beginTransaction()
            .add(R.id.central_frame, groupPickerFragment)
            .commit()
}

override fun finishPicking() {
    Toast.makeText(applicationContext, "Finished picking", Toast.LENGTH_SHORT).show()
}
}

Fragment class:

class GroupPickerFragment : MvpFragment(), GroupPickerView {

    val TAG = "GroupPickerFragment"

    @InjectPresenter
    lateinit var groupPickerPresenter: GroupPickerPresenter

    var noCitySelected = true
    var noSchoolSelected = true
    var noGroupSelected = true

    override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {

        return inflater!!.inflate(R.layout.fragment_group_picker, container, false)
    }


    override fun onStart() {
        super.onStart()

        groupPickerPresenter.start(activity.applicationContext)

        makeCitySpinnerClickable()
    }

    private fun makeCitySpinnerClickable() {
        /*
        * Set onItemSelectedListener on City picker spinner
        * */

        pick_your_city_tv.visibility = View.VISIBLE
        city_picker_spinner.visibility = View.VISIBLE

        city_picker_spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
            override fun onItemSelected(parentView: AdapterView<*>, selectedItemView: View?, pos: Int, id: Long) {
                if (!noCitySelected) {
                    val selectedItem = parentView.getItemAtPosition(pos)

                    if (pos != 0) {
                        onCitySelected(selectedItem.toString())
                    } else {
                        onCreateNewCitySelected()
                    }
                } else {
                    noCitySelected = false
                }

            }

            override fun onNothingSelected(parentView: AdapterView<*>?) {}
        }
    }

    private fun onCreateNewCitySelected() {

        val textbox = EditText(activity.applicationContext)

        val alertDialogBuilder = AlertDialog.Builder(activity,
                R.style.AppTheme_myDialogueWindow)

        alertDialogBuilder.setTitle(R.string.enter_new_city_name)
                .setCancelable(true)
                .setView(textbox)
                .setPositiveButton(R.string.create_new, { _, _ ->
                    val newCityName = textbox.text.toString()

                    if (newCityName != "") {
                        groupPickerPresenter.createNewCity(newCityName)
                    } else {
                        Toast.makeText(activity.applicationContext,
                                R.string.city_name_cannot_be_empty,
                                Toast.LENGTH_SHORT).show()
                    }
                })
                .show()
    }

    override fun onCitiesDownloaded(cityList: ArrayAdapter<String>) {
        val spinner = city_picker_spinner

        val adapter = cityList
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)

        spinner.adapter = adapter

    }

    fun onCitySelected(cityName: String) {
        Log.v(TAG, "Get cities by name")

        groupPickerPresenter.onCitySelected(cityName)
        makeSchoolSpinnerClickable()

    }

    override fun onCityAdded(newCityId: String) {
        groupPickerPresenter.start(activity.applicationContext)
    }

    override fun onSchoolsDownloaded(schoolsList: ArrayAdapter<String>) {
        val spinner = school_picker_spinner

        val adapter = schoolsList
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)

        spinner.adapter = adapter

    }

    private fun makeSchoolSpinnerClickable() {
        /*
        * Set onItemSelectedListener on School picker spinner
        * */

        pick_your_school_tv.visibility = View.VISIBLE
        school_picker_spinner.visibility = View.VISIBLE

        school_picker_spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
            override fun onItemSelected(parentView: AdapterView<*>, selectedItemView: View?, pos: Int, id: Long) {
                if (!noSchoolSelected) {
                    val selectedItem = parentView.getItemAtPosition(pos)

                    if (pos != 0) {
                        onSchoolSelected(selectedItem.toString())
                    } else {
                        onCreateNewSchoolSelected()
                    }
                } else {
                    noSchoolSelected = false
                }

            }

            override fun onNothingSelected(parentView: AdapterView<*>?) {}
        }
    }

    private fun onCreateNewSchoolSelected() {
        val textbox = EditText(activity.applicationContext)

        val alertDialogBuilder = AlertDialog.Builder(activity,
                R.style.AppTheme_myDialogueWindow)

        alertDialogBuilder.setTitle(R.string.enter_new_school_name)
                .setCancelable(true)
                .setView(textbox)
                .setPositiveButton(R.string.create_new, { _, _ ->
                    val newSchoolName = textbox.text.toString()

                    if (newSchoolName != "") {
                        val cityName = city_picker_spinner.selectedItem.toString()

                        groupPickerPresenter.createNewSchool(newSchoolName, cityName)

                    } else {
                        Toast.makeText(activity.applicationContext,
                                R.string.school_name_cannot_be_empty,
                                Toast.LENGTH_SHORT).show()
                    }
                })
                .show()
    }

    private fun onSchoolSelected(schoolName: String) {
        Log.v(TAG, "Get schools by name")

        groupPickerPresenter.onSchoolSelected(schoolName)

        makeGroupSpinnerClickable()
    }

    override fun onSchoolAdded(newCityId: String) {
        //todo Когда новая школа добавлена, надо обновить список школ
    }

    override fun onGroupsDownloaded(groupsList: ArrayAdapter<String>) {
        val spinner = group_picker_spinner

        val adapter = groupsList
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)

        spinner.adapter = adapter

    }

    private fun makeGroupSpinnerClickable() {
        /*
        * Set onItemSelectedListener on School picker spinner
        * */

        pick_your_group_tv.visibility = View.VISIBLE
        group_picker_spinner.visibility = View.VISIBLE


        group_picker_spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
            override fun onItemSelected(parentView: AdapterView<*>, selectedItemView: View?, pos: Int, id: Long) {
                if (!noGroupSelected) {
                    val selectedItem = parentView.getItemAtPosition(pos)

                    if (pos != 0) {
                        onGroupSelected(selectedItem.toString())
                    } else {
                        onCreateNewGroupSelected()
                    }
                } else {
                    noGroupSelected = false
                }

            }

            override fun onNothingSelected(parentView: AdapterView<*>?) {}
        }
    }

    private fun onGroupSelected(groupName: String) {
        groupPickerPresenter.onGroupSelected(groupName)
    }

    private fun onCreateNewGroupSelected() {
        val textbox = EditText(activity.applicationContext)

        val alertDialogBuilder = AlertDialog.Builder(activity,
                R.style.AppTheme_myDialogueWindow)

        alertDialogBuilder.setTitle(R.string.enter_new_group_name)
                .setCancelable(true)
                .setView(textbox)
                .setPositiveButton(R.string.create_new, { _, _ ->
                    val newGroupName = textbox.text.toString()

                    if (newGroupName != "") {
                        val schoolName = school_picker_spinner.selectedItem.toString()

                        groupPickerPresenter.createNewGroup(newGroupName, schoolName)

                    } else {
                        Toast.makeText(activity.applicationContext,
                                R.string.group_name_cannot_be_empty,
                                Toast.LENGTH_SHORT).show()
                    }
                })
                .show()
    }

    override fun onGroupAdded(updatedSchoolId: String) {
        //todo обновить список групп
    }

    override fun letUserContinue() {
        group_picker_next_btn.isEnabled = true
        group_picker_next_btn.setOnClickListener({
            val selectedSchoolName = school_picker_spinner.selectedItem.toString()
            val selectedGroupName = group_picker_spinner.selectedItem.toString()

            groupPickerPresenter.pick(selectedSchoolName, selectedGroupName)

            group_picker_progressbar.visibility = View.VISIBLE
        })
    }

    override fun finishPicking() {
        group_picker_progressbar.visibility = View.GONE
        (activity as GroupPickerActivity).finishPicking()
    }
}

Fragment controller class:

@InjectViewState
class GroupPickerPresenter : MvpPresenter<GroupPickerView>() {
    val TAG = "GroupPickerPresenter"

    val cityModel = CityFirestoreModel()
    val schoolModel = SchoolFirestoreModel()

    lateinit var context: Context

    init {

    }

    fun start(context: Context) {
        this.context = context

        getCities()
    }

    private fun getCities() {
        cityModel.getAllCities {
            val cityTitlesList = mutableListOf<String>()

            // Adding 'Create new'
            cityTitlesList.add(this.context.resources.getString(R.string.create_new))

            it.forEach {
                cityTitlesList.add(it.title)
            }

            val arrayAdapter = ArrayAdapter<String>(this.context,
                    android.R.layout.simple_spinner_item, cityTitlesList)


            viewState.onCitiesDownloaded(arrayAdapter)
        }

    }

    fun createNewCity(newCityName: String) {
        cityModel.addCity(newCityName, { newCityId ->
            if (newCityId != null)
                viewState.onCityAdded(newCityId)
        })
    }

    fun onCitySelected(cityName: String) {
        val cityModel = CityFirestoreModel()

        cityModel.getCitiesByName(cityName, {
            getSchoolsByCity(it)
        })
    }

    private fun getSchoolsByCity(cities: MutableList<City>) {
        val schoolModel = SchoolFirestoreModel()

        if (cities.size > 0) {
            val city = cities[0]

            schoolModel.getSchoolsByCityName(city.title, {
                val schoolTitlesList = mutableListOf<String>()

                // Adding 'Create new'
                schoolTitlesList.add(this.context.resources.getString(R.string.create_new))

                it.forEach {
                    schoolTitlesList.add(it.title)
                }

                val arrayAdapter = ArrayAdapter<String>(this.context,
                        android.R.layout.simple_spinner_item, schoolTitlesList)

                viewState.onSchoolsDownloaded(arrayAdapter)
            })
        } else {
            Log.e(TAG, "Cities list is empty")

            val schoolTitlesList = mutableListOf<String>()

            // Adding 'Create new'
            schoolTitlesList.add(this.context.resources.getString(R.string.create_new))

            val arrayAdapter = ArrayAdapter<String>(this.context,
                    android.R.layout.simple_spinner_item, schoolTitlesList)

            viewState.onSchoolsDownloaded(arrayAdapter)
        }

    }

    fun onSchoolSelected(schoolName: String) {
        val schoolModel = SchoolFirestoreModel()

        schoolModel.getSchoolGroups(schoolName, {
            val groupTitlesList = mutableListOf<String>()

            // Adding 'Create new'
            groupTitlesList.add(this.context.resources.getString(R.string.create_new))

            it.forEach {
                groupTitlesList.add(it.title)
            }

            val arrayAdapter = ArrayAdapter<String>(this.context,
                    android.R.layout.simple_spinner_item, groupTitlesList)

            viewState.onGroupsDownloaded(arrayAdapter)
        })
    }

    fun createNewSchool(newSchoolName: String, cityName: String) {
        val city = City()
        city.title = cityName

        val teachers = mutableListOf<Teacher>()
        val subjects = mutableListOf<Subject>()
        val groups = mutableListOf<Group>()


        schoolModel.addSchool(newSchoolName, city, teachers, subjects, groups, { newCityId ->
            if (newCityId != null)
                viewState.onSchoolAdded(newCityId)
        })
    }

    fun onGroupSelected(groupName: String) {
        viewState.letUserContinue()
    }

    fun createNewGroup(newGroupName: String, schoolName: String) {
        schoolModel.getSchoolsByName(schoolName, {
            val school = it[0] //todo сделать какую-то проверку на уникальность, чтоб не было проблем

            val group = Group()
            group.title = newGroupName

            school.groups.add(group)

            schoolModel.updateSchool(school.school_id, school, {updatedSchoolId ->
                if (updatedSchoolId != null) {
                    viewState.onGroupAdded(updatedSchoolId)
                }
            })
        })
    }

    fun pick(schoolName: String, groupName: String) {
        val schoolModel = SchoolFirestoreModel()

        schoolModel.getSchoolGroup(schoolName, groupName, {group ->
            if (group != null) {
                val userFirestoreModel = UserFirestoreModel()
                val currentUserId = FirebaseAuth.getInstance().currentUser!!.uid
                userFirestoreModel.setGroup(currentUserId, group, {
                    viewState.finishPicking()
                })


            }
        })

    }
}

Before that I tried to use Fragment instead of FrameLayout in my activity xml file, but when I was using .commit() on fragmentManager, my application crashed on orientation change and also showed the same bug as here every time I entered it. But everything was okay when I didn't call .commit(). Orientation change worked fine (besides the fact it was reseting chosen values) and there was no item doubling.

My former activity code:

class ScheduleActivity : AppCompatActivity(), GroupPickerActivity {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_lessons_list)

        val groupPickerFragment = GroupPickerFragment()

        fragmentManager.beginTransaction()
                .replace(R.id.group_picker_fragment, groupPickerFragment)
    }
} 
Filipp Maksimov
  • 97
  • 1
  • 1
  • 5

1 Answers1

3

My guess is when you change your orientation you are adding same fragment twice; In your code

fragmentManager.beginTransaction()
        .add(R.id.central_frame, groupPickerFragment)
        .commit()

change the add method with replace like this

fragmentManager.beginTransaction()
        .replace(R.id.central_frame, groupPickerFragment)
        .commit()
Ashik
  • 1,035
  • 12
  • 15
  • Thanks, it cured it! Do you have any idea though why `.commit()` even on `.replace()` method would crash the app on orientation change in my previous code? (I added some old code to the question) – Filipp Maksimov Mar 28 '18 at 17:20
  • Best solution there is wrap this code in `if (savedInstanceState == null) {...}` – senneco Apr 04 '18 at 06:45