数据绑定,MaterialCardView 应该像 Radiogroup 一样起作用

huangapple go评论87阅读模式
英文:

Databinding, MaterialCardView should act like Radiogroup

问题

  1. 我试图实现两个 MaterialCardViews,它们应该像一个 Radiogroup 一样工作。因此,如果我点击其中一个,另一个应该取消选中。我正在使用 viewModelliveData 和自定义的双向数据绑定来保存这些值以供以后使用(通过电子邮件发送)。
  2. 我已经成功编写了 .xml 文件并实现了选中逻辑,但在实现取消选中逻辑时遇到了困难。
  3. ## XML,缩短版以获得更好的可见性 ##
  4. <layout
  5. xmlns:app="http://schemas.android.com/apk/res-auto">
  6. <data>
  7. <variable
  8. name="vm"
  9. type="com.example.app.data.viewmodel.EmailViewModel" />
  10. </data>
  11. <com.google.android.material.card.MaterialCardView
  12. android:id="@+id/cardViewOne"
  13. android:checkable="true"
  14. android:clickable="true"
  15. android:focusable="true"
  16. <!-- 自定义双向数据绑定 -->
  17. app:state_checked="@={vm.cardOptionOneChecked}">
  18. </com.google.android.material.card.MaterialCardView>
  19. <com.google.android.material.card.MaterialCardView
  20. android:id="@+id/cardViewTwo"
  21. android:checkable="true"
  22. android:clickable="true"
  23. android:focusable="true"
  24. <!-- 自定义双向数据绑定 -->
  25. app:state_checked="@={vm.cardOptionTwoChecked}">
  26. </com.google.android.material.card.MaterialCardView>
  27. </layout>
  28. ## ViewModel ##
  29. class EmailViewModel @ViewModelInject constructor(
  30. @Assisted private val savedStateHandle: SavedStateHandle
  31. ) : ViewModel() {
  32. // Id = cardViewOne 的变量
  33. val cardOptionOneChecked = MutableLiveData<Boolean>()
  34. // Id = cardViewTwo 的变量
  35. val cardOptionTwoChecked = MutableLiveData<Boolean>()
  36. }
  37. ## CardViewAdapter.kt ##
  38. @BindingAdapter("state_checked")
  39. fun setStateChecked(view: MaterialCardView, liveData: MutableLiveData<Boolean>) {
  40. if (view.isChecked != liveData.value) {
  41. liveData.value = view.isChecked
  42. }
  43. }
  44. @InverseBindingAdapter(attribute = "state_checked")
  45. fun getStateChecked(view: MaterialCardView): Boolean {
  46. return view.isChecked
  47. }
  48. // 我不知道这里应该有什么逻辑来使其工作!
  49. // 当前的方法只是检查当前视图,没有做更多的事情。如何保存上次选中的值?
  50. @BindingAdapter("state_checkedAttrChanged")
  51. fun setCheckedAttrListener(
  52. view: MaterialCardView,
  53. attrChange: InverseBindingListener,
  54. ) {
  55. view.apply {
  56. setOnClickListener { view.isChecked = true }
  57. setOnCheckedChangeListener { card, isChecked ->
  58. if (card.isChecked && card != view) {
  59. card.isChecked = false
  60. }
  61. }
  62. attrChange.onChange()
  63. }
  64. }
  65. 我非常感谢任何帮助,非常感谢!
  66. 附注:如果有更好、更简单的方法来实现这一点,例如从视图告诉 viewModel 保存 isChecked,请告诉我。MaterialCardView 默认实现了 "isChecked",但没有逻辑。
英文:

I am trying to implement two MaterialCardViews that should act like a Radiogroup. So if I click one, the other should be unchecked. I am using viewModel, liveData and custom two-way data binding to save these values for later purpose (sending per email).

I had success writing the .xml and implementing the check logic, but I struggle implementing uncheck logic.

