Поиск…


Вступление

RecyclerView - это более продвинутая версия List View с улучшенной производительностью и дополнительными функциями.

параметры

параметр подробность
адаптер Подкласс RecyclerView.Adapter, ответственный за предоставление представлений, которые представляют элементы в наборе данных
Позиция Позиция элемента данных в адаптере
Индекс Индекс прикрепленного дочернего представления, используемого при вызове getChildAt (int). Контраст с позицией
переплет Процесс подготовки дочернего представления для отображения данных, соответствующих позиции в адаптере
Переработать (просмотреть) Представление, ранее использовавшееся для отображения данных для конкретной позиции адаптера, может быть помещено в кеш для последующего повторного использования для повторного отображения одного и того же типа данных позже. Это может значительно повысить производительность, пропуская первоначальную инфляцию или строительство
Лом (просмотреть) Детский вид, который во время макета вошел во временное состояние. Представления лома могут быть повторно использованы, не становясь полностью отделенными от родительского RecyclerView, либо немодифицированы, если переадресация не требуется или не изменена адаптером, если представление считается грязным
Грязный (просмотреть) Детский вид, который должен быть отсканирован адаптером перед отображением

замечания

RecyclerView - это гибкое представление для предоставления ограниченного окна в большой набор данных.

Перед использованием RecyclerView вам необходимо добавить зависимость библиотеки поддержки в файле build.gradle :

dependencies {
    // Match the version of your support library dependency
    compile 'com.android.support:recyclerview-v7:25.3.1'
}

Вы можете найти последнюю версию номера recyclerview с официального сайта .

Другие связанные темы:

Существуют и другие темы, описывающие компоненты RecyclerView :

Официальная документация

http://developer.android.com/reference/android/support/v7/widget/RecyclerView.html

Старые версии:

  //it requires compileSdkVersion 25
  compile 'com.android.support:recyclerview-v7:25.2.0'
  compile 'com.android.support:recyclerview-v7:25.1.0'
  compile 'com.android.support:recyclerview-v7:25.0.0'

  //it requires compileSdkVersion 24
  compile 'com.android.support:recyclerview-v7:24.2.1' 
  compile 'com.android.support:recyclerview-v7:24.2.0' 
  compile 'com.android.support:recyclerview-v7:24.1.1'  
  compile 'com.android.support:recyclerview-v7:24.1.0'  

  //it requires compileSdkVersion 23
  compile 'com.android.support:recyclerview-v7:23.4.0'
  compile 'com.android.support:recyclerview-v7:23.3.0'
  compile 'com.android.support:recyclerview-v7:23.2.1'
  compile 'com.android.support:recyclerview-v7:23.2.0'
  compile 'com.android.support:recyclerview-v7:23.1.1'
  compile 'com.android.support:recyclerview-v7:23.1.0'
  compile 'com.android.support:recyclerview-v7:23.0.1'
  compile 'com.android.support:recyclerview-v7:23.0.0'

  //it requires compileSdkVersion 22
  compile 'com.android.support:recyclerview-v7:22.2.1'
  compile 'com.android.support:recyclerview-v7:22.2.0'
  compile 'com.android.support:recyclerview-v7:22.1.1'
  compile 'com.android.support:recyclerview-v7:22.1.0'
  compile 'com.android.support:recyclerview-v7:22.0.0'

  //it requires compileSdkVersion 21
  compile 'com.android.support:recyclerview-v7:21.0.3'
  compile 'com.android.support:recyclerview-v7:21.0.2'
  compile 'com.android.support:recyclerview-v7:21.0.0'

Добавление RecyclerView

Добавьте зависимость, как описано в разделе «Замечание», затем добавьте RecyclerView в ваш макет:

<android.support.v7.widget.RecyclerView
    android:id="@+id/my_recycler_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>

После добавления виджета RecyclerView к вашему макету, получите дескриптор объекта, подключите его к диспетчеру макета и прикрепите адаптер для отображаемых данных:

mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);

// set a layout manager (LinearLayoutManager in this example)

mLayoutManager = new LinearLayoutManager(getApplicationContext());
mRecyclerView.setLayoutManager(mLayoutManager);

