Разделите элементы по группам в элементах RecyclerView или Grouping Recyclerview, скажем, по дате

Я хочу сгруппировать мой андроид RecyclerView элементы с заголовком, основанным на дате, например:

    1 week ago
    - item
    - item
    - item
    - item
    2 weeks ago
    - item
    - item
    - item

такого рода вещи или группировки на основе какого-то элемента.

5 ответов

Решение

Вот решение, которое я нашел с помощью множества исследований по сети, а также по этой ссылке в блоге, а также Kartikey Kuswhaha, так что это не весь мой кредит, но я просто хочу дать больше ясности этому. ниже приведен код: создайте следующие файлы: PojoOfJsonArray, MainActivity, ListItem, GeneralItem, DateItem, Adapter

PojoOfJsonArray.java - этот файл будет представлять ваш класс POJO или все, что у вас есть в приложении, так:

 public class PojoOfJsonArray  {

    public PojoOfJsonArray(String name, String date) {
        this.name = name;
        this.date = date;
    }

    private String name,date;


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDate() {
        return date;
    }

    public void setDate(String date) {
        this.date = date;
    }
}

MainActivity.java. Это действие, которое вы будете использовать для реализации вашего повторного просмотра:

public class MainActivity extends AppCompatActivity {
    private List<PojoOfJsonArray> myOptions = new ArrayList<>();
    List<ListItem> consolidatedList = new ArrayList<>();

    private RecyclerView mRecyclerView;
    private Adapter adapter;


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

        mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview);
        mRecyclerView.setHasFixedSize(true);

        myOptions.add(new PojoOfJsonArray("name 1", "2016-06-21"));
        myOptions.add(new PojoOfJsonArray("name 2", "2016-06-05"));
        myOptions.add(new PojoOfJsonArray("name 2", "2016-06-05"));
        myOptions.add(new PojoOfJsonArray("name 3", "2016-05-17"));
        myOptions.add(new PojoOfJsonArray("name 3", "2016-05-17"));
        myOptions.add(new PojoOfJsonArray("name 3", "2016-05-17"));
        myOptions.add(new PojoOfJsonArray("name 3", "2016-05-17"));
        myOptions.add(new PojoOfJsonArray("name 2", "2016-06-05"));
        myOptions.add(new PojoOfJsonArray("name 3", "2016-05-17"));

        HashMap<String, List<PojoOfJsonArray>> groupedHashMap = groupDataIntoHashMap(myOptions);


        for (String date : groupedHashMap.keySet()) {
            DateItem dateItem = new DateItem();
            dateItem.setDate(date);
            consolidatedList.add(dateItem);


            for (PojoOfJsonArray pojoOfJsonArray : groupedHashMap.get(date)) {
                GeneralItem generalItem = new GeneralItem();
                generalItem.setPojoOfJsonArray(pojoOfJsonArray);//setBookingDataTabs(bookingDataTabs);
                consolidatedList.add(generalItem);
            }
        }


        adapter = new Adapter(this, consolidatedList);
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        mRecyclerView.setLayoutManager(layoutManager);
        mRecyclerView.setAdapter(adapter);


    }

    private HashMap<String, List<PojoOfJsonArray>> groupDataIntoHashMap(List<PojoOfJsonArray> listOfPojosOfJsonArray) {

        HashMap<String, List<PojoOfJsonArray>> groupedHashMap = new HashMap<>();

        for (PojoOfJsonArray pojoOfJsonArray : listOfPojosOfJsonArray) {

            String hashMapKey = pojoOfJsonArray.getDate();

            if (groupedHashMap.containsKey(hashMapKey)) {
                // The key is already in the HashMap; add the pojo object
                // against the existing key.
                groupedHashMap.get(hashMapKey).add(pojoOfJsonArray);
            } else {
                // The key is not there in the HashMap; create a new key-value pair
                List<PojoOfJsonArray> list = new ArrayList<>();
                list.add(pojoOfJsonArray);
                groupedHashMap.put(hashMapKey, list);
            }
        }


        return groupedHashMap;
    }

}