XML, short version for better visibility

  1. &lt;layout
  2. xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;&gt;
  3. &lt;data&gt;
  4. &lt;variable
  5. name=&quot;vm&quot;
  6. type=&quot;com.example.app.data.viewmodel.EmailViewModel&quot; /&gt;
  7. &lt;/data&gt;
  8. &lt;com.google.android.material.card.MaterialCardView
  9. android:id=&quot;@+id/cardViewOne&quot;
  10. android:checkable=&quot;true&quot;
  11. android:clickable=&quot;true&quot;
  12. android:focusable=&quot;true&quot;
  13. &lt;!-- Custom Two way databinding --&gt;
  14. app:state_checked=&quot;@={vm.cardOptionOneChecked}&quot;
  15. &lt;/com.google.android.material.card.MaterialCardView&gt;
  16. &lt;com.google.android.material.card.MaterialCardView
  17. android:id=&quot;@+id/cardViewTwo&quot;
  18. android:checkable=&quot;true&quot;
  19. android:clickable=&quot;true&quot;
  20. android:focusable=&quot;true&quot;
  21. &lt;!-- Custom Two way databinding --&gt;
  22. app:state_checked=&quot;@={vm.cardOptionTwoChecked}&quot;&gt;
  23. &lt;/com.google.android.material.card.MaterialCardView&gt;
  24. &lt;/layout&gt;

ViewModel

  1. class EmailViewModel @ViewModelInject constructor(
  2. @Assisted private val savedStateHandle: SavedStateHandle
  3. ) : ViewModel() {
  4. // Variable for Id = cardViewOne
  5. val cardOptionOneChecked = MutableLiveData&lt;Boolean&gt;()
  6. // Variable for Id = cardViewTwo
  7. val cardOptionTwoChecked = MutableLiveData&lt;Boolean&gt;()
  8. }

CardViewAdapter.kt

  1. @BindingAdapter(&quot;state_checked&quot;)
  2. fun setStateChecked(view: MaterialCardView, liveData: MutableLiveData&lt;Boolean&gt;) {
  3. if (view.isChecked != liveData.value) {
  4. liveData.value = view.isChecked
  5. }
  6. }
  7. @InverseBindingAdapter(attribute = &quot;state_checked&quot;)
  8. fun getStateChecked(view: MaterialCardView,): Boolean {
  9. return view.isChecked
  10. }
  11. // I don&#39;t know what logic belongs here to make it work!
  12. // Current approach just checks the current view and does nothing more. How can I save the last
  13. // checked value?
  14. @BindingAdapter(&quot;state_checkedAttrChanged&quot;)
  15. fun setCheckedAttrListener(
  16. view: MaterialCardView,
  17. attrChange: InverseBindingListener,
  18. ) {
  19. view.apply {
  20. setOnClickListener { view.isChecked = true }
  21. setOnCheckedChangeListener { card, isChecked -&gt;
  22. if (card.isChecked &amp;&amp; card != view) {
  23. card.isChecked = false
  24. }
  25. }
  26. attrChange.onChange()
  27. }
  28. }

I appreciate every help, thank you very much!

P.S: If there is a better and easier way to achieve this e.g. telling the viewModel from the view to save isChecked, please inform me. MaterialCardView has implemented "isChecked" by default but no logic.

答案1

得分: 0

好的,我已经解决了这个问题:

首先,修改绑定适配器

实际上,我没有看到使用双向数据绑定来实现上述所写情况的任何方法。以下是新的绑定适配器代码:

  1. // View = 被点击的 MaterialCard,liveData = ViewModel 中的值
  2. @BindingAdapter("state_checked")
  3. fun setStateChecked(view: MaterialCardView, liveData: MutableLiveData<Boolean>) {
  4. if (view.isChecked != liveData.value) {
  5. if (liveData.value != null) {
  6. view.isChecked = liveData.value!!
  7. }
  8. }
  9. }

其次,修改 XML 布局,因为我们不再使用双向数据绑定

  1. <layout xmlns:app="http://schemas.android.com/apk/res-auto">
  2. <data>
  3. <variable
  4. name="vm"
  5. type="com.example.app.data.viewmodel.EmailViewModel" />
  6. </data>
  7. <com.google.android.material.card.MaterialCardView
  8. android:id="@+id/cardViewOne"
  9. android:checkable="true"
  10. android:clickable="true"
  11. android:focusable="true"
  12. <!-- 删除了 "=" -->
  13. app:state_checked="@{vm.cardOptionOneChecked}">
  14. </com.google.android.material.card.MaterialCardView>
  15. <com.google.android.material.card.MaterialCardView
  16. android:id="@+id/cardViewTwo"
  17. android:checkable="true"
  18. android:clickable="true"
  19. android:focusable="true"
  20. <!-- 删除了 "=" -->
  21. app:state_checked="@{vm.cardOptionTwoChecked}">
  22. </com.google.android.material.card.MaterialCardView>
  23. </layout>