// specify an adapter
mAdapter = new MyAdapter(myDataset);
mRecyclerView.setAdapter(mAdapter);

Или просто настройте диспетчер компоновки из xml, добавив следующие строки:

xmlns:app="http://schemas.android.com/apk/res-auto"
app:layoutManager="android.support.v7.widget.LinearLayoutManager"

Если вы знаете, что изменения в содержимом RecyclerView не изменят размер макета RecyclerView , используйте следующий код для повышения производительности компонента. Если RecyclerView имеет фиксированный размер, он знает, что сам RecyclerView не будет изменять размер из-за его дочерних элементов, поэтому он не будет вызывать структуру запроса вообще. Он просто обрабатывает само изменение. Если это недействительно независимо от родителя, координатор, макет или что-то еще. (вы можете использовать этот метод еще до установки LayoutManager и Adapter ):

mRecyclerView.setHasFixedSize(true);

RecyclerView предоставляет эти встроенные менеджеры макетов для использования. Таким образом, вы можете создать список, сетку и шахматную сетку с помощью RecyclerView :

  1. LinearLayoutManager показывает элементы в списке вертикальной или горизонтальной прокрутки.
  2. GridLayoutManager показывает элементы в сетке.
  3. StaggeredGridLayoutManager показывает элементы в шахматном порядке.

Более плавная загрузка предметов

Если элементы вашего RecyclerView загружают данные из сети (обычно изображения) или выполняют другую обработку, это может занять значительное количество времени, и вы можете получить элементы на экране, но не полностью загруженные. Чтобы этого избежать, вы можете расширить существующий LinearLayoutManager чтобы предварительно загрузить несколько элементов, прежде чем они станут видимыми на экране:

package com.example;

import android.content.Context;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.OrientationHelper;
import android.support.v7.widget.RecyclerView;

/**
 * A LinearLayoutManager that preloads items off-screen.
 * <p>
 * Preloading is useful in situations where items might take some time to load
 * fully, commonly because they have maps, images or other items that require
 * network requests to complete before they can be displayed.
 * <p>
 * By default, this layout will load a single additional page's worth of items,
 * a page being a pixel measure equivalent to the on-screen size of the
 * recycler view.  This can be altered using the relevant constructor, or
 * through the {@link #setPages(int)} method.
 */
public class PreLoadingLinearLayoutManager extends LinearLayoutManager {
  private int mPages = 1;
  private OrientationHelper mOrientationHelper;

  public PreLoadingLinearLayoutManager(final Context context) {
    super(context);
  }

  public PreLoadingLinearLayoutManager(final Context context, final int pages) {
    super(context);
    this.mPages = pages;
  }

  public PreLoadingLinearLayoutManager(final Context context, final int orientation, final boolean reverseLayout) {
    super(context, orientation, reverseLayout);
  }

  @Override
  public void setOrientation(final int orientation) {
    super.setOrientation(orientation);
    mOrientationHelper = null;
  }

  /**
   * Set the number of pages of layout that will be preloaded off-screen,
   * a page being a pixel measure equivalent to the on-screen size of the
   * recycler view.
   * @param pages the number of pages; can be {@code 0} to disable preloading
   */
  public void setPages(final int pages) {
    this.mPages = pages;
  }

  @Override
  protected int getExtraLayoutSpace(final RecyclerView.State state) {
    if (mOrientationHelper == null) {
      mOrientationHelper = OrientationHelper.createOrientationHelper(this, getOrientation());
    }
    return mOrientationHelper.getTotalSpace() * mPages;
  }
}

Перетаскивание и прокрутка с помощью RecyclerView

Вы можете реализовать функции перетаскивания и удаления с помощью функции RecyclerView без использования сторонних библиотек.
Просто используйте класс ItemTouchHelper включенный в библиотеку поддержки RecyclerView.