myOptions - это место, в которое можно использовать для подачи ваших данных. ListItem.java

public abstract class ListItem {

    public static final int TYPE_DATE = 0;
    public static final int TYPE_GENERAL = 1;

    abstract public int getType();
}

GeneralItem.java

public class GeneralItem extends ListItem {
    private PojoOfJsonArray pojoOfJsonArray;

    public PojoOfJsonArray getPojoOfJsonArray() {
        return pojoOfJsonArray;
    }

    public void setPojoOfJsonArray(PojoOfJsonArray pojoOfJsonArray) {
        this.pojoOfJsonArray = pojoOfJsonArray;
    }

    @Override
    public int getType() {
        return TYPE_GENERAL;
    }


}

DateItem.java

public class DateItem extends ListItem {

    private String date;

    public String getDate() {
        return date;
    }

    public void setDate(String date) {
        this.date = date;
    }

    @Override
    public int getType() {
        return TYPE_DATE;
    }
}

Адаптер.java этот адаптер предназначен для рециркулятора, если вы не очень хорошо осведомлены о том, как сделать простое секционное рециркуляцию, тогда я советую вам прочитать об этом и хорошо разбираться в этой области, потому что в любом случае это немного сложнее:

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


    private Context mContext;
    List<ListItem> consolidatedList = new ArrayList<>();

    public Adapter(Context context, List<ListItem> consolidatedList) {
        this.consolidatedList = consolidatedList;
        this.mContext = context;


    }


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

        RecyclerView.ViewHolder viewHolder = null;
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());

        switch (viewType) {

            case ListItem.TYPE_GENERAL:
                View v1 = inflater.inflate(R.layout.items, parent,
                        false);
                viewHolder = new GeneralViewHolder(v1);
                break;

            case ListItem.TYPE_DATE:
                View v2 = inflater.inflate(R.layout.itemsh, parent, false);
                viewHolder = new DateViewHolder(v2);
                break;
        }

        return viewHolder;
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {

        switch (viewHolder.getItemViewType()) {

            case ListItem.TYPE_GENERAL:

                GeneralItem generalItem   = (GeneralItem) consolidatedList.get(position);
                GeneralViewHolder generalViewHolder= (GeneralViewHolder) viewHolder;
                generalViewHolder.txtTitle.setText(generalItem.getPojoOfJsonArray().getName());

                break;

            case ListItem.TYPE_DATE:
                DateItem dateItem = (DateItem) consolidatedList.get(position);
                DateViewHolder dateViewHolder = (DateViewHolder) viewHolder;

                dateViewHolder.txtTitle.setText(dateItem.getDate());
                // Populate date item data here

                break;
        }
    }





    // ViewHolder for date row item
    class DateViewHolder extends RecyclerView.ViewHolder {
        protected TextView txtTitle;

        public DateViewHolder(View v) {
            super(v);
            this.txtTitle = (TextView) v.findViewById(R.id.txt);

        }
    }

    // View holder for general row item
    class GeneralViewHolder extends RecyclerView.ViewHolder {
        protected TextView txtTitle;

        public GeneralViewHolder(View v) {
            super(v);
            this.txtTitle = (TextView) v.findViewById(R.id.txt);

        }
    }

    @Override
    public int getItemViewType(int position) {
        return consolidatedList.get(position).getType();
    }

    @Override
    public int getItemCount() {
        return consolidatedList != null ? consolidatedList.size() : 0;
    }

}

и это имеет две схемы использования. таким образом все

Я просто сообщаю здесь котлинскую версию ответа kinsley kajiva .

Примечание . В этой версии используется привязка данных , поэтому не забудьте включить ее в app build.gradle:

      android {
    ...
    buildFeatures {
        dataBinding true
    }
}

