You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SharedPreferences实现视频完成标记功能空指针异常求助

解决SharedPreferences标记视频完成时的NullPointerException问题

嘿,我来帮你搞定这个烦人的空指针问题!从你的报错日志和代码来看,核心问题一眼就能看出来:你要操作的completedTick这个ImageView对象始终是null,所以调用它的setVisibility()方法才会抛出NullPointerException。咱一步步拆解原因,再给你针对性的解决方案:

先看你两次报错的核心原因

第一次报错(onCreateView里的问题)

你在onCreateView里的执行顺序搞反了:

// 先调用了判断逻辑,这时候completedTick还没初始化!
if(activityCompleted == false || activityCompleted == null ) {
    completedTick.setVisibility(view.INVISIBLE);
}else{
    completedTick.setVisibility(View.VISIBLE);
}
// 之后才调用initialiseRecyclerView,里面才会初始化completedTick
initialiseRecyclerView(view);

这就好比你想开灯,但灯还没装上去,肯定会出问题!

第二次报错(onViewCreated里的问题)

你把逻辑移到了onViewCreated,但这里有两个大概率的坑:

  1. ID不匹配:你代码里写的是R.id.completedTickImage,但之前第一次用的是R.id.completedTick,得确认你的布局文件里这个ImageView的ID到底是啥,拼写差一个字符都找不到控件!
  2. 控件在RecyclerView的Item里:如果这个对勾是每个视频条目旁边的,那它是在RecyclerView的item布局里,不是Fragment的根布局。这种情况下,你直接用view.findViewById()是绝对找不到的——因为RecyclerView的item是动态加载的,每个item都有自己的completedTick,不能在Fragment的生命周期方法里直接获取单个实例。

针对性解决方案

情况1:对勾在Fragment根布局(不是每个视频条目都有)

如果是这种场景,那你只需要做两件事:

  • 把布局文件里的ImageView ID和代码里的统一,比如布局里是android:id="@+id/completedTick",代码里就别写成completedTickImage。
  • 确保先初始化控件,再调用setVisibility():
@Override
public void onViewCreated(@NonNull final View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    // 先初始化SharedPreferences
    loginPref = getContext().getSharedPreferences("loginPref", Context.MODE_PRIVATE);
    activityPref = getContext().getSharedPreferences("activityPref", Context.MODE_PRIVATE);
    activityCompleted = activityPref.getBoolean("activityCompleted", true);
    // 先找到控件!
    completedTick = view.findViewById(R.id.completedTick);
    // 再调用其他方法
    initialiseRecyclerView(view);
    retrieveActivities();
    // 最后设置可见性
    if (activityCompleted == false || activityCompleted == null) {
        completedTick.setVisibility(View.INVISIBLE);
    } else {
        completedTick.setVisibility(View.VISIBLE);
    }
}

情况2:对勾在RecyclerView的每个视频条目里(这才是你要的场景)

这应该是你真实的需求——每个视频旁边都有自己的完成对勾,这时候必须在RecyclerView的Adapter里处理,而不是在Fragment里直接操作:

第一步:修改RecyclerView的Adapter

public class ActivitiesAdapter extends RecyclerView.Adapter<ActivitiesAdapter.ViewHolder> {
    private List<YourActivityModel> activityList; // 替换成你的视频数据模型类
    private SharedPreferences activityPref;

    // 构造方法传入数据和上下文,用来获取SharedPreferences
    public ActivitiesAdapter(List<YourActivityModel> activityList, Context context) {
        this.activityList = activityList;
        this.activityPref = context.getSharedPreferences("activityPref", Context.MODE_PRIVATE);
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        // 加载你的item布局
        View itemView = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.your_activity_item_layout, parent, false);
        return new ViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        YourActivityModel currentActivity = activityList.get(position);
        // 关键:用每个视频的唯一标识(比如activityId)作为SharedPreferences的key
        // 这样每个视频的完成状态是独立的
        boolean isCompleted = activityPref.getBoolean("completed_" + currentActivity.getId(), false);
        
        // 根据状态设置对勾的可见性
        if (isCompleted) {
            holder.completedTick.setVisibility(View.VISIBLE);
        } else {
            holder.completedTick.setVisibility(View.INVISIBLE);
        }

        // 这里设置视频的其他内容,比如标题、封面等
        // holder.titleTextView.setText(currentActivity.getTitle());
    }

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

    // ViewHolder里初始化item里的控件
    public static class ViewHolder extends RecyclerView.ViewHolder {
        ImageView completedTick;
        // 其他控件,比如TextView titleTextView;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            completedTick = itemView.findViewById(R.id.completedTick);
            // titleTextView = itemView.findViewById(R.id.activityTitle);
        }
    }
}

第二步:在Fragment里初始化Adapter

修改你的initialiseRecyclerView方法:

private void initialiseRecyclerView(View view) {
    mRecyclerView = view.findViewById(R.id.recyclerViewActivities);
    mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
    // 假设你已经获取到了视频列表数据activityList
    ActivitiesAdapter adapter = new ActivitiesAdapter(activityList, getContext());
    mRecyclerView.setAdapter(adapter);
}

第三步:修改视频页面的完成状态保存逻辑

现在要保存单个视频的完成状态,而不是全局的一个值:

// 先从Intent里拿到当前视频的唯一ID(你需要在跳转视频页面时传递这个ID)
private String currentActivityId;

@Override
protected void onCreate(final Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.youtube_layout);
    activityPref = getSharedPreferences("activityPref", MODE_PRIVATE);
    // 获取传递过来的视频ID
    currentActivityId = getIntent().getStringExtra("ACTIVITY_ID");
    // 其他初始化逻辑...
    markActivityAsCompleted();
}

private void markActivityAsCompleted() {
    SharedPreferences.Editor editor = activityPref.edit();
    // 用视频ID作为key的一部分,保存该视频已完成
    editor.putBoolean("completed_" + currentActivityId, true);
    editor.apply();
}

最后再提醒两句

  • 空指针的本质就是“你要操作的对象不存在”,遇到这种问题先检查对象是否初始化、ID是否匹配、控件所在的布局是否正确。
  • 对于RecyclerView的item控件,绝对不能在Fragment/Activity的生命周期方法里直接获取,必须在Adapter的ViewHolder和onBindViewHolder里处理。

内容的提问来源于stack exchange,提问作者androidnewbie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:13:13