#android #android-fragments #viewmodel #android-dialogfragment #android-viewmodel
#Android #android-фрагменты #viewmodel #android-dialogfragment #android-viewmodel
Вопрос:
Я все еще пытаюсь разобраться в работе с viewmodels и немного смущен теперь, когда у меня также есть элементы диалога и recyclerview, но постараюсь быть максимально понятным, если смогу получить любую помощь, пожалуйста.
У меня есть диалоговое окно с элементами, которые, когда один из них выбран и закрыт, должны возвращать данные в мой вызывающий фрагмент, чтобы выбранный элемент отображался в этом представлении.
Однако, как только элемент выбран и диалоговое окно закрыто, я не вижу новый выбранный элемент как тот, который отображается в пользовательском интерфейсе, но вместо этого остается старый элемент. (При первом запуске фрагмента отображается элемент, который установлен как выбранный в моем списке. Выбранное значение сначала жестко закодировано, но обновляется при нажатии на элемент, и я вижу, что обновление произошло при отладке наблюдателя viewmodel внутри метода onDismiss для диалога).
Я работаю над этим пару часов и попробовал несколько разных вещей, таких как вызов viewmodel внутри onResume или onDismiss и изменение viewmodel для инициализации by by activityViewModels()
в соответствии с этим сообщением, но пока ничего из этого не сработало, и я думаю, что я застрял на данный момент. Ниже приведена моя самая последняя версия кода.
class CovidCheckInFragment : Fragment(R.layout.fragment_covid_check_in) {
var navController: NavController? = null
private val model: MainViewModel by activityViewModels()
@RequiresApi(Build.VERSION_CODES.M)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
navController = Navigation.findNavController(view)
model.userMutableLiveData.observe(viewLifecycleOwner, Observer<Any?> { list ->
if (list != null)
(list as Iterable<*>).map {
if ((it as ModelDialogOption).selected == true) {
tvHeader.text = it.title
}
}
})
}
}
..
class MyDialogFragment : DialogFragment(), RecyclerDialogOptionsItem.AdapterListener {
private val viewModel: MainViewModel by activityViewModels()
private lateinit var adapter: GroupAdapter<GroupieViewHolder>
var selectedPosition = -1
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NO_TITLE, R.style.AppTheme_Dialog_Custom)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_dialog, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
rvOptions.layoutManager = LinearLayoutManager(activity)
adapter = GroupAdapter()
rvOptions.adapter = adapter
ivClose.setOnClickListener {
this.dismiss()
}
initViewModel()
}
private fun initViewModel() {
viewModel.userMutableLiveData.observe(this, Observer { list ->
for (i in list!!) {
adapter.add(
RecyclerDialogOptionsItem(
this@MyDialogFragment,
i,
this@MyDialogFragment
)
)
}
})
}
override fun onClickItem(position: Int) {
selectedPosition = position
adapter.notifyDataSetChanged()
Log.i("clicked", "position: $position")
}
}
..
class MainViewModel : ViewModel() {
private var list: ArrayList<ModelDialogOption>? = null
val userMutableLiveData: MutableLiveData<ArrayList<ModelDialogOption>?> = MutableLiveData()
init {
populateList()
userMutableLiveData.value = list!!
}
private fun populateList() {
list = ArrayList()
list!!.add(ModelDialogOption("Prefer not to say", false))
list!!.add(ModelDialogOption("16-39", false))
list!!.add(ModelDialogOption("40-59", true))
list!!.add(ModelDialogOption("60 ", false))
}
}
..
class RecyclerDialogOptionsItem(
private val fragment: MyDialogFragment,
private val modelDialogOption: ModelDialogOption,
private val adapterListener: AdapterListener
) : Item<GroupieViewHolder>() {
companion object {
var clickListener: AdapterListener? = null
}
override fun bind(viewHolder: GroupieViewHolder, position: Int) {
viewHolder.apply {
with(viewHolder.itemView) {
tvTitle.text = modelDialogOption.title
clickListener = adapterListener
if (fragment.selectedPosition == position) {
ivChecked.visible()
modelDialogOption.selected = true
} else {
ivChecked.invisible()
modelDialogOption.selected = false
}
itemView.setOnClickListener {
clickListener?.onClickItem(adapterPosition)
}
}
}
}
override fun getLayout() = R.layout.rv_options_item_row
interface AdapterListener {
fun onClickItem(position: Int)
}
}
Большое вам спасибо.
Комментарии:
1. Где ваш код активности? В вашей деятельности сначала объявите ViewModel. Тогда CovidCheckInFragment и MyDialogFragment получат ссылку на ViewModel, поскольку вы используете activityViewModels(). Это означает, что CovidCheckInFragment и MyDialogFragment будут использовать одну и ту же виртуальную машину для совместного использования и прослушивания данных
2. Привет! Извините, не знал, что нужно включить код и в действие? Есть ли какой-либо способ избежать этого? Я думаю, что я уже достаточно запутался, управляя этими четырьмя классами..
3. Я обновил сообщение некоторой упрощенной версией для своей деятельности. Спасибо.
4. Я думаю, что теперь я смог заставить его работать, заменив activityViewModels на this
private val viewModel: MainViewModel by viewModels( { requireParentFragment() } )
как для родительского фрагмента, так и для диалогового.5. Теперь просто нужно выяснить, как просмотреть это обновление, как только диалоговое окно будет закрыто, потому что на данный момент мне требуется оставить фрагмент и вернуться к нему, чтобы произошли изменения.
Ответ №1:
Ваша основная модель представления должна быть такой
class MainViewModel : ViewModel() {
private var list: ArrayList<ModelDialogOption>? = null
val userMutableLiveData = MutableLiveData<ArrayList<ModelDialogOption>>()
init {
populateList()
userMutableLiveData.value = list!!
}
private fun populateList() {
list = ArrayList()
list!!.add(ModelDialogOption("Prefer not to say", false))
list!!.add(ModelDialogOption("16-39", false))
list!!.add(ModelDialogOption("40-59", true))
list!!.add(ModelDialogOption("60 ", false))
}
fun updateItem(position:Int){
val itemToUpdate = list!!.get(position)
itemToUpdate.selected = !itemToUpdate.selected!!
list!![position] = itemToUpdate
}
fun flushItems(){
userMutableLiveData.value = list!!
}
}
Тогда из MyDialogFragment должно быть так.
class MyDialogFragment : DialogFragment(), RecyclerDialogOptionsItem.AdapterListener {
private val viewModel: MainViewModel by activityViewModels()
private lateinit var adapter: GroupAdapter<GroupieViewHolder>
var selectedPosition = -1
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NO_TITLE, R.style.AppTheme_Dialog_Custom)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_dialog, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
rvOptions.layoutManager = LinearLayoutManager(activity)
adapter = GroupAdapter()
rvOptions.adapter = adapter
ivClose.setOnClickListener {
this.dismiss()
}
initViewModel()
}
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
viewModel.flushItems()
}
private fun initViewModel() {
viewModel.userMutableLiveData.observe(this, Observer { list ->
for (i in list!!) {
adapter.add(
RecyclerDialogOptionsItem(
this@MyDialogFragment,
i,
this@MyDialogFragment
)
)
}
})
}
override fun onClickItem(position: Int) {
selectedPosition = position
adapter.notifyDataSetChanged()
viewModel.updateItem(position)
Log.i("clicked", "position: $position")
}
}