java – How to sort recyclerview by lowest number / String Android Studio-ThrowExceptions

Exception or error:

I have a recyclerview in my app, that contains two textviews, one is rankTextView and other is nameTextview. Something like this;

enter image description here

What I want to do is sort this recyclerview, firstly in lowest number order and if there is two same numbers then I want it to be sorted out by Strings. In the above screenshot I have for example, two people rank 1, firstly I want the recyclerview to put these to the top and then sort it out by String.

I have searched online to see how can I go about doing this but being new to android I haven’t been able to play around/adapt the findings into my project. For example;

How to sort Strings on an Android RecyclerView?

What is the SortedList<T> working with RecyclerView.Adapter?

I have created a custom adapter that contains a textview called rankTextview and another textview called nameTextview, like this;

 rankTextview = (TextView) itemView.findViewById(R.id.ranktextview);
 nameTextview = (TextView) itemView.findViewById(R.id.nametextview);

then I have a method that takes what value to put in these textview as parameter, like this;

 public addPerson(String rankTextview, String personTextview,) {
        this.rankTextview = rankTextview;
        this.personTextview = personTextview;
    }

and then I call this method in my main class to add data, like this;

person.add(new addPerson
        ("1\nrank", "James Kub"));
person.add(new addPerson
        ("2\nrank", "Peter Hanly"));
person.add(new addPerson
        ("3\nrank", "Josh Penny"));
person.add(new addPerson
        ("1\nrank", "Danny Jackson"));
person.add(new addPerson
        ("3\nrank", "Brad Black"));

Now what I want to do is sort out this data firstly by rank lowest number order e.g 1, 2,3… and if there are two same numbers then I want to sort out by name alphabetical order. Also, in future my app will contain points in stead of ranks which will be decimal numbers like this. 1.1, 1.5, 1.1, 2.1, 2.5 and so on, so would it possible to take in count decimal numbers when sorting out by rank.

Also, since I had so many lines of code, I wasn’t sure which section to provide and which not to provide, please let me know if there is any code I am missing that I should have included.

EDITED:

public void animateTo(List<ExampleModel> models) {
        applyAndAnimateRemovals(models);
        applyAndAnimateAdditions(models);
        applyAndAnimateMovedItems(models);
    }

    private void applyAndAnimateRemovals(List<ExampleModel> newModels) {
        for (int i = mModels.size() - 1; i >= 0; i--) {
            final ExampleModel model = mModels.get(i);
            if (!newModels.contains(model)) {
                removeItem(i);
            }
        }
    }

    private void applyAndAnimateAdditions(List<ExampleModel> newModels) {
        for (int i = 0, count = newModels.size(); i < count; i++) {
            final ExampleModel model = newModels.get(i);
            if (!mModels.contains(model)) { // error here, saying cannot resolve method contains
                addItem(i, model);
            }
        }
    }

    private void applyAndAnimateMovedItems(List<ExampleModel> newModels) {
        for (int toPosition = newModels.size() - 1; toPosition >= 0; toPosition--) {
            final ExampleModel model = newModels.get(toPosition);
            final int fromPosition = mModels.indexOf(model);
            if (fromPosition >= 0 && fromPosition != toPosition) {
                moveItem(fromPosition, toPosition);
            }
        }
    }

    public ExampleModel removeItem(int position) {
        final ExampleModel model = mModels.remove(position); // Error here, saying in sortedlist cannot be applied to (int)
        notifyItemRemoved(position);
        return model;
    }

    public void addItem(int position, ExampleModel model) {
        mModels.add(position, model); // Error here, saying add has private access in 'android.support.v7.util.SortedList'
        notifyItemInserted(position);
    }

    public void moveItem(int fromPosition, int toPosition) {
        final ExampleModel model = mModels.remove(fromPosition); // Error here, saying in sortedlist cannot be applied to (int)
        mModels.add(toPosition, model); // Error here, saying add has private access in 'android.support.v7.util.SortedList'
        notifyItemMoved(fromPosition, toPosition);
    }
How to solve:

There are some options for implementing sorting in a RecyclerView. Of course it is possible to rely on Comparable<T> and Comparator<T> interfaces but, as you mentioned, it is also possible to exploit SortedList<T> class defined in Android SDK.

Purpose of SortedList<T> is simplifying sorting of elements in a RecyclerView, allowing you to intercept significant events like “new item added”, “item removed” and so on.

In your case you can proceed as follows:

  1. Define a Person class for wrapping rank and name. Please notice that in this version I’m assuming to have integer values for rank, but it’s quite easy to move to decimal value.

    class Person {
    
        private String rank;
        private String name;
    
        public Person(String rank, String name) {
            this.rank = rank;
            this.name = name;
        }
    
        // getters and setters here
    
    }
    
  2. Define an Activity where to build RecyclerView and corresponding adapter. In this example I’ve included a FloatingActionButton for inserting new random Persons. As you can see, when creating a new Person, method addPerson is invoked on the adapter. Its effect will be to update the RecyclerView, sorting it according to criteria defined within the adapter itself (see point 3).

    public class SortPersonsActivity extends AppCompatActivity {
    
        private List<Person> mPersons;
    
        private SortPersonsAdapter mPersonsAdapter;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_persons_list);
            Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
            setSupportActionBar(toolbar);
    
            mPersons = new ArrayList<>();
            mPersons.add(new Person("1\nrank", "James Kub"));
            mPersons.add(new Person("2\nrank", "Peter Hanly"));
            mPersons.add(new Person("3\nrank", "Josh Penny"));
            mPersons.add(new Person("1\nrank", "Danny Jackson"));
            mPersons.add(new Person("3\nrank", "Brad Black"));
    
            RecyclerView recyclerView = (RecyclerView) findViewById(R.id.lst_items);
            recyclerView.setLayoutManager(getLayoutManager());
            mPersonsAdapter = new SortPersonsAdapter(this, mPersons);
            recyclerView.setAdapter(mPersonsAdapter);
    
            FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
            fab.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // building new fake person
                    Person person = new Person(
                            buildRandomInt(10) + "\nrank",
                            buildRandomName(5) + " " + buildRandomName(5));
                    // let's keep also basic list updated
                    mPersons.add(person);
                    // let's update adapter
                    mPersonsAdapter.addPerson(person);
                }
            });
        }
    
        private RecyclerView.LayoutManager getLayoutManager() {
            LinearLayoutManager llm = new LinearLayoutManager(this);
            llm.setOrientation(LinearLayoutManager.VERTICAL);
            return llm;
        }
    
        // support method for random names and ranks here
    
    }
    
  3. Implement a RecyclerView Adapter relying on SortedList<Person>. Here it’s important to notice that all Persons are inserted into a SortedList<Person>. Creating a SortedList<T> requires a Callback to be defined for intercepting events as well as for defining sorting criteria. In our case, as you can see, compare method defines criteria for sorting Persons while onInserted method defines what to do when a new Person is inserted (notify a data set change for updating RecyclerView in this case). Please notice also implementation of addPerson method described at point 2. It just adds a Person to the SortedList, because the logic for updating RecyclerView is embedded into the Callback method onInserted mentioned before.

    class SortPersonsAdapter extends RecyclerView.Adapter<SortPersonsAdapter.PersonViewHolder> {
    
        protected static class PersonViewHolder extends RecyclerView.ViewHolder {
    
            View layout;
            TextView txt_rank;
            TextView txt_full_name;
    
            public PersonViewHolder(View itemView) {
                super(itemView);
                layout = itemView;
                txt_rank = (TextView) itemView.findViewById(R.id.txt_rank);
                txt_full_name = (TextView) itemView.findViewById(R.id.txt_full_name);
            }
    
        }
    
        private Context mContext;
        private LayoutInflater mLayoutInflater;
    
        private SortedList<Person> mPersons;
    
        public SortPersonsAdapter(Context context, List<Person> persons) {
            mContext = context;
            mLayoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            mPersons = new SortedList<>(Person.class, new PersonListCallback());
            mPersons.addAll(persons);
        }
    
        public void addPerson(Person person) {
            mPersons.add(person);
        }
    
        @Override
        public int getItemCount() {
            return mPersons.size();
        }
    
        @Override
        public SortPersonsAdapter.PersonViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View itemView = mLayoutInflater.inflate(R.layout.view_person_item, parent, false);
            return new PersonViewHolder(itemView);
        }
    
        @Override
        public void onBindViewHolder(final PersonViewHolder viewHolder, final int position) {
            Person person = mPersons.get(position);
            viewHolder.txt_rank.setText(person.getRank());
            viewHolder.txt_full_name.setText(person.getName());
        }
    
        /**
         * Implementation of callback for getting updates on person list changes.
         */
        private class PersonListCallback extends SortedList.Callback<Person> {
    
            @Override
            public int compare(Person p1, Person p2) {
                String[] rank1 = p1.getStringRank().split("\n");
                String[] rank2 = p2.getStringRank().split("\n");
                int diff = Integer.parseInt(rank1[0]) - Integer.parseInt(rank2[0]);
                return (diff == 0) ? p1.getName().compareTo(p2.getName()) : diff;
            }
    
            @Override
            public void onInserted(int position, int count) {
                notifyItemInserted(position);
            }
    
            @Override
            public void onRemoved(int position, int count) {
                notifyItemRemoved(position);
            }
    
            @Override
            public void onMoved(int fromPosition, int toPosition) {
            }
    
            @Override
            public void onChanged(int position, int count) {
            }
    
            @Override
            public boolean areContentsTheSame(Person oldItem, Person newItem) {
                return false;
            }
    
            @Override
            public boolean areItemsTheSame(Person item1, Person item2) {
                return false;
            }
    
        }
    
    }
    

Hope this could help. Here I’ve put an implementation for your RecyclerView, in case you need more details on code.

###

public int compare(Person p1, Person p2) {
   if(p1.getRank() == p2.getRank()){
      return p1.getName().compareTo(p2.getName());
   }else if(p1.getRank() > p2.getRank()){
      return 1;
   }else{
      return -1;
   }
}  

The sorting depends upon what comparison result is returned. You need to return -1, 0, or 1. In the code snippet, all I am doing is checking the ranks first. If they are the same rank, I compare their names which are Strings and every String has a compareTo( ) which lexicographically compares two strings.

If not, we just sort them based on rank.

You can simplify your compare( ) method further by making your Person class implement Comparable interface. That will allow you to use Collections framework goodies out of the box.

Leave a Reply

Your email address will not be published. Required fields are marked *