【问题标题】:safeUnbox() cannot be invertedsafeUnbox() 不能倒置
【发布时间】:2018-04-30 07:29:27
【问题描述】:

我正在尝试消除我的 Android 应用程序的所有警告,其中之一是:

viewModel.value 是一个装箱字段,但需要取消装箱才能执行 android:checked。这可能会导致 NPE,因此 Data Binding 将安全地将其拆箱。您可以更改表达式并使用 safeUnbox() 显式包装 viewModel.value 以防止警告

其中 value 是来自超类的通用 ObservableField

public abstract class BaseDataTypeViewModel<T> extends BaseObservable  {
    public final ObservableField<T> value = new ObservableField<>();
    ...
}

并且在某处扩展为Boolean

public class CheckBooleanDataTypeViewModel extends BaseDataTypeViewModel<Boolean> {
    ...
}

我在data binding - safeUnbox warning 上看到警告的发生是因为这是Boolean 而不是boolean,所以我尝试添加:android:checked="@={safeUnbox(viewModel.value)}" 而不是android:checked="@={viewModel.value}",但后来我收到错误消息说我无法反转 safeUnbox() 方法。

****/ data binding error ****msg:The expression android.databinding.DynamicUtil.safeUnbox(viewModelValue) cannot be reverse: There is no inverse for method safeUnbox, you must add an @InverseMethod annotation to the方法来指示在双向绑定表达式中使用时应该使用哪种方法

我正确理解这两个独立的问题,但我是否必须忍受警告以避免错误,或者它们是避免警告和错误的解决方案?它所说的@InverseMethod 呢?我没有设法添加此注释,因为该方法来自 android 包。

【问题讨论】:

    标签: android warnings android-databinding


    【解决方案1】:

    我没有以这种特殊方式使用过 Android 架构组件或数据绑定库,但我认为我仍然可以提供帮助。

    在你的 XML 中,你有这个:

    android:checked="@={viewModel.value}"
    

    系统给你一个警告是因为它想让你知道在viewModel.valuenull 的情况下,它会做一些特别的事情(表现得好像它是false,大概)。它通过safeUnbox() 方法完成此操作。

    为了解决警告,建议明确调用safeUnbox()。你不能这样做,因为从booleanBoolean 没有safeUnbox() 的“逆”返回

    但听起来你不必使用safeUnbox();您可以创建自己的将Boolean 转换为boolean 的方法,然后您可以使用建议的注释来声明哪个方法将从boolean 转换回Boolean

    public class MyConversions {
    
        @InverseMethod("myBox")
        public static boolean myUnbox(Boolean b) {
            return (b != null) && b.booleanValue();
        }
    
        public static Boolean myBox(boolean b) {
            return b ? Boolean.TRUE : Boolean.FALSE;
        }
    }
    

    现在您可以将 XML 更改为:

    android:checked="@={com.example.stackoverflow.MyConversions.myUnbox(viewModel.value)}"
    

    我希望这会有所帮助。如果事实证明我离基地很远,请告诉我;我很想了解有关此主题的更多信息。

    我从https://medium.com/google-developers/android-data-binding-inverse-functions-95aab4b11873 学到的这个答案的大部分内容

    【讨论】:

    • 你很好地理解了这个问题!解决它的好方法。为了更简洁,我将“com.example ...”导入到&lt;import&gt; 标签中。
    • 谢谢!我创建了一个类,其中包含所有 Java 原始类型的映射。 gist.github.com/pasmat/f1f8f04eeab4dde7dc9b534a51f67d15
    • 我试过这个,但在更改我的复选框后在我的 ViewModel 中执行 livedata.getValue() 时出现空指针异常。如果我放置断点并且警告消失了,我可以看到它正在运行装箱方法。知道可能出了什么问题吗?
    【解决方案2】:

    遇到了这个问题,并找到了一个更简单的解决方案。您可以通过为盒装类型创建自定义BindingAdapter 来避免此警告,如下所示:

    @BindingAdapter("android:checked")
    public static void setChecked(CompoundButton checkableView, Boolean isChecked) {
        checkableView.setChecked(isChecked != null ? isChecked : false);
    }
    

    此解决方案可以复制到visibilityenabled 等任何属性以及IntegerFloat 等任何盒装原语。

    如果您的LiveData 值为null,您还可以提供要使用的值,如下所示:

    @BindingAdapter(value={"android:checked", "nullValue"}, requireAll=false)
    public static void setChecked(CompoundButton checkableView, Boolean isChecked, boolean nullValue) {
        checkableView.setChecked(isChecked != null ? isChecked : nullValue);
    }
    

    然后这样称呼它:

    <CheckBox
        ...
        android:checked='@{viewModel.value}'
        app:nullValue="@{false}"
        />
    

    【讨论】:

      【解决方案3】:

      在 xml 中这样做。最后一行代码是最重要的。

      <layout>
      
        <data>
          <import type="android.view.View"/>
          <variable name="entryViewModel"
              type="com.craiovadata.groupmap.viewmodel.EntryViewModel" />
        </data>
      
        <Button 
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:visibility="@{safeUnbox(entryViewModel.showBtnMyGroups) ? View.VISIBLE : View.GONE, default = gone}" />
      

      【讨论】:

        【解决方案4】:

        我找到了一个代码,这个 safeUnbox 实现。

        /** @hide */
        protected static int safeUnbox(java.lang.Integer boxed) {
            return boxed == null ? 0 : (int)boxed;
        }
        
        /** @hide */
        protected static long safeUnbox(java.lang.Long boxed) {
            return boxed == null ? 0L : (long)boxed;
        }
        
        /** @hide */
        protected static short safeUnbox(java.lang.Short boxed) {
            return boxed == null ? 0 : (short)boxed;
        }
        
        /** @hide */
        protected static byte safeUnbox(java.lang.Byte boxed) {
            return boxed == null ? 0 : (byte)boxed;
        }
        
        /** @hide */
        protected static char safeUnbox(java.lang.Character boxed) {
            return boxed == null ? '\u0000' : (char)boxed;
        }
        
        /** @hide */
        protected static double safeUnbox(java.lang.Double boxed) {
            return boxed == null ? 0.0 : (double)boxed;
        }
        
        /** @hide */
        protected static float safeUnbox(java.lang.Float boxed) {
            return boxed == null ? 0f : (float)boxed;
        }
        
        /** @hide */
        protected static boolean safeUnbox(java.lang.Boolean boxed) {
            return boxed == null ? false : (boolean)boxed;
        }
        

        所以,如果你的值不是基类型,你不能直接使用safeUnbox,你应该自己定义一个静态函数来安全拆箱。

        【讨论】:

          猜你喜欢
          • 2023-03-17
          • 1970-01-01
          • 1970-01-01
          • 2010-09-06
          • 2014-04-20
          • 2016-07-17
          • 2018-08-21
          • 2021-06-14
          • 2014-08-30
          相关资源
          最近更新 更多