1

very new to Android App development here. I'm not sure if I'm asking the right question, but here's my situation. I have a simple gradebook app that looks like the following:

enter image description here

My goal is to display multiple courses on the app. I am using the RecyclerView to implement this, using the following onCreate function in my activity to pass the course information to the adapter:

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_grade);

        getSupportActionBar().setDisplayShowHomeEnabled(true);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        getSupportActionBar().setDisplayShowTitleEnabled(false);

        for(int i = 0; i<2; i++)
        {
            Course course = Course.generateRandomCourse();

            recyclerView = (RecyclerView) findViewById(R.id.togglegrades);
            layoutManager = new LinearLayoutManager(this);
            recyclerView.setLayoutManager(layoutManager);

            mAdapter = new RecyclerViewAdapter(course);
            recyclerView.setAdapter(mAdapter);
        }

My goal for the for loop was to have two courses show up, but this is not the case. I'm not sure whether I need to rework my Recycling Adapter to take a list of courses, or if there is a simple way to display multiple courses.

Any help is much appreciated! Thank you for your time :)

EDIT: After adding 2 recyclers in XML and trying to invoke both, this is the code I used:

        Course course = Course.generateRandomCourse();
        Course course1 = Course.generateRandomCourse();

        recyclerView = (RecyclerView) findViewById(R.id.togglegrades);
        recyclerView1 = (RecyclerView) findViewById(R.id.togglegrades1);

        layoutManager = new LinearLayoutManager(this);
        layoutManager1 = new LinearLayoutManager(this);


        recyclerView.setLayoutManager(layoutManager);
        recyclerView1.setLayoutManager(layoutManager1);

        mAdapter = new RecyclerViewAdapter(course);
        mAdapter1 = new RecyclerViewAdapter(course1);

        recyclerView.setAdapter(mAdapter);
        recyclerView1.setAdapter(mAdapter1);

Now the output is just overlapped.

enter image description here

EDIT 2: Including full adapter code below.

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
{
    private static final int TYPE_HEADER = 0;
    private static final int TYPE_ITEM = 1;

    private List<Course> courses = new ArrayList<>();
    private ArrayList<Assignment> assignments;
    private String course;
    private String average;


    public RecyclerViewAdapter(List<Course> courses)
    {
        for(Course c : courses)
        {
            course = c.getCourseTitle();
            assignments = c.getAssignments();
            if(assignments.size()==0)
            {
                average = "0";
            }
            else
            {
                Integer grade_total = new Integer(0);
                Integer assignment_total = new Integer(0);
                for (Assignment i : assignments)
                {
                    grade_total += i.getAssignmentGrade();
                    assignment_total++;
                }
                average = Integer.toString(grade_total /assignment_total);
            }
        }
    }

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

        if(viewType == TYPE_HEADER)
        {
            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.header_course,parent,false);
            return new ViewHolderHeader(v);
        }
        else if(viewType == TYPE_ITEM)
        {
            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_course, parent, false);
            return new ViewHolderItem(v);
        }
        else return null;
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position)
    {
        if(holder instanceof ViewHolderHeader)
        {
            ViewHolderHeader VHHeader = (ViewHolderHeader)holder;
            VHHeader.title.setText(course);
            VHHeader.average.setText("Average: " + average + "%");
        }
        else if(holder instanceof ViewHolderItem)
        {
            String assignemnt_name = assignments.get(position-1).getAssignmentTitle();
            String assignment_grade = Integer.toString(assignments.get(position-1).getAssignmentGrade());
            ViewHolderItem VHItem = (ViewHolderItem)holder;
            VHItem.name.setText(assignemnt_name);
            VHItem.grade.setText(assignment_grade + "%");
        }

    }

    @Override
    public int getItemCount()
    {
        return assignments.size() + 1;
    }

    private boolean isPositionHeader(int position)
    {
        if(position == 0)
            return true;
        else
            return false;
    }

    @Override
    public int getItemViewType(int position)
    {
        if(isPositionHeader(position))
            return TYPE_HEADER;
        return TYPE_ITEM;
    }

    public class ViewHolderHeader extends RecyclerView.ViewHolder
    {
        TextView title;
        TextView average;
        public ViewHolderHeader(View view)
        {
            super(view);
            this.title = (TextView)itemView.findViewById(R.id.course);
            this.average = (TextView)itemView.findViewById(R.id.average);
        }
    }

    public class ViewHolderItem extends RecyclerView.ViewHolder
    {
        TextView name;
        TextView grade;
        public ViewHolderItem(View view)
        {
            super(view);
            this.name = (TextView)itemView.findViewById(R.id.assignment);
            this.grade = (TextView)itemView.findViewById(R.id.grade);
        }
    }

}

4 Answers4

1

You need to have 2 RecycleViews in your activity_grade xml layout, and do what you're doing inside the for loops for each. What your for loop is currently doing, is creating a random course, and then setting that course to the RecyclerView called togglegrades, then on the second iteration, it does the same thing again, on the same RecyclerView

So what I would do after adding a second RecyclerView in the xml layout, is replace the for loop with something like this:

recyclerView1 = (RecyclerView) findViewById(R.id.togglegrades);
recyclerView2 = (RecyclerView) findViewById(R.id.togglegrades2);

layoutManager = new LinearLayoutManager(this);

recyclerView1.setLayoutManager(layoutManager);
recyclerView2.setLayoutManager(layoutManager);
    
mAdapter = new RecyclerViewAdapter(course);
// Maybe create a different course here and set it to a new adapter variable
recyclerView.setAdapter(mAdapter);
recyclerView2.setAdapter(mAdapter);
Omar Hezi
  • 155
  • 8
  • I tried this, and had to also create another layout manager. Still, I get the following error: 2020-09-17 22:29:12.268 5786-5786/com.example.a40048841_ass1 E/RecyclerView: **No adapter attached; skipping layout**. See my edit to see how I modified my code – Adrian Patterson Sep 18 '20 at 02:31
  • Scratch that ^ Got it to work. Now I just have them both overlapping. – Adrian Patterson Sep 18 '20 at 02:42
1

You can do it in a very optimal way. Please see the tutorial titled Recyclerview multiple view type. Here you don't need to create multiple recycler views and you can use a single recycler view for multiple types of data.

Gk Mohammad Emon
  • 6,084
  • 3
  • 42
  • 42
1

Why do you use two RecyclerView?

Your Layout looks simple, just use one recyclerView with two viewType.

This is a sample

GHH
  • 1,713
  • 1
  • 8
  • 21
1

You do not need to use multiple RecyclerView.

Just look at your code and you will find the answer why your code is not working.

RecyclerView is similar like listview where you have to pass a list. But in your code you are passing just 1 object of course.

You must not add recyclerview inside for loop, as recyclerview purpose is to store list,not 1 object.

So you should pass List to the recyclerview.

 @Override
protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_grade);

    getSupportActionBar().setDisplayShowHomeEnabled(true);
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    getSupportActionBar().setDisplayShowTitleEnabled(false);

    recyclerView = (RecyclerView) findViewById(R.id.togglegrades);
    layoutManager = new LinearLayoutManager(this);
    recyclerView.setLayoutManager(layoutManager);

    List<Course> listCourse=new ArrayList();

    for(int i = 0; i<2; i++)
    {
        listCourse.add(Course.generateRandomCourse());
    }

    mAdapter = new RecyclerViewAdapter(listCourse);
    recyclerView.setAdapter(mAdapter);
}

And inside the adapter you can use this list of course.

For more reference you can check this link, on how to use RecyclerView with Adapter

EDIT: In your adapter look at this code:

 private boolean isPositionHeader(int position)
{
    if(position == 0)
        return true;
    else
        return false;
}

Here position == 0 is static code, which will not work for dynamic code.If your list contains 1 Header and 1 Item, than you can change simply position%2 == 0 condition. else you have to create other recyclerview inside adapter.

Check out below code and comments I have added , at the end of onBindViewHolder you can add new recyclerview, for that you have to add recyclerview in your layout also.

Checkout how you can use List<Course> courses in your constructor and onBindViewHolder.

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;

private List<Course> courses = new ArrayList<>();



public RecyclerViewAdapter(List<Course> courses)
{
   this.courses = courses;
}

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

      View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.header_course,parent,false);
        return new ViewHolderHeader(v);
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
        Course c=courses.get(position);
        String course = c.getCourseTitle();
        String assignments = c.getAssignments();
        if(assignments.size()==0)
        {
            average = "0";
        }
        else
        {
            Integer grade_total = new Integer(0);
            Integer assignment_total = new Integer(0);
            for (Assignment i : assignments)
            {
                grade_total += i.getAssignmentGrade();
                assignment_total++;
            }
            average = Integer.toString(grade_total /assignment_total);
        }
     ViewHolderHeader VHHeader = (ViewHolderHeader)holder;
        VHHeader.title.setText(course);
        VHHeader.average.setText("Average: " + average + "%");

        //Here you can set new recyclerview in your adapter for assignments

}

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

/*private boolean isPositionHeader(int position)
{
    //Change logic here as per your list, because you have list not single object. 
    //if(position == 0)
    //    return true;
    //else
    //    return false;
}*/
//Remove this code as you have not different views in your list
/*@Override
public int getItemViewType(int position)
{

    if(isPositionHeader(position))
        return TYPE_HEADER;
    return TYPE_ITEM;
}*/

public class ViewHolderHeader extends RecyclerView.ViewHolder
{
     View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.header_course,parent,false);
        return new ViewHolderHeader(v);
}


}
Keyur Nimavat
  • 3,595
  • 3
  • 13
  • 19
  • 1
    If you find it useful than please upvote and accept answer. – Keyur Nimavat Sep 18 '20 at 05:54
  • I find it useful but still have been unable to find how to recieve a list in recycler constructor and to have recycler view handle that. I see in other examples that people pass the recycler a list, but don't really do anything to it? It's initialized in the constructor and that's about it. I tried this to no avail... Any extra resources would be appreciated. – Adrian Patterson Sep 18 '20 at 12:56
  • 1
    can you add your adapter code so I can help you more. – Keyur Nimavat Sep 18 '20 at 12:59
  • Yes, will add it in an edit. Thank you for your help, it's seriously appreciated – Adrian Patterson Sep 18 '20 at 13:50
  • Wow, what a mess my code was. Definitely over complicated things. Marking this as solution! You rock. Thank you – Adrian Patterson Sep 18 '20 at 20:00
  • I just want to clarify, when you say "Here you can set new recyclerview in your adapter for assignments," do you mean I have to create a seperate recycler view with its own adapter for assignments? – Adrian Patterson Sep 19 '20 at 17:27
  • 1
    I told that because I don't know how your data is. If your Course has Title with assignments list than yes (I assume this, based on your adapter code), you have to add other recyclerview inside adapter. But if your Course title is come in different object and assignment is in different object than your previous code was ok, (I also mentioned that you have to make different logic position%2 == 0 like this) – Keyur Nimavat Sep 20 '20 at 03:17
  • 1
    If you have still doubt than tell me, I will update my answer with more clarification. – Keyur Nimavat Sep 20 '20 at 03:18