ItemTouchHelper экземпляр ItemTouchHelper с SimpleCallback обратного вызова SimpleCallback и в зависимости от того, какую функциональность вы поддерживаете, вы должны переопределить onMove(RecyclerView, ViewHolder, ViewHolder) и / или onSwiped(ViewHolder, int) и, наконец, подключиться к вашему RecyclerView .

ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {

    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
        // remove item from adapter
    }

    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
            final int fromPos = viewHolder.getAdapterPosition();
            final int toPos = target.getAdapterPosition();
            // move item in `fromPos` to `toPos` in adapter.
            return true;// true if moved, false otherwise
        }
    
};

ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback);
itemTouchHelper.attachToRecyclerView(recyclerView);

Стоит отметить, что конструктор SimpleCallback применяет одну и ту же стратегию SimpleCallback всех элементов в RecyclerView . В любом случае возможно обновить направление getSwipeDirs(RecyclerView, ViewHolder) по умолчанию для определенных элементов, просто переопределив метод getSwipeDirs(RecyclerView, ViewHolder) .

Предположим, например, что наш RecyclerView включает в себя HeaderViewHolder и что мы, очевидно, не хотим применять его для прокрутки. Достаточно переопределить getSwipeDirs следующим образом:

@Override
public int getSwipeDirs(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
    if (viewHolder instanceof HeaderViewHolder) {
        // no swipe for header
        return 0;
    }
    // default swipe for all other items
    return super.getSwipeDirs(recyclerView, viewHolder);
} 

Добавить заголовок / нижний колонтитул в RecyclerView

Это пример кода адаптера.

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

private static final int FOOTER_VIEW = 1;

// Define a view holder for Footer view

public class FooterViewHolder extends ViewHolder {
    public FooterViewHolder(View itemView) {
        super(itemView);
        itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // Do whatever you want on clicking the item
            }
        });
    }
}

// Now define the viewholder for Normal list item
public class NormalViewHolder extends ViewHolder {
    public NormalViewHolder(View itemView) {
        super(itemView);

        itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // Do whatever you want on clicking the normal items 
            }
        });
    }
}

// And now in onCreateViewHolder you have to pass the correct view
// while populating the list item.

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

    View v;

    if (viewType == FOOTER_VIEW) {
        v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_footer, parent, false);

        FooterViewHolder vh = new FooterViewHolder(v);

        return vh;
    }

    v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_normal, parent, false);

    NormalViewHolder vh = new NormalViewHolder(v);

    return vh;
}

// Now bind the viewholders in onBindViewHolder
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

    try {
        if (holder instanceof NormalViewHolder) {
            NormalViewHolder vh = (NormalViewHolder) holder;

            vh.bindView(position);
        } else if (holder instanceof FooterViewHolder) {
            FooterViewHolder vh = (FooterViewHolder) holder;
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

// Now the critical part. You have return the exact item count of your list
// I've only one footer. So I returned data.size() + 1
// If you've multiple headers and footers, you've to return total count
// like, headers.size() + data.size() + footers.size()

@Override
public int getItemCount() {
    if (data == null) {
        return 0;
    }

    if (data.size() == 0) {
        //Return 1 here to show nothing
        return 1;
    }

    // Add extra view to show the footer view
    return data.size() + 1;
}

// Now define getItemViewType of your own. 

@Override
public int getItemViewType(int position) {
    if (position == data.size()) {
        // This is where we'll add footer.
        return FOOTER_VIEW;
    }

    return super.getItemViewType(position);
}

// So you're done with adding a footer and its action on onClick. 
// Now set the default ViewHolder for NormalViewHolder

public class ViewHolder extends RecyclerView.ViewHolder {
    // Define elements of a row here
    public ViewHolder(View itemView) {
        super(itemView);
        // Find view by ID and initialize here
    }

    public void bindView(int position) {
        // bindView() method to implement actions
    }
}
}

Вот хорошее представление о реализации RecyclerView с верхним и нижним колонтитулом.

Альтернативный метод:

Хотя приведенный выше ответ будет работать, вы также можете использовать этот подход, используя представление recycler с помощью NestedScrollView Вы можете добавить макет для заголовка, используя следующий подход:

<android.support.v4.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

     <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <include
            layout="@layout/drawer_view_header"
            android:id="@+id/navigation_header"/>

        <android.support.v7.widget.RecyclerView
            android:layout_below="@id/navigation_header"
            android:id="@+id/followers_list"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>

    </RelativeLayout>
</android.support.v4.widget.NestedScrollView>

Или вы также можете использовать LinearLayout с вертикальным выравниванием в вашем NestedScrollView .

Примечание. Это будет работать только с RecyclerView выше 23.2.0

compile 'com.android.support:recyclerview-v7:23.2.0'

Использование нескольких ViewHolders с ItemViewType

Иногда RecyclerView должен будет использовать несколько типов представлений, которые будут отображаться в списке, показанном в пользовательском интерфейсе, и каждому представлению нужен другой макет xml для раздувания.

Для этой проблемы вы можете использовать разные ViewHolders в одном адаптере, используя специальный метод в RecyclerView - getItemViewType(int position) .

Ниже приведен пример использования двух ViewHolders:

  1. ViewHolder для отображения записей в списках

  2. ViewHolder для отображения нескольких видов заголовков

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(context).inflate(viewType, parent, false);
        return ViewHolder.create(itemView, viewType);
    }
    
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        final Item model = this.items.get(position);
        ((ViewHolder) holder).bind(model);
    }
    
    @Override
    public int getItemViewType(int position) {
        return inSearchState ? R.layout.item_header : R.layout.item_entry;
    }
    
    abstract class ViewHolder {
        abstract void bind(Item model);
    
        public static ViewHolder create(View v, int viewType) {
            return viewType == R.layout.item_header ? new HeaderViewHolder(v) :new EntryViewHolder(v);
        }
    }  
    
    static class EntryViewHolder extends ViewHolder {
        private View v;
    
        public EntryViewHolder(View v) {
            this.v = v;
        }
    
        @Override public void bind(Item model) {
            // Bind item data to entry view.
        }
    }
    
    static class HeaderViewHolder extends ViewHolder {
        private View v;
    
        public HeaderViewHolder(View v) {
            this.v = v;
        }
    
        @Override public void bind(Item model) {
            // Bind item data to header view.
        }
    }
    

Фильтрация элементов внутри RecyclerView с помощью SearchView

добавить filter в RecyclerView.Adapter :

public void filter(String text) {
        if(text.isEmpty()){
            items.clear();
            items.addAll(itemsCopy);
        } else{
            ArrayList<PhoneBookItem> result = new ArrayList<>();
            text = text.toLowerCase();
            for(PhoneBookItem item: itemsCopy){
                //match by name or phone
                if(item.name.toLowerCase().contains(text) || item.phone.toLowerCase().contains(text)){
                    result.add(item);
                }
            }
            items.clear();
            items.addAll(result);
        }
        notifyDataSetChanged();
    }

itemsCopy инициализируется в конструкторе адаптера, например itemsCopy.addAll(items) .

Если вы это сделаете, просто вызовите filter из OnQueryTextListener из SearchView :

searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
    @Override
    public boolean onQueryTextSubmit(String query) {
        adapter.filter(query);
        return true;
    }

    @Override
    public boolean onQueryTextChange(String newText) {
        adapter.filter(newText);
        return true;
    }
});

Всплывающее меню с recyclerView

поместите этот код в свой ViewHolder

note: В этом коде я использую btnExpand click-event, для всего события click recyclerview вы можете установить прослушиватель в объект itemView.

public class MyViewHolder extends RecyclerView.ViewHolder{
        CardView cv;
        TextView recordName, visibleFile, date, time;
        Button btnIn, btnExpand;

        public MyViewHolder(final View itemView) {
            super(itemView);

            cv = (CardView)itemView.findViewById(R.id.cardview);
            recordName = (TextView)itemView.findViewById(R.id.tv_record);
            visibleFile = (TextView)itemView.findViewById(R.id.visible_file);
            date = (TextView)itemView.findViewById(R.id.date);
            time = (TextView)itemView.findViewById(R.id.time);
            btnIn = (Button)itemView.findViewById(R.id.btn_in_out);

            btnExpand = (Button) itemView.findViewById(R.id.btn_expand);

            btnExpand.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    PopupMenu popup = new PopupMenu(btnExpand.getContext(), itemView);

                    popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
                        @Override
                        public boolean onMenuItemClick(MenuItem item) {
                            switch (item.getItemId()) {
                                case R.id.action_delete:
                                    moveFile(recordName.getText().toString(), getAdapterPosition());
                                    return true;
                                case R.id.action_play:
                                    String valueOfPath = recordName.getText().toString();
                                    Intent intent = new Intent();
                                    intent.setAction(android.content.Intent.ACTION_VIEW);
                                    File file = new File(valueOfPath);
                                    intent.setDataAndType(Uri.fromFile(file), "audio/*");
                                    context.startActivity(intent);
                                    return true;
                                case R.id.action_share:
                                    String valueOfPath = recordName.getText().toString();
                                    File filee = new File(valueOfPath);
                                    try {
                                        Intent sendIntent = new Intent();
                                        sendIntent.setAction(Intent.ACTION_SEND);
                                        sendIntent.setType("audio/*");
                                        sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(filee));
                                        context.startActivity(sendIntent);
                                    } catch (NoSuchMethodError | IllegalArgumentException | NullPointerException e) {
                                        e.printStackTrace();
                                    } catch (Exception e) {
                                        e.printStackTrace();
                                    }
                                    return true;
                                default:
                                    return false;
                            }
                        }
                    });
                    // here you can inflate your menu
                    popup.inflate(R.menu.my_menu_item);
                    popup.setGravity(Gravity.RIGHT);

                    // if you want icon with menu items then write this try-catch block.
                    try {
                        Field mFieldPopup=popup.getClass().getDeclaredField("mPopup");
                        mFieldPopup.setAccessible(true);
                        MenuPopupHelper mPopup = (MenuPopupHelper) mFieldPopup.get(popup);
                        mPopup.setForceShowIcon(true);
                    } catch (Exception e) {

                    }
                    popup.show();
                }
            });

        }
    }

альтернативный способ отображения значков в меню

try {
    Field[] fields = popup.getClass().getDeclaredFields();
    for (Field field : fields) {
        if ("mPopup".equals(field.getName())) {
            field.setAccessible(true);
            Object menuPopupHelper = field.get(popup);
            Class<?> classPopupHelper = Class.forName(menuPopupHelper
                .getClass().getName());
            Method setForceIcons = classPopupHelper.getMethod(
                "setForceShowIcon", boolean.class);
            setForceIcons.invoke(menuPopupHelper, true);
            break;
        }
    }
} catch (Exception e) {

}

Вот результат:

введите описание изображения здесь

Анимация изменения данных

RecyclerView будет выполнять соответствующую анимацию, если используется какой-либо из методов «уведомлять», за исключением notifyDataSetChanged ; это включает notifyItemChanged , notifyItemInserted , notifyItemMoved , notifyItemRemoved и т. д.

Адаптер должен расширить этот класс вместо RecyclerView.Adapter .

import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;

import java.util.List;

public abstract class AnimatedRecyclerAdapter<T, VH extends RecyclerView.ViewHolder>
        extends RecyclerView.Adapter<VH> {
    protected List<T> models;
            
    protected AnimatedRecyclerAdapter(@NonNull List<T> models) {
        this.models = models;
    }

    //Set new models.
    public void setModels(@NonNull final List<T> models) {
        applyAndAnimateRemovals(models);
        applyAndAnimateAdditions(models);
        applyAndAnimateMovedItems(models);
    }

    //Remove an item at position and notify changes.
    private T removeItem(int position) {
        final T model = models.remove(position);
        notifyItemRemoved(position);
        return model;
    }

    //Add an item at position and notify changes.
    private void addItem(int position, T model) {
        models.add(position, model);
        notifyItemInserted(position);
    }

    //Move an item at fromPosition to toPosition and notify changes.
    private void moveItem(int fromPosition, int toPosition) {
        final T model = models.remove(fromPosition);
        models.add(toPosition, model);
        notifyItemMoved(fromPosition, toPosition);
    }

    //Remove items that no longer exist in the new models.
    private void applyAndAnimateRemovals(@NonNull final List<T> newTs) {
        for (int i = models.size() - 1; i >= 0; i--) {
            final T model = models.get(i);
            if (!newTs.contains(model)) {
                removeItem(i);
            }
        }
    }

    //Add items that do not exist in the old models.
    private void applyAndAnimateAdditions(@NonNull final List<T> newTs) {
        for (int i = 0, count = newTs.size(); i < count; i++) {
            final T model = newTs.get(i);
            if (!models.contains(model)) {
                addItem(i, model);
            }
        }
    }

    //Move items that have changed their position.
    private void applyAndAnimateMovedItems(@NonNull final List<T> newTs) {
        for (int toPosition = newTs.size() - 1; toPosition >= 0; toPosition--) {
            final T model = newTs.get(toPosition);
            final int fromPosition = models.indexOf(model);
            if (fromPosition >= 0 && fromPosition != toPosition) {
                moveItem(fromPosition, toPosition);
            }
        }
    }
}

Вы не должны использовать один и тот же List для setModels и List в адаптере.

Вы объявляете models глобальными переменными. DataModel - только фиктивный класс.

private List<DataModel> models;
private YourAdapter adapter;

Инициализируйте models перед тем, как передать их адаптеру. YourAdapter - это реализация AnimatedRecyclerAdapter .

models = new ArrayList<>();
//Add models
models.add(new DataModel());
//Do NOT pass the models directly. Otherwise, when you modify global models, 
//you will also modify models in adapter.
//adapter = new YourAdapter(models); <- This is wrong.
adapter = new YourAdapter(new ArrayList(models));

Вызовите это после обновления ваших глобальных models .

adapter.setModels(new ArrayList(models));

Если вы не переопределяете equals , сравнение сравнивается по ссылке.

Пример использования SortedList

Android представил класс SortedList вскоре после появления RecyclerView . Этот класс обрабатывает все вызовы метода «уведомлять» в RecyclerView.Adapter для обеспечения правильной анимации и даже позволяет выполнять многократные изменения, поэтому анимации не дрожат.

import android.support.v7.util.SortedList;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.util.SortedListAdapterCallback;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import java.util.List;

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

    private SortedList<DataModel> mSortedList;

    class ViewHolder extends RecyclerView.ViewHolder {

        TextView text;
        CheckBox checkBox;

        ViewHolder(View itemView){
            super(itemView);

            //Initiate your code here...

        }

        void setDataModel(DataModel model) {
            //Update your UI with the data model passed here...
            text.setText(modle.getText());
            checkBox.setChecked(model.isChecked());
        }
    }

    public MyAdapter() {
        mSortedList = new SortedList<>(DataModel.class, new SortedListAdapterCallback<DataModel>(this) {
            @Override
            public int compare(DataModel o1, DataModel o2) {
                //This gets called to find the ordering between objects in the array.
                if (o1.someValue() < o2.someValue()) {
                    return -1;
                } else if (o1.someValue() > o2.someValue()) {
                    return 1;
                } else {
                    return 0;
                }
            }

            @Override
            public boolean areContentsTheSame(DataModel oldItem, DataModel newItem) {
                //This is to see of the content of this object has changed. These items are only considered equal if areItemsTheSame() returned true.

                //If this returns false, onBindViewHolder() is called with the holder containing the item, and the item's position.
                return oldItem.getText().equals(newItem.getText()) && oldItem.isChecked() == newItem.isChecked();
            }

            @Override
            public boolean areItemsTheSame(DataModel item1, DataModel item2) {
                //Checks to see if these two items are the same. If not, it is added to the list, otherwise, check if content has changed.
                return item1.equals(item2);
            }
        });
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = //Initiate your item view here.
        return new ViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        //Just update the holder with the object in the sorted list from the given position
        DataModel model = mSortedList.get(position);
        if (model != null) {
            holder.setDataModel(model);
        }
    }

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

    public void resetList(List<DataModel> models) {
        //If you are performing multiple changes, use the batching methods to ensure proper animation.
        mSortedList.beginBatchedUpdates();
        mSortedList.clear();
        mSortedList.addAll(models);
        mSortedList.endBatchedUpdates();
    }

    //The following methods each modify the data set and automatically handles calling the appropriate 'notify' method on the adapter.
    public void addModel(DataModel model) {
        mSortedList.add(model);
    }

    public void addModels(List<DataModel> models) {
        mSortedList.addAll(models);
    }

    public void clear() {
        mSortedList.clear();
    }

    public void removeModel(DataModel model) {
        mSortedList.remove(model);
    }

    public void removeModelAt(int i) {
        mSortedList.removeItemAt(i);
    }
}

RecyclerView с DataBinding

Вот общий класс ViewHolder, который можно использовать с любым макетом DataBinding. Здесь экземпляр определенного класса ViewDataBinding создается с использованием ViewDataBinding класса View и DataBindingUtil .

import android.databinding.DataBindingUtil;
import android.support.v7.widget.RecyclerView;
import android.view.View;

public class BindingViewHolder<T> extends RecyclerView.ViewHolder{

    private final T binding;

    public BindingViewHolder(View itemView) {
        super(itemView);
        binding = (T)DataBindingUtil.bind(itemView);
    }

    public T getBinding() {
        return binding;
    }
}

После создания этого класса вы можете использовать <layout> в вашем файле макета, чтобы включить привязку данных для этого макета следующим образом:

file name: my_item.xml

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

    <data>
        <variable
            name="item"
            type="ItemModel" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:text="@{item.itemLabel}" />
    </LinearLayout>
</layout>

и вот ваш образец dataModel:

public class ItemModel {
    public String itemLabel;
}

По умолчанию библиотека ViewDataBinding данных Android генерирует класс ViewDataBinding на основе имени файла макета, преобразуя его в регистр Pascal и суффикс «привязки» к нему. В этом примере это будет MyItemBinding для файла макета my_item.xml . Этот класс Binding также будет иметь метод setter, чтобы установить объект, определенный как данные в файле макета ( ItemModel для этого примера).

Теперь, когда у нас есть все части, мы можем реализовать наш адаптер следующим образом:

class MyAdapter extends RecyclerView.Adapter<BindingViewHolder<MyItemBinding>>{
    ArrayList<ItemModel> items = new ArrayList<>();
        
    public MyAdapter(ArrayList<ItemModel> items) {
        this.items = items;
    }

    @Override public BindingViewHolder<MyItemBinding> onCreateViewHolder(ViewGroup parent, int viewType) {
        return new BindingViewHolder<>(LayoutInflater.from(parent.getContext()).inflate(R.layout.my_item, parent, false));
    }

    @Override public void onBindViewHolder(BindingViewHolder<ItemModel> holder, int position) {
        holder.getBinding().setItemModel(items.get(position));
        holder.getBinding().executePendingBindings();
    }

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

Бесконечная прокрутка в Recycleview.

Здесь я поделился фрагментом кода для реализации бесконечной прокрутки в режиме просмотра.

Шаг 1. Сначала сделайте один абстрактный метод в адаптере Recycleview, как показано ниже.

public abstract class ViewAllCategoryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    public abstract void load();
}

Шаг 2: Теперь переопределите метод onBindViewHolder и getItemCount() класса ViewAllCategoryAdapter и вызовите метод Load() как показано ниже.

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
    if ((position >= getItemCount() - 1)) {
        load();
    }
}

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

Шаг 3. Теперь каждая логика бэкэнд завершена, теперь пришло время выполнить эту логику. Просто вы можете переопределить метод загрузки, когда вы создаете объект вашего адаптера. Этот метод автоматически вызывается, пока пользователь достигает в конце списка.

  adapter = new ViewAllCategoryAdapter(CONTEXT, YOURLIST) {
        @Override
        public void load() {

            /* do your stuff here */
            /* This method is automatically call while user reach at end of your list. */
        }
    };
    recycleCategory.setAdapter(adapter);

Теперь метод load() автоматически вызывается во время прокрутки пользователя в конце списка.

Лучшая удача

Показывать представление по умолчанию до загрузки элементов или когда данные недоступны

Скриншот
Скриншот

Класс адаптера

private class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

final int EMPTY_VIEW = 77777;
List<CustomData> datalist = new ArrayList<>();

MyAdapter() {
    super();
}

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

    LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());

    if (viewType == EMPTY_VIEW) {
        return new EmptyView(layoutInflater.inflate(R.layout.nothing_yet, parent, false));
    } else {
        return new ItemView(layoutInflater.inflate(R.layout.my_item, parent, false));
    }
}

@SuppressLint("SetTextI18n")
@Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
    if (getItemViewType(position) == EMPTY_VIEW) {
        EmptyView emptyView = (EmptyView) holder;
        emptyView.primaryText.setText("No data yet");
        emptyView.secondaryText.setText("You're doing good !");
        emptyView.primaryText.setCompoundDrawablesWithIntrinsicBounds(null, new IconicsDrawable(getActivity()).icon(FontAwesome.Icon.faw_ticket).sizeDp(48).color(Color.DKGRAY), null, null);

    } else {
        ItemView itemView = (ItemView) holder;
        // Bind data to itemView
    }
}

@Override
public int getItemCount() {
    return datalist.size() > 0 ? datalist.size() : 1;
}

@Override
public int getItemViewType(int position) {
    if  datalist.size() == 0) {
        return EMPTY_VIEW;
    }
    return super.getItemViewType(position);
}

}

nothing_yet.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_gravity="center"
    android:orientation="vertical"
    android:paddingBottom="100dp"
    android:paddingTop="100dp">

    <TextView
        android:id="@+id/nothingPrimary"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:drawableTint="@android:color/secondary_text_light"
        android:drawableTop="@drawable/ic_folder_open_black_24dp"
        android:enabled="false"
        android:fontFamily="sans-serif-light"
        android:text="No Item's Yet"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:textColor="@android:color/secondary_text_light"
        android:textSize="40sp"
        tools:targetApi="m" />

    <TextView
        android:id="@+id/nothingSecondary"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:enabled="false"
        android:fontFamily="sans-serif-condensed"
        android:text="You're doing good !"
        android:textAppearance="?android:attr/textAppearanceSmall"
        android:textColor="@android:color/tertiary_text_light" />
</LinearLayout>

Я использую FontAwesome с библиотекой Iconics для изображений. Добавьте это в файл build.gradle на уровне приложения.

compile 'com.mikepenz:fontawesome-typeface:4.6.0.3@aar'
compile 'com.mikepenz:iconics-core:2.8.1@aar'

Добавление разделительных линий в элементы RecyclerView

Просто добавьте эти строки в инициализацию

RecyclerView mRecyclerView = (RecyclerView) view.findViewById(recyclerView);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
mRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL));

Добавьте adapter и вызовите .notifyDataSetChanged(); по-прежнему !
Это не встроенная функция Recyclerview, но добавлена ​​в библиотеки поддержки. Поэтому не забудьте включить это в свой файл build.gradle на уровне приложения

compile "com.android.support:appcompat-v7:25.3.1"
compile "com.android.support:recyclerview-v7:25.3.1"

Несколько элементов ItemDecorations могут быть добавлены в один RecyclerView.

Изменение цвета разделителя :

Очень легко установить цвет для украшения предметов.

  1. шаг: создание divider.xml файл , который находится на drawable папке
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="line">
    <size
        android:width="1px"
        android:height="1px"/>
    <solid android:color="@color/divider_color"/>
</shape>
  1. шаг: установка возможности рисования
    // Get drawable object
    Drawable mDivider = ContextCompat.getDrawable(m_jContext, R.drawable.divider);
    // Create a DividerItemDecoration whose orientation is Horizontal
    DividerItemDecoration hItemDecoration = new DividerItemDecoration(m_jContext,
            DividerItemDecoration.HORIZONTAL);
    // Set the drawable on it
    hItemDecoration.setDrawable(mDivider);

horizontal_divider

    // Create a DividerItemDecoration whose orientation is vertical
    DividerItemDecoration vItemDecoration = new DividerItemDecoration(m_jContext,
            DividerItemDecoration.VERTICAL);
    // Set the drawable on it
    vItemDecoration.setDrawable(mDivider);

введите описание изображения здесь



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow