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

Я хочу отобразить текст в моем представлении. Однако текст не зависит от «данных» из моей модели, которые я могу отображать напрямую, но зависит от перечисления состояния. В зависимости от состояния, я хочу отобразить предопределенный текст в моем файле строковых ресурсов.

Например, у меня есть следующее перечисление в слое модели:

enum class GreetingType
{
    GREETING_FIRST_TIME,
    WELCOME_BACK,
    GOODBYE
}

В моих строках . xml У меня есть соответствующие строки:

R.string.greeting_first_time
R.string.welcome_back
R.string.goodbye

Мой вопрос: как должен выглядеть интерфейс между моделью представления и представлением? Должен ли я передать:

  1. перечисление (например, GreetingType.GOODBYE)
  2. идентификатор ресурса (например, R.string.goodbye)
  3. разрешенную строку (например, context.resources.getString(R.string.goodbye )

Кажется, что у каждого подхода есть свои недостатки:

  • С 1. и 2. Мне нужно отображение логики c в представлении.
  • С 3. Мне нужно context в модели представления, которой я бы хотел избежать.

Есть ли рекомендуемый подход?

Могу ли я добиться обоих:

  • Сохранять вид немым (избегая if / else)
  • Содержать модель представления в чистоте от Android Framework (т.е. используйте ViewModel вместо AndroidViewModel)?

Приведенные мною примеры касаются только данных, отображаемых напрямую.

Ответы [ 3 ]

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

Для меня лучше выбрать android привязку данных .

Благодаря привязке данных ваше представление (. xml) имеет переменные. Таким образом, вы можете передать viewModel прямо в ваше представление.

Нет ничего плохого в том, чтобы отобразить простые логи c в ваше представление. В веб-разработке это то, что мы делаем с React или VueJs в шаблоне. Самое главное - сохранить простоту.

С этой точки зрения, когда вы хотите что-то изменить, вы знаете, что ваш display logic каждый раз находится в вашем view.

Если ваша логика отображения c будет более сложной, вы можете создать Binding Adapter .

Вот пример:

class MyViewModel() : ViewModel() {

    // prevent changing the value of the greeting var outside the viewModel
    private val _greeting = MutableLiveData<GreetingType>()

    // we can access greeting value from here
    val greeting: LiveData<GreetingType>
        get() = _greeting

    fun setGreeting(g: GreetingType) {
        if (_greeting.value != g) {
            _greeting.value = g
        }
    }

}

Ваш фрагмент:

class MyFragment() : Fragment() {

    private lateinit var viewModel: MyViewModel
    private lateinit var binding: MyFragmentBinding

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

        // init your viewModel here
        // [...]

        binding = MyFragmentBinding.inflate(inflater, container, false)
        binding.lifecycleOwner = viewLifecycleOwner
        binding.viewModel = viewModel
        return binding.root

    }

}

Ваш объект привязки данных:

object MyFirstDatabinding {

    @JvmStatic
    @BindingAdapter("android:dispGreeting")
    fun displayGreeting(txtView: TextView, greeting: GreetingType?) {
        greeting?.let { g ->
            val sentence = when(g) {
                GREETING_FIRST_TIME -> view.context.getString(R.string.greeting_first_time)
                WELCOME_BACK -> view.context.getString(R.string.welcome_back)
                GOODBYE -> view.context.getString(R.string.goodbye)
            }
            txtView.setText(sentence)
        }
    }

}

Ваш взгляд

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

        <variable
            name="viewModel"
            type="your.package.MyViewModel" />

    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:id="@+id/textView"
            android:dispGreeting="@{viewModel.greeting}" />

    </ androidx.constraintlayout.widget.ConstraintLayout>

</layout>
0 голосов
/ 26 марта 2020

Чтобы использовать контекст в ViewModel, вы можете использовать AndroidViewModel(application), который нуждается в applicationContext, а затем вы получите ресурсы.

пример из Android Codelabs , вот репо :

val displayPropertyPrice = Transformations.map(selectedProperty) {
    application.applicationContext.getString(
            when (it.isRental) {
                true -> R.string.display_price_monthly_rental
                false -> R.string.display_price
            }, it.price)
}
0 голосов
/ 26 марта 2020

Зависит от вашего варианта использования, но не является идеальным иметь ссылки на ресурсы также внутри viewModels. Поэтому я бы посоветовал вам назначить его непосредственно в виде (мероприятии или фрагменте), например:

fun setGreeting() { textView.setText(R.string.greeting_first_time) }
fun setWelcome() { textView.setText(R.string.welcome_back) }
fun setGoodbye() { textView.setText(R.string.goodbye) }

Затем отобразить их в вашей модели представления, например, если это, тогда сделать setGreeting (), если это, то сделать setWelcome () или сделать переключите регистр с помощью enum и вызовите соответствующий view (). setGreeting () внутри вашей viewModel.

...