PojoOfJsonArray.kt

      data class PojoOfJsonArray(
    val name: String,
    val date: String
)

MainActivity.kt

      class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)

        binding.list.setHasFixedSize(true)

        val myOptions = listOf(
            PojoOfJsonArray("name 1", "2016-06-21"),
            PojoOfJsonArray("name 2", "2016-06-05"),
            PojoOfJsonArray("name 2", "2016-06-05"),
            PojoOfJsonArray("name 3", "2016-05-17"),
            PojoOfJsonArray("name 3", "2016-05-17"),
            PojoOfJsonArray("name 3", "2016-05-17"),
            PojoOfJsonArray("name 3", "2016-05-17"),
            PojoOfJsonArray("name 2", "2016-06-05"),
            PojoOfJsonArray("name 3", "2016-05-17")
        )

        val groupedMapMap: Map<String, List<PojoOfJsonArray>> = myOptions.groupBy {
            it.date
        }

        val consolidatedList = mutableListOf<ListItem>()
        for (date:String in groupedMapMap.keys){
            consolidatedList.add(DateItem(date))
            val groupItems: List<PojoOfJsonArray>? = groupedMapMap[date]
            groupItems?.forEach {
                consolidatedList.add(GeneralItem(it.name))
            }
        }

        val adapter = Adapter(consolidatedList)
        val layoutManager = LinearLayoutManager(this)
        layoutManager.orientation = LinearLayoutManager.VERTICAL
        binding.list.layoutManager = layoutManager
        binding.list.adapter = adapter
    }
}

activity_main.xml

      <?xml version="1.0" encoding="utf-8"?>
<layout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/list"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:listitem="@layout/general_item"/>

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

ListItem.kt

      open class ListItem(
    val type: Int
) {
    companion object {
        const val TYPE_DATE = 0
        const val TYPE_GENERAL = 1
    }
}

GeneralItem.kt

      class GeneralItem(
    var name: String,
) : ListItem(TYPE_GENERAL)

DateItem.kt

      class DateItem(
    val date: String
) : ListItem(TYPE_DATE)

Adapter.kt

      class Adapter(
    private val items: List<ListItem>,
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val layoutInflater = LayoutInflater.from(parent.context)
        return when (viewType) {
            ListItem.TYPE_DATE ->
                DateViewHolder(DateItemBinding.inflate(layoutInflater))
            else ->
                GeneralViewHolder(GeneralItemBinding.inflate(layoutInflater))
        }
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        when (holder.itemViewType) {
            ListItem.TYPE_DATE -> (holder as DateViewHolder).bind(
                item = items[position] as DateItem,
            )
            ListItem.TYPE_GENERAL -> (holder as GeneralViewHolder).bind(
                item = items[position] as GeneralItem
            )
        }
    }

    override fun getItemViewType(position: Int): Int {
        return items[position].type
    }

    override fun getItemCount(): Int {
        return items.size
    }

    inner class DateViewHolder(val binding: DateItemBinding) : RecyclerView.ViewHolder(binding.root) {
        fun bind(item: DateItem) {
            binding.txtDate.text = item.date
        }
    }

    inner class GeneralViewHolder(val binding: GeneralItemBinding) : RecyclerView.ViewHolder(binding.root) {
        fun bind(item: GeneralItem) {
            binding.txtTitle.text = item.name
        }
    }

}

date_item.xml

      <?xml version="1.0" encoding="utf-8"?>
<layout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:id="@+id/txt_date"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="8dp"
            android:textStyle="bold"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:text="01/01/2021"/>

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

general_item.xml

      <?xml version="1.0" encoding="utf-8"?>
<layout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:id="@+id/txt_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="8dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:text="Name"/>

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

Вы можете использовать библиотеку https://github.com/luizgrp/SectionedRecyclerViewAdapter, чтобы легко сгруппировать данные в разделы и добавить заголовок к каждому разделу.

Сначала создайте класс Section:

class MySection extends StatelessSection {

