В общем случае решить данную проблему можно следующим образом.
Ваш блок состоит из данных трех видов: заголовок силовой части с названием и датой и внизу статичный текст "силовая часть", данные силовой части и данные кардио-части. Начало кардио-части тоже имеет заголовок, но он не содержит данных, поэтому в отдельный вид выделять его не будем, а выведем статичный заголовок в адаптере.
Создаем интерфейс, который будет объединять данные разных типов. Определяем в нем метод для получения типа данных и ID блока для упорядочивания в списке (чтобы данные одного блока шли друг за другом):
public interface Item {
int getItemType ();
long getID();
}
Создаем модели для разных типов данных. Одна будет хранить заголовок, другая условный адрес и еще одна - имя и номер. Как видно, модели отличаются и по типу полей и по их количеству, поэтому для их объединения в одну сущность имплементируем им наш интерфейс, с реализацией его метода, необходимого для сортировки.
заголовок:
public class Header implements Item {
String title;
int type;
long id;
public Header(String title, int type, long id) {
this.title = title;
this.type = type;
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public void setType(int type) {
this.type = type;
}
public void setId(long id) {
this.id = id;
}
@Override
public int getItemType() {
return type;
}
@Override
public long getID() {
return id;
}
}
первая модель:
public class DataOne implements Item {
String adress;
int type;
long id;
public DataOne(String adress, int type, long id) {
this.adress = adress;
this.type = type;
this.id = id;
}
public String getAdress() {
return adress;
}
public void setAdress(String adress) {
this.adress = adress;
}
public void setType(int type) {
this.type = type;
}
public void setId(long id) {
this.id = id;
}
@Override
public int getItemType() {
return type;
}
@Override
public long getID() {
return id;
}
}
вторая модель:
public class DataTwo implements Item {
String name;
int number;
int type;
long id;
public DataTwo(String name, int number, int type, long id) {
this.name = name;
this.number = number;
this.type = type;
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public void setType(int type) {
this.type = type;
}
public void setId(long id) {
this.id = id;
}
@Override
public int getItemType() {
return type;
}
@Override
public long getID() {
return id;
}
}
Теперь делаем адаптер, который будет выводить данные из объектов разного типа, но с одним интерфейсом. Ключевое действие тут - переопределение метода getItemViewType(), который и определяет, какие именно данные (из какого типа модели) выводить в текущей позиции.
Так же добавим заголовок для кардио-части. Алгоритм определения основан на том, что данные отсортированы по типу и когда мы попадаем в ветку второго типа, то на первом элементе значение isSecondIrem = false, потом мы присваиваем ему true, чтобы другие в этом блоке были без заголовка. При переходе в другой блок (HEADER) опять присваиваем ему false:
public class ItemAdapter extends RecyclerView.Adapter<ItemAdapter.ItemHolder> {
private final int HEADER = 0;
private final int TYPE_ITEM1 = 1;
private final int TYPE_ITEM2 = 2;
boolean isSecondItem = false;
ArrayList<Item> items;
public ItemAdapter (ArrayList<Item> items){
this.items = items;
}
@Override
public ItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v;
switch (viewType) {
case TYPE_ITEM1:
// разметка для первого типа
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item1, parent, false);
break;
case TYPE_ITEM2:
// разметка для второго типа
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item2, parent, false);
break;
default:
// разметка заголовка
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.header, parent, false);
}
return new ItemHolder(v);
}
@Override
public void onBindViewHolder(ItemHolder holder, final int position) {
int type = getItemViewType(position);
switch (type) {
case HEADER:
// приводим интерфейс к типу заголовка
Header header = (Header)items.get(position);
// биндим данные в заголовок
holder.title.setText(header.getTitle());
isSecondItem = false;
break;
case TYPE_ITEM1:
// приводим интерфейс к первому типу
DataOne dataOne = (DataOne)items.get(position);
// биндим данные первого типа
holder.adress.setText(dataOne.getAdress());
break;
case TYPE_ITEM2:
// приводим интерфейс ко второму типу
DataTwo dataTwo = (DataTwo)items.get(position);
// биндим данные второго типа
holder.header2.setVisibility(isSecondItem == false? View.VISIBLE: View.GONE);
holder.name.setText(dataTwo.getName());
holder.number.setText(String.valueOf(dataTwo.getNumber()));
isSecondItem = true;
break;
}
}
@Override
public int getItemViewType(int position) {
// определяем какой тип в текущей позиции
int type = items.get(position).getItemType();
if (type == 0) return HEADER;
else if (type == 1) return TYPE_ITEM1;
else return TYPE_ITEM2;
}
@Override
public int getItemCount() {
return items.size();
}
class ItemHolder extends RecyclerView.ViewHolder {
TextView title, name, adress, number, header2;
ItemHolder(View itemView) {
super(itemView);
title = itemView.findViewById(R.id.title);
name = itemView.findViewById(R.id.name);
adress = itemView.findViewById(R.id.adress);
number = itemView.findViewById(R.id.number);
header2 = itemView.findViewById(R.id.header2);
}
}
}
сам заголовок кардио-блока - это просто скрытый по умолчанию виджет с текстом в разметке айтема. Когда нужно показать заголовок мы делаем его видимым.
header.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="8dp">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center" />
</LinearLayout>
item1.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:orientation="vertical">
<TextView
android:id="@+id/adress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
item2.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="8dp">
<TextView
android:id="@+id/header2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="subtitle"
android:visibility="gone" />
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
В активити. Здесь при формировании данных сортируем их по типу, чтобы сначала шли данные одного типа, затем другого. Так же сортируем по ID, чтобы данного одного блока шли друг за другом. Так как интерфейсы не реализуют метод compareTo() проводим сравнение самостоятельно - нужно при сортировке возвращать -1 - если первое меньше второго, 0 - если равны и 1 - если первое больше второго. Все это логично реализуется простым вычитанием при идентификаторах типа, как числа 0, 1 и 2:
public class MainActivity extends Activity {
ArrayList <Item> items = new ArrayList();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
RecyclerView list = findViewById(R.id.list);
list.setHasFixedSize(true);
list.setLayoutManager(new LinearLayoutManager(this));
ItemAdapter adapter = new ItemAdapter(items);
list.setAdapter(adapter);
}
private void init() {
// формируем данные трех типов
for (int i=0; i < 4; i++){
Header header = new Header("title " + i, 0, i);
DataOne dataOne = new DataOne("adress " + i, 1, i);
DataTwo dataTwo = new DataTwo("name " + i, i, 2, i );
// каждый блок с одним ID это заголовок и по два айтема данных
items.add(header);
items.add(dataOne);
items.add(dataOne);
items.add(dataTwo);
items.add(dataTwo);
// сортируем по типу и по ID
Collections.sort(items, new Comparator<Item>() {
public int compare(Item o1, Item o2) {
if (o1.getID() == o2.getID()) return o1.getItemType() - o2.getItemType();
else return (int) (o1.getID() - o2.getID());
}
});
}
}
}
результат:

Значения внизу списка вообще не нужны в данных самого списка (где общее время, вес и тд), их нужно выводить в отдельный виджет (контейнер), который находится под списком.