Могу ли я использовать одну вместо двух моих моделей просмотра? - PullRequest
0 голосов
/ 26 марта 2020

Вот мой код:

class AboutAdapter(private val clickListener: OnItemClickListener?) : AbstractListAdapter() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AboutItemViewHolder {
        val binding: AboutBinding = DataBindingUtil
            .inflate(
                LayoutInflater.from(parent.context), R.layout.view_item_about,
                parent, false
            )
        return AboutItemViewHolder(binding = binding)
    }

    override fun onBindHolder(holder: RecyclerView.ViewHolder, position: Int, item: Any) {
        (holder as AboutItemViewHolder).bind(item as SettingsItemViewModel, listener = clickListener)
    }
}

Мой AboutFragment:

class AboutFragment : BaseFragment() {

    private lateinit var viewModel: AboutViewModel
    private lateinit var viewModelFactory: AboutViewModelFactory
    private var onItemClickListener: OnItemClickListener = object : OnItemClickListener {
        override fun onItemClick(titleName: Int) {
            when (titleName) {
                R.string.about_terms_service -> {
                    activity?.addFragment(WebViewFragment.newInstance(TERMS_LINK, getString(R.string.about_terms_service)))
                }
                R.string.about_open_source_licenses -> activity?.addFragment(LicensesFragment())
            }
        }
    }

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

        viewModelFactory = AboutViewModelFactory(requireContext(), onItemClickListener)
        viewModel = ViewModelProviders.of(this, viewModelFactory)
            .get(AboutViewModel::class.java)

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

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        activityComponents?.updateAppbarTitleWithFabAction(getString(R.string.about_feature_title))
        setupViews()
    }

    private fun setupViews() {
        try {
            val layoutManager = LinearLayoutManager(activity)
            recyclerView?.layoutManager = layoutManager
            val dividerItemDecoration = DividerItemDecoration(context, layoutManager.orientation)
            ContextCompat.getDrawable(context ?: return, R.drawable.shape_full_divider)?.let {
                dividerItemDecoration.setDrawable(it)
            }
            recyclerView?.addItemDecoration(dividerItemDecoration)
            recyclerView?.adapter = AboutAdapter(viewModel.onItemClickListener).apply {
                data = viewModel.getListItems()
            }
        } catch (e: Exception) {
            e.log()
        }
    }
}

Мой AboutItemViewHolder:

class AboutItemViewHolder(
    binding: AboutBinding
) : RecyclerView.ViewHolder(binding.root){

    private var aboutBinding: AboutBinding? = null

    init {
        this.aboutBinding = binding
    }

    fun bind(item: SettingsItemViewModel, listener: OnItemClickListener?) {
        aboutBinding?.about = AboutListItemViewModel(item)
        aboutBinding?.onItemClickListener = listener
        aboutBinding?.executePendingBindings()
    }
}

interface OnItemClickListener {
    fun onItemClick(titleName: Int)
}

Мой первый ViewModel, который я использовал в адаптере AboutListItemViewModel :

class AboutListItemViewModel(item: SettingsItemViewModel) {

    val titleName: Int = item.titleId
    val subTitleName: String? = item.value
    var isVisible: Boolean = item.value != null
}

Моя вторая ViewModel, которую я использовал во фрагменте AboutViewModel:

class AboutViewModel(val appContext: Context, val onItemClickListener: OnItemClickListener): ViewModel() {

    fun getListItems(): List<SettingsItemViewModel> {
        return listOf(
            SettingsItemViewModel(
                titleId = R.string.about_app_version,
                value = AppTools.getAppVersionName(appContext)
            ),
            SettingsItemViewModel(
                titleId = R.string.about_copyright,
                value = appContext.getString(R.string.about_copyright_description)
            ),
            SettingsItemViewModel(
                titleId = R.string.about_terms_service,
                itemIsClickable = true
            ),
            SettingsItemViewModel(
                titleId = R.string.about_open_source_licenses,
                itemIsClickable = true
            )
        )
    }
}

Моя AboutViewModelFactory:

class AboutViewModelFactory(val appContext: Context, private val onItemClickListener: OnItemClickListener) : ViewModelProvider.Factory {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(AboutViewModel::class.java)) {
            return AboutViewModel(appContext, onItemClickListener) as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }
}

SettingsViewModel - простой класс данных:

data class SettingsItemViewModel(
    @StringRes val titleId: Int,
    val value: String? = null,
    val switchEnabled: Boolean? = null,
    val isChecked: Boolean = false,
    val itemIsClickable: Boolean = false,
    val colorResId: Int = R.color.black
)

Q: Могу ли я использовать одну модель представления вместо двух моделей представления (AboutViewModel и AboutListItemViewModel)?

1 Ответ

1 голос
/ 26 марта 2020

ViewModel для шаблона MVVM, в котором ViewModel не знает ни о каких классах типов View, таких как OnItemClickListener, поэтому вы можете непреднамеренно использовать его, когда вы передаете слушателей фабрикам ViewModel , Это также приведет к утечке вашего фрагмента или действия в случае изменения конфигурации.

Я не вижу, чтобы вы даже использовали прослушиватель из ViewModel, поэтому вы можете полностью исключить это свойство и фабрику. Вы можете наследовать от AndroidViewModel и принимать один аргумент Context в своем конструкторе. Затем вы можете использовать by viewModels() для своего свойства ViewModel.

Без этих аргументов обратного просмотра view ничто не будет ограничивать вас от помещения всего этого в одну ViewModel реализацию.

Также странно, что в вашей модели представления есть функция, которая возвращает список других моделей представления. Почему это могут быть простые классы данных? ViewModel не должен быть непосредственно создан (кроме как с помощью заводской реализации). В противном случае его жизненный цикл не будет управляться.

...