0

I am trying to display a message in a new TextView when an adapter is empty, but I keep getting the following error: E/RecyclerView: No adapter attached; skipping layout.

The method where the error is occurring:

    @BindView(R.id.courses_recycler_view)
    RecyclerView coursesRecyclerView;

   private CoursesAdapter coursesAdapter;
   private List<CourseEntity> coursesData = new ArrayList<>();
   private TextView noCourses;

   private void initViewModel() {
        final Observer<List<CourseEntity>> coursesObserver = new Observer<List<CourseEntity>>() {
            @Override
            public void onChanged(List<CourseEntity> courseEntities) {
                coursesData.clear();
                coursesData.addAll(courseEntities);

                if(coursesAdapter == null) {
                    coursesAdapter = new CoursesAdapter(coursesData, TermDetailsActivity.this);
                    coursesRecyclerView.setAdapter(termsAdapter);
                    if (coursesAdapter.getItemCount() == 0) {
                        noCourses = new TextView(getApplicationContext());
                        noCourses.setId(R.id.noTerms);
                        ConstraintLayout.LayoutParams lp = new ConstraintLayout.LayoutParams(
                                ConstraintLayout.LayoutParams.WRAP_CONTENT,
                                ConstraintLayout.LayoutParams.WRAP_CONTENT);
                        noCourses.setLayoutParams(lp);
                        noCourses.setText("No terms found.");
                        noCourses.setTextColor(Color.parseColor("#000000"));
                        coursesLayout.addView(noCourses);

                        ConstraintSet cs = new ConstraintSet();
                        cs.constrainHeight(noCourses.getId(), ConstraintSet.WRAP_CONTENT);
                        cs.constrainWidth(noCourses.getId(), ConstraintSet.WRAP_CONTENT);
                        cs.connect(noCourses.getId(), ConstraintSet.LEFT, ConstraintSet.PARENT_ID, ConstraintSet.LEFT, 0);
                        cs.connect(noCourses.getId(), ConstraintSet.RIGHT, ConstraintSet.PARENT_ID, ConstraintSet.RIGHT, 0);
                        cs.connect(noCourses.getId(), ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP, 0);
                        cs.connect(noCourses.getId(), ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM, 0);
                        cs.applyTo(coursesLayout);
                    }
                } else {
                    coursesAdapter.notifyDataSetChanged();
                }
            }
        };
        termDetailsViewModel = new ViewModelProvider(this).get(TermDetailsViewModel.class);
        termDetailsViewModel.mCourses.observe(this, coursesObserver);

    }

My adapter class:

public class CoursesAdapter extends RecyclerView.Adapter<CoursesAdapter.ViewHolder> {

    String pattern = "MM/dd/yyyy";
    DateFormat df = new SimpleDateFormat(pattern);

    public final List<CourseEntity> mCourses;
    private final Context mContext;

    public CoursesAdapter(List<CourseEntity> mCourses, Context mContext) {
        this.mCourses = mCourses;
        this.mContext = mContext;
    }


    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        View view = inflater.inflate(R.layout.courses_list_item, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        final CourseEntity course = mCourses.get(position);

        String courseStartDate = df.format(course.getCourseStartDate());
        String courseEndDate = df.format(course.getCourseEndDate());

        holder.courseName.setText(course.getCourseName());
        holder.courseStartDate.setText(courseStartDate);
        holder.courseEndDate.setText(courseEndDate);

        holder.courseListItem.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

            }
        });
    }

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

    public class ViewHolder extends RecyclerView.ViewHolder {

        @BindView(R.id.list_course_name)
        TextView courseName;
        @BindView(R.id.list_course_start_date)
        TextView courseStartDate;
        @BindView(R.id.list_course_end_date)
        TextView courseEndDate;
        @BindView(R.id.course_list_layout)
        ConstraintLayout courseListItem;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            ButterKnife.bind(this, itemView);
        }
    }
}

The TextView is being added this way as this is a school project and part of the requirements is to be able to add views programatically.

  • Does any of this help? https://stackoverflow.com/questions/2395769/how-to-programmatically-add-views-to-views – valegians Feb 29 '20 at 03:51

1 Answers1

0

You are seeing this warning because no adapter is set for the RecyclerView when the view is drawn on the screen. You are loading data asynchronously and then call setAdapter() on the RecyclerView later. Therefore, when the view is shown, the data is still loading and you still haven't set the adapter yet.

What you are doing is fine but if you don't want to get this warning, you can give an empty list to the adapter and set it during view initialization. Then you can update that list asynchronously and call notifyDataSetChanged() to tell the RecyclerView refresh itself.

Mehmed
  • 2,880
  • 4
  • 41
  • 62
  • Is that not what I am doing with my coursesData list? At the beginning of onChanged I have the coursesData list get emptied, add any existing courses (there are none as of now) and passing that list into my adapter constructor. I have something that is almost identical working in another location in my app, but I cannot get this one to work even after cross referencing with the method that is working properly. – Jake Henderson Feb 29 '20 at 02:33
  • You are doing it but not when the view is drawn. You should be setting adapter Activity's `onCreate()` or Fragment's `onCreateView()`. You are already initializing `courseData` during declaration. You can move adapter creation and `setAdapter()` to Activity's `onCreate()` or Fragment's `onCreateView()`. – Mehmed Feb 29 '20 at 03:17
  • My initViewModel method is being called inside the activities onCreate() – Jake Henderson Feb 29 '20 at 16:47