    String title;
    List<String> list;

    public MySection(String title, List<String> list) {
        // call constructor with layout resources for this Section header, footer and items 
        super(R.layout.section_header, R.layout.section_item);

        this.title = title;
        this.list = list;
    }

    @Override
    public int getContentItemsTotal() {
        return list.size(); // number of items of this section
    }

    @Override
    public RecyclerView.ViewHolder getItemViewHolder(View view) {
        // return a custom instance of ViewHolder for the items of this section
        return new MyItemViewHolder(view);
    }

    @Override
    public void onBindItemViewHolder(RecyclerView.ViewHolder holder, int position) {
        MyItemViewHolder itemHolder = (MyItemViewHolder) holder;

        // bind your view here
        itemHolder.tvItem.setText(list.get(position));
    }

    @Override
    public RecyclerView.ViewHolder getFooterViewHolder(View view) {
        return new MyFooterViewHolder(view);
    }

    @Override
    public void onBindFooterViewHolder(RecyclerView.ViewHolder holder) {
        MyFooterViewHolder footerHolder = (MyFooterViewHolder) holder;

        // bind your footer view here
        footerHolder.tvItem.setText(title);
    }
}

Затем вы настраиваете RecyclerView с вашими разделами:

// Create an instance of SectionedRecyclerViewAdapter 
SectionedRecyclerViewAdapter sectionAdapter = new SectionedRecyclerViewAdapter();

// Create your sections with the list of data for each year
MySection section1 = new MySection("1 week ago", week1data);
MySection section2 = new MySection("2 weeks ago", week2data);

// Add your Sections to the adapter
sectionAdapter.addSection(section1);
sectionAdapter.addSection(section2);

// Set up your RecyclerView with the SectionedRecyclerViewAdapter
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(sectionAdapter);

Я хочу просто оптимизировать следующие вещи в ответе user6334851

  1. Kartikey Kuswhaha использовал два основных цикла и несколько вложенных циклов для консолидации списка, в то время как это можно сделать, пройдя только один цикл и не группируя данные в . В следующем методе используется возможностьSetхранить в нем только уникальные объекты.

.

          /** this method takes a List of PojoOfJsonArray and consolidate the list in this format
 *  item's Date
item
item
item
item
item's Date
item
item
item
 * @param list PojoOfJsonArray to be consolidated
 * @return consolidate list
 */

private static List<ListItem> consolidateList(List<PojoOfJsonArray> list){
    Set<String> set = new HashSet<String>();
    List<ListItem> consolidatedList = new ArrayList<>();

    for(PojoOfJsonArray item: list) {
        
        if(set.add(item.getDate())) {
            //add DateItem to the consolidatedList only when 
            //the item's date can be inserted in the set
            //(as Set only allows unique object inside it)
            //this implies that the item's date is not previously inserted into the list
            
            DateItem dateItem = new DateItem();
            dateItem.setDate(item.getDate());
            
            
            consolidatedList.add(dateItem);
        }
        
        //add general item into the consolidated list
        GeneralItem generalItem = new GeneralItem();
        generalItem.setPojoOfJsonArray(item);
        consolidatedList.add(generalItem);
    }
    return consolidatedList;
}
  1. Более того, он использовалHashMapв первую группу данных, которая при получении набора ключей возвращает ключ (в данном случае даты) в случайном порядке. Это можно решить с помощьюLinkedHashMap. Однако, если вы используете 1-й вариант (этого ответа)

Я решил эту проблему с помощью библиотеки Section RecyclerView и с помощью этих функций (Gist), чтобы проверить, является ли дата "Сегодня", "Вчера" и так далее.

Я надеюсь построить для этого сквозную библиотеку. Дайте мне знать, если вы заинтересованы, тогда я могу создать и с открытым исходным кодом.

Пример скриншота ниже:

Снимок экрана пользовательского интерфейса, показывающий разделы даты

Другие вопросы по тегам