4

Мне приходит JSON массив с сервера с датой и количеством сообщений. Мне нужно сделать список в котором дата будет находиться перед началом сообщений этой даты. Никак не пойму как можно научить адаптер строить такой список.

Помогите, покажите пальцем, куда копать?

Есть примерный способ реализации:

  1. Завести массив с датами сообщений в формате HH:mm

  2. ???

SVE
  • 22,387
  • вам нужно использовать несколько типов View в адаптере – Vladyslav Matviienko Oct 03 '16 at 07:48
  • Их уже два) Сообщения чьи-то и свои) Я не пойму как его вставлять правильно, чтобы он был над сообщениями. К тому же у меря происходит много операцией над позицией, хедер мешаться будет –  Oct 03 '16 at 07:50
  • Нужно в onBindView() вставлять эту дату, но как её вставлять до сообщения со следующей датой –  Oct 03 '16 at 07:54
  • правильно будет добавить еще один тип View в адаптер. Неправильно - можете добавить в уже существующие вью элемент, который будет скрываться и показываться в зависимости от того, надо показывать дату, или ненадо – Vladyslav Matviienko Oct 03 '16 at 08:02
  • @metalurgus При такой структуре данных (каждая запись содержит дату и текст) все не так просто, чтобы сгруппировать данные по датам в заголовках в плане того, что при изменении даты, если выводить другой View с датой, то текст теряется. Я делал два типа View так, что один - только текст, второй дата и текст (выводится при изменении даты), но это как то не тру, на мой взгляд :) – pavlofff Oct 03 '16 at 08:17
  • 1
    не делайте велосипеды https://github.com/timehop/sticky-headers-recyclerview – georgehardcore Oct 03 '16 at 10:17
  • 1
    Не ищу простых путей –  Oct 03 '16 at 10:21
  • ну тогда вам костыльно массив придется составлять, чтобы все шло по очереди – georgehardcore Oct 03 '16 at 10:24
  • Ну да) Так и сделаю –  Oct 03 '16 at 10:28
  • 1
    @СергейГрушин сделаете DateItem и MessageItem, при считывании из JSON, если дата меняется добавляйте DateItem, потом в getItemViewType используйте instanceof, хотя в ответе тоже неплохо – georgehardcore Oct 03 '16 at 10:34

1 Answers1

3

Организация данных такая, что нужно распарсить JSON в объекты модели, каждая модель будет содержать поле даты и поле записи. Коллекцию необходимо отсортировать по возрастанию даты.

Для начала нам понадобится два типа разметок. Первая будет отображать дату и запись (layout.header), вторая - только запись (layout.record). Разметка layout.header включает в себя разметку layout.record через include.

layout.record:

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
    >

    <TextView 
        android:layout_width="match_parent"
        android:layout_height="wrap_content" 
        android:id = "@+id/record" />

</LinearLayout>

layout.header:

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
    >

    <TextView 
        android:layout_width="match_parent"
        android:layout_height="wrap_content" 
        android:id = "@+id/date" />

    <include
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>

Логика определения, какой тип View выводить основана на сравнении даты текущей записи и прошлой - если они не равны, то выводить заголовок с датой и записью, иначе только запись. Нулевой элемент обрабатывается отдельно и всегда содержит дату и запись.

Так же прошу обратить внимание на организацию блока switch - case в методе onBindViewHolder(). При выводе View с датой case выводит дату на разметку и "проваливается" на следующий case (нет оператора break), где на разметку выводится запись. Если заголовок не требуется, то на разметку дата не выводится (срабатывает второй case)

class SomeAdapter extends RecyclerView.Adapter <SomeAdapter.ItemHolder> {

    private ArrayList <Data>  mData;
    private final int TYPE_HEADER = 0;
    private final int TYPE_ITEM = 1;


    public SomeAdapter (ArrayList <Data> data) {
        mData = data;
    }

    @Override
    public ItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v;

        switch (viewType) {

            case TYPE_HEADER:
                v = LayoutInflater.from(parent.getContext()).inflate(R.layout.header, parent, false);
                break;
            default:
                v = LayoutInflater.from(parent.getContext()).inflate(R.layout.record, parent, false);
        }
      return new ItemHolder(v);
    }

    @Override
    public void onBindViewHolder( ItemHolder holder, int position) {


        int type = getItemViewType(position);

        switch (type) {

            case TYPE_HEADER:
                holder.mHeaderDate.setText(mData.get(position).getDate()));

            case TYPE_ITEM:
                holder.mItemRecord.setText(mData.get(position).getRecord());

                break;
        }
    }

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

    @Override
    public int getItemViewType(int position) {

        if (isIdentType(position)) return TYPE_ITEM;
        return TYPE_HEADER;
    }

    private boolean isIdentType (int position ){

        if (!(position == 0)&&(mData.get(position).getDate()).equals(mData.get(position-1).getDate())) return true;
        return false;
    }

public static class ItemHolder extends RecyclerView.ViewHolder{

        TextView mHeaderDate;
        TextView mItemRecord;

        public ItemHolder(View v) {
            super(v);

            mHeaderDate = (TextView) v.findViewById(R.id.date);
            mItemRecord = (TextView) v.findViewById(R.id.record);
       }

    }

Естественно, разметки нужно оформить покрасивее как то, выделить дату можно другим фоном или другое. Данный пример - только идея.

pavlofff
  • 36,765
  • 1
    Кажется можно сделать проще - только один тип разметки, который имеет поле для даты сверху. Оно по умолчанию с видимостью GONE. И в onBindViewHolder просто проверять надо или нет показывать и менять текст/видимость – ЮрийСПб Oct 03 '16 at 11:39
  • 2
    @ЮрийСПб Так можно сделать, но это будет по сути то же самое, что и в ответе, только реализовано не средствами, предусмотренными адаптером, а "самодельной" реализацией. Из минусов - метод биндинга данных на айтем лучше не перегружать бизнес-логикой, если это возможно, особенно, когда данных не на два TextView (где я это использовал в заголовке было три виджета, а в айтеме еще 5) - быстрее крутиться будет. К тому же, реализация в ответе дает возможность оформить список более свободно – pavlofff Oct 03 '16 at 11:53