第三,修改 ViewModel

  1. class EmailViewModel @ViewModelInject constructor(
  2. @ApplicationContext context: Context,
  3. @Assisted private val savedStateHandle: SavedStateHandle
  4. ) : ViewModel() {
  5. val cardOptionOneChecked = MutableLiveData<Boolean>()
  6. val cardOptionTwoChecked = MutableLiveData<Boolean>()
  7. // 添加了
  8. fun firstCardClicked() {
  9. cardOptionOneChecked.value = true
  10. cardOptionTwoChecked.value = false
  11. }
  12. fun secondCardClicked() {
  13. cardOptionOneChecked.value = false
  14. cardOptionTwoChecked.value = true
  15. }
  16. }

第四,为 XML 或 Fragment(这里是 Fragment)添加点击监听器

  1. cardViewOne.setOnClickListener {
  2. viewModel.firstCardClicked()
  3. }
  4. cardViewTwo.setOnClickListener {
  5. viewModel.secondCardClicked()
  6. }

如果有人有任何问题,请在评论中写下,我会提供帮助。

英文:

Okay, I've solved the Problem:

First, Change Binding Adapter

I actually don't saw any way to use two-way data binding to achieve the above written case. Here is the new Binding Adapter

  1. // View = Clicked MaterialCard, liveData = value in viewModel
  2. @BindingAdapter(&quot;state_checked&quot;)
  3. fun setStateChecked(view: MaterialCardView, liveData: MutableLiveData&lt;Boolean&gt;) {
  4. if (view.isChecked != liveData.value) {
  5. if (liveData.value != null) {
  6. view.isChecked = liveData.value!!
  7. }
  8. }
  9. }

Second, Change XML Layout, because we don't use two-way data binding anymore

  1. &lt;layout
  2. xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;&gt;
  3. &lt;data&gt;
  4. &lt;variable
  5. name=&quot;vm&quot;
  6. type=&quot;com.example.app.data.viewmodel.EmailViewModel&quot; /&gt;
  7. &lt;/data&gt;
  8. &lt;com.google.android.material.card.MaterialCardView
  9. android:id=&quot;@+id/cardViewOne&quot;
  10. android:checkable=&quot;true&quot;
  11. android:clickable=&quot;true&quot;
  12. android:focusable=&quot;true&quot;
  13. &lt;!-- Deleted &quot;=&quot; --&gt;
  14. app:state_checked=&quot;@{vm.cardOptionOneChecked}&quot;
  15. &lt;/com.google.android.material.card.MaterialCardView&gt;
  16. &lt;com.google.android.material.card.MaterialCardView
  17. android:id=&quot;@+id/cardViewTwo&quot;
  18. android:checkable=&quot;true&quot;
  19. android:clickable=&quot;true&quot;
  20. android:focusable=&quot;true&quot;
  21. &lt;!-- Deleted &quot;=&quot; --&gt;
  22. app:state_checked=&quot;@{vm.cardOptionTwoChecked}&quot;&gt;
  23. &lt;/com.google.android.material.card.MaterialCardView&gt;
  24. &lt;/layout&gt;

Third, Change viewmodel

  1. class EmailViewModel @ViewModelInject constructor(
  2. @ApplicationContext context: Context,
  3. @Assisted private val savedStateHandle: SavedStateHandle
  4. ) : ViewModel() {
  5. val cardOptionOneChecked = MutableLiveData&lt;Boolean&gt;()
  6. val cardOptionTwoChecked = MutableLiveData&lt;Boolean&gt;()
  7. // Added
  8. fun firstCardClicked() {
  9. cardOneChecked.value = true
  10. cardTwoChecked.value = false
  11. }
  12. fun secondCardClicked() {
  13. cardOneChecked.value = false
  14. cardTwoChecked.value = true
  15. }
  16. }

Fourth, add clickListener to XML or Fragment (here Fragment)

  1. cardViewOne.setOnClickListener {
  2. viewModel.firstCardClicked()
  3. }
  4. cardViewTwo.setOnClickListener {
  5. viewModel.secondCardClicked()
  6. }

If someone has any questions, just write it in the comments, I will help.

huangapple
  • 本文由 发表于 2020年8月25日 03:54:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/63567889.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定