java - 表达式的计算结果可能为 null,但由声明为 @NotNull 的方法返回

标签 java android android-studio

创建 RecyclerView 适配器后,会返回有关我的 View 持有者的警告。我读过this question并理解了一点,但不清楚 return viewHolder; 中的 viewHolder 实例应该替换为什么。

Expression might evaluate to null but is returned by the method declared as @NotNull

public class MyRVAapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private final static int TYPE_EXPANDABLE = 1, TYPE_NONEXPANDABLE = 2;
    private ArrayList callSMSFeed = new ArrayList();
    private Context context;

    public MyRVAapter(Context context){
        this.context = context;
    }

    public void setCallSMSFeed(List<Object> callSMSFeed){
        this.callSMSFeed = (ArrayList) callSMSFeed;
    }

  @Override
    public int getItemViewType(int position) {
        if (callSMSFeed.get(position) instanceof Phonecall) {
            return TYPE_EXPANDABLE;
        } else if (callSMSFeed.get(position) instanceof SMSmessage) {
            return TYPE_NONEXPANDABLE;
        }
        return -1;
    }

    @Override
    public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, final int position) {
        int viewType=holder.getItemViewType();
        switch (viewType){
            case TYPE_EXPANDABLE:
                Phonecall call = (Phonecall) callSMSFeed.get(position);
                ((CallViewHolder)holder).showCallDetails(call);
                break;
            case TYPE_NONEXPANDABLE:
                SMSmessage sms = (SMSmessage)callSMSFeed.get(position);
                ((SMSViewHolder)holder).showSmsDetails(sms);
                break;
        }
    }

    @Override
    public int getItemCount(){return callSMSFeed.size();}

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {


        int layout;

        RecyclerView.ViewHolder viewHolder;
        switch (viewType){
            case TYPE_EXPANDABLE:
                layout = R.layout.cardview_a;
                View callsView = LayoutInflater
                        .from(parent.getContext())
                        .inflate(layout, parent, false);
                viewHolder = new CallViewHolder(callsView);
                break;
            case TYPE_NONEXPANDABLE:
                layout = R.layout.cardview_b;
                View smsView = LayoutInflater
                        .from(parent.getContext())
                        .inflate(layout, parent, false);
                viewHolder = new SMSViewHolder(smsView);
                break;
            default:
                viewHolder = null;
                break;
        }
        return viewHolder;
    }
}

最佳答案

我喜欢@TheWanderer 的答案,但我想添加另一个答案,以强调我认为会让您的生活更轻松的一些内容。

ListView不同,RecyclerView不关心从getItemViewType()返回什么值;只要您从此方法返回不同的值,RecyclerView 就很高兴。这允许您使用布局资源 id 作为返回值,这意味着您无需定义自己的常量!

@Override
public int getItemViewType(int position) {
    Object obj = callSMSFeed.get(position);

    if (obj instanceof Phonecall) {
        return R.layout.cardview_a;
    } else if (obj instanceof SMSmessage) {
        return R.layout.cardview_b;
    }

    throw new IllegalStateException("item at position " + position + " is not a Phonecall or SMSmessage: " + obj);
}

这里我们返回R.layout.cardview_a而不是TYPE_EXPANDABLE;两者都是 int 类型,但现在我们可以让 Resources 框架为我们定义它们。

如果我们遇到的对象不是电话短信,我们也会抛出异常,以便我们立即知道有一个案子我们需要处理。这种异常应该只有开发人员犯错误时才会看到;该应用程序永远不会对用户造成崩溃,因为您将在发布该应用程序之前继续修复崩溃问题。

@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    LayoutInflater inflater = LayoutInflater.from(parent.getContext());
    View itemView = inflater.inflate(viewType, parent, false);

    switch (viewType) {
        case R.layout.cardview_a:
            return new CallViewHolder(itemView);

        case R.layout.cardview_b:
            return new SMSViewHolder(itemView);

        default:
            throw new IllegalArgumentException("unexpected viewType: " + viewType);
    }
}

因为我们从 getItemViewType() 返回布局 ID,所以我们不必担心从我们自己的常量转换为 R.layout 值;我们可以直接膨胀 viewType 。不过,我们仍然需要 switch 语句来了解要返回哪种类型的 ViewHolder

再次,我们在这里抛出了一个异常,以防我们传递了一个我们不期望的viewType。这种情况“不会发生”,但如果发生了,问题出在哪里就会非常清楚,并且很容易修复。

@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
    int viewType = holder.getItemViewType();

    switch (viewType) {
        case R.layout.cardview_a:
            Phonecall call = (Phonecall) callSMSFeed.get(position);
            ((CallViewHolder) holder).showCallDetails(call);
            break;

        case R.layout.cardview_b:
            SMSmessage sms = (SMSmessage) callSMSFeed.get(position);
            ((SMSViewHolder) holder).showSmsDetails(sms);
            break;

        default:
            throw new IllegalArgumentException("unexpected viewType: " + viewType);
    }
}

这里唯一的区别是用 switch 内的布局 id 替换常量。没什么大不了。当然,如果我们得到一些我们没有预料到的东西,我们就会崩溃。

另请注意,我已从参数中删除了 final 关键字。尽管编译器很乐意让您添加它们,但您不应该这样做。由于使用 notifyItemInserted() 等方法,ViewHolder 的位置可能会随着时间的推移而发生变化,而无需重新绑定(bind)。处理这个问题超出了您的问题范围,但需要指出。

关于java - 表达式的计算结果可能为 null,但由声明为 @NotNull 的方法返回,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52744611/

相关文章:

java - 处理预期的 java.lang.OutOfMemoryError : Java heap space 的解决方法

java - 如何获取不同时区的年、月、日?

android - 带有 ubuntu 索引器的 C/C++ IDE

安卓 : Upgrading user database using SQLiteAssetHelper

android - 在 Android Studio 中开发的 Flutter 项目的合法 .gitignore 是什么?

java - JTable 单元格中的图像显示

java - 使用 Jackson 将 json 嵌入节点作为值反序列化到 Map 中

java - 无法解析符号 'App'

java - support_simple_spinner_dropdown_item 上的 android.content.res.Resources$NotFoundException

android - AOSP 源代码树中的这些版本名称是什么意思?