Search code examples
androidandroid-recyclerviewandroid-viewholderstickyrecycleview

How to sort items in RecyclerView depending on a Date


enter image description here

This is what my screen should look like.

I added this library https://github.com/ShamylZakariya/StickyHeaders and tried to do it in similar way.

My model looks like this:

public class Transaction {

private int id;
private Date createdDate;
private String description;
private int amount;
private float newCredits;
}

and

public class Transactions {

private int totalCount;
private List<Transaction> transactions = new ArrayList<>();
}

So after i fetch all my transactions I set the data in adapter.. this is what my adapter looks like:

public class WalletAdapter extends SectioningAdapter {

private static final boolean USE_DEBUG_APPEARANCE = false;

private Transactions transactions;
private List<Section> sections = new ArrayList<>();

public WalletAdapter() {
}

private class Section {
    String alpha;
    Transactions transactions;
}

public Transactions getTransactions() {
    return transactions;
}

public void setTransactions(Transactions transactions) {
    this.transactions = transactions;
    sections.clear();

    char alpha = 0;

    Section currentSection = null;
    for (Transaction transaction : transactions.getTransactions()) {
        String date = parseDate(transaction.getCreatedDate());
        if (date.charAt(0) != alpha) {
            if (currentSection != null) {
                sections.add(currentSection);
            }

            currentSection = new Section();
            alpha = date.charAt(0);
            currentSection.alpha = String.valueOf(alpha);
        }

        if (currentSection != null) {
            currentSection.transactions = this.transactions;
        }
    }

    sections.add(currentSection);
    notifyAllSectionsDataSetChanged();
}

private String parseDate(Date date) {
    DateFormat df = new SimpleDateFormat("dd.MM.yyyy", Locale.getDefault());
    String formattedDate = "";
    formattedDate = df.format(date);
    return formattedDate;
}

@Override
public int getNumberOfSections() {
    return sections.size();
}

@Override
public boolean doesSectionHaveHeader(int sectionIndex) {
    return true;
}

@Override
public boolean doesSectionHaveFooter(int sectionIndex) {
    return false;
}

@Override
public int getNumberOfItemsInSection(int sectionIndex) {
    return sections.get(sectionIndex).transactions.getTransactions().size();
}

@Override
public ItemViewHolder onCreateItemViewHolder(ViewGroup parent, int itemUserType) {
    LayoutInflater inflater = LayoutInflater.from(parent.getContext());
    View v = inflater.inflate(R.layout.wallet_item, parent, false);
    return new ItemViewHolder(v);
}

@Override
public HeaderViewHolder onCreateHeaderViewHolder(ViewGroup parent, int headerUserType) {
    LayoutInflater inflater = LayoutInflater.from(parent.getContext());
    View v = inflater.inflate(R.layout.wallet_header, parent, false);
    return new HeaderViewHolder(v);
}

@Override
public void onBindItemViewHolder(SectioningAdapter.ItemViewHolder viewHolder, int sectionIndex, int itemIndex, int itemType) {
    Section section = sections.get(sectionIndex);
    ItemViewHolder holder = (ItemViewHolder) viewHolder;
    Transaction transaction = section.transactions.getTransactions().get(itemIndex);
    holder.description.setText(transaction.getDescription());
    holder.ammount.setText(String.valueOf(transaction.getAmount()));
    holder.time.setText(parseDate(transaction.getCreatedDate()));
    holder.total.setText(String.valueOf(transaction.getNewCredits()));

}

@Override
public void onBindHeaderViewHolder(SectioningAdapter.HeaderViewHolder viewHolder, int sectionIndex, int headerType) {
    Section s = sections.get(sectionIndex);
    HeaderViewHolder hvh = (HeaderViewHolder) viewHolder;
    Transaction transaction = s.transactions.getTransactions().get(sectionIndex);
    if (USE_DEBUG_APPEARANCE) {
        hvh.itemView.setBackgroundColor(0x55ffffff);
        hvh.dateHeader.setText(pad(sectionIndex * 2) + s.alpha);
    } else {
        hvh.dateHeader.setText(parseDate(transaction.getCreatedDate()));
    }
}

private String pad(int spaces) {
    StringBuilder b = new StringBuilder();
    for (int i = 0; i < spaces; i++) {
        b.append(' ');
    }
    return b.toString();
}


public class HeaderViewHolder extends SectioningAdapter.HeaderViewHolder {

    TextView dateHeader;

    public HeaderViewHolder(View itemView) {
        super(itemView);
        dateHeader = (TextView) itemView.findViewById(R.id.date);
    }
}

public class ItemViewHolder extends SectioningAdapter.ItemViewHolder {

    TextView description;
    TextView time;
    TextView ammount;
    TextView total;

    public ItemViewHolder(View itemView) {
        super(itemView);
        description = (TextView) itemView.findViewById(R.id.description);
        time = (TextView) itemView.findViewById(R.id.time);
        ammount = (TextView) itemView.findViewById(R.id.ammount);
        total = (TextView) itemView.findViewById(R.id.new_credits);
    }
}

}

So if I do it this way, It sorts and creates headers/sections by date, but it puts all transactions in all sections, not inside matching date...


Solution

  • Seems like this should handle if item should have header or no. Smth like, if it is first item with this date return true, if not - false.

    @Override
    public boolean doesSectionHaveHeader(int sectionIndex) {
        return true;
    }
    

    EDIT:

    You can make filtered list before setting it. For example for address book with name letter header:

    public class AddressBookDemoAdapter extends SectioningAdapter {
    
        Locale locale = Locale.getDefault();
        static final boolean USE_DEBUG_APPEARANCE = false;
    
        private class Section {
            String alpha;
            ArrayList<Person> people = new ArrayList<>();
        }
    
        public class ItemViewHolder extends SectioningAdapter.ItemViewHolder {
            TextView personNameTextView;
    
            public ItemViewHolder(View itemView) {
                super(itemView);
                personNameTextView = (TextView) itemView.findViewById(R.id.personNameTextView);
            }
        }
    
        public class HeaderViewHolder extends SectioningAdapter.HeaderViewHolder {
            TextView titleTextView;
    
            public HeaderViewHolder(View itemView) {
                super(itemView);
                titleTextView = (TextView) itemView.findViewById(R.id.titleTextView);
            }
        }
    
    
        List<Person> people;
        ArrayList<Section> sections = new ArrayList<>();
    
        public AddressBookDemoAdapter() {
        }
    
        public List<Person> getPeople() {
            return people;
        }
    
        public void setPeople(List<Person> people) {
            this.people = people;
            sections.clear();
    
            // sort people into buckets by the first letter of last name
            char alpha = 0;
            Section currentSection = null;
            for (Person person : people) {
                if (person.name.last.charAt(0) != alpha) {
                    if (currentSection != null) {
                        sections.add(currentSection);
                    }
    
                    currentSection = new Section();
                    alpha = person.name.last.charAt(0);
                    currentSection.alpha = String.valueOf(alpha);
                }
    
                if (currentSection != null) {
                    currentSection.people.add(person);
                }
            }
    
            sections.add(currentSection);
            notifyAllSectionsDataSetChanged();
        }
    
        @Override
        public int getNumberOfSections() {
            return sections.size();
        }
    
        @Override
        public int getNumberOfItemsInSection(int sectionIndex) {
            return sections.get(sectionIndex).people.size();
        }
    
        @Override
        public boolean doesSectionHaveHeader(int sectionIndex) {
            return true;
        }
    
        @Override
        public boolean doesSectionHaveFooter(int sectionIndex) {
            return false;
        }
    
        @Override
        public ItemViewHolder onCreateItemViewHolder(ViewGroup parent, int itemType) {
            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
            View v = inflater.inflate(R.layout.list_item_addressbook_person, parent, false);
            return new ItemViewHolder(v);
        }
    
        @Override
        public HeaderViewHolder onCreateHeaderViewHolder(ViewGroup parent, int headerType) {
            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
            View v = inflater.inflate(R.layout.list_item_addressbook_header, parent, false);
            return new HeaderViewHolder(v);
        }
    
        @SuppressLint("SetTextI18n")
        @Override
        public void onBindItemViewHolder(SectioningAdapter.ItemViewHolder viewHolder, int sectionIndex, int itemIndex, int itemType) {
            Section s = sections.get(sectionIndex);
            ItemViewHolder ivh = (ItemViewHolder) viewHolder;
            Person person = s.people.get(itemIndex);
            ivh.personNameTextView.setText(capitalize(person.name.last) + ", " + capitalize(person.name.first));
        }
    
        @SuppressLint("SetTextI18n")
        @Override
        public void onBindHeaderViewHolder(SectioningAdapter.HeaderViewHolder viewHolder, int sectionIndex, int headerType) {
            Section s = sections.get(sectionIndex);
            HeaderViewHolder hvh = (HeaderViewHolder) viewHolder;
    
            if (USE_DEBUG_APPEARANCE) {
                hvh.itemView.setBackgroundColor(0x55ffffff);
                hvh.titleTextView.setText(pad(sectionIndex * 2) + s.alpha);
            } else {
                hvh.titleTextView.setText(s.alpha);
            }
        }
    
        private String capitalize(String s) {
            if (s != null && s.length() > 0) {
                return s.substring(0,1).toUpperCase(locale) + s.substring(1);
            }
    
            return "";
        }
    
        private String pad(int spaces) {
            StringBuilder b = new StringBuilder();
            for (int i = 0; i < spaces; i++) {
                b.append(' ');
            }
            return b.toString();
        }
    
    }