Android
RecyclerView
Szukaj…
Wprowadzenie
RecyclerView to bardziej zaawansowana wersja widoku listy o ulepszonej wydajności i dodatkowych funkcjach.
Parametry
Parametr | Szczegół |
---|---|
Adapter | Podklasa RecyclerView.Adapter odpowiedzialna za udostępnianie widoków reprezentujących elementy w zestawie danych |
Pozycja | Pozycja elementu danych w adapterze |
Indeks | Indeks dołączonego widoku potomnego używany w wywołaniu getChildAt (int). Kontrast z pozycją |
Wiążący | Proces przygotowania widoku potomnego do wyświetlenia danych odpowiadających pozycji w adapterze |
Recykling (zobacz) | Widok poprzednio używany do wyświetlania danych dla określonej pozycji adaptera może zostać umieszczony w pamięci podręcznej w celu późniejszego ponownego wykorzystania w celu późniejszego wyświetlenia tego samego typu danych. Może to drastycznie poprawić wydajność, pomijając wstępne inflowanie układu lub budowę |
Złom (zobacz) | Widok potomny, który wszedł w tymczasowo odłączony stan podczas układu. Widoki złomu mogą być ponownie użyte bez pełnego odłączenia od nadrzędnego RecyclerView, albo niezmodyfikowane, jeśli nie jest wymagane ponowne wiązanie, albo zmodyfikowane przez adapter, jeśli widok został uznany za brudny |
Brudny (zobacz) | Widok potomny, który musi zostać powiązany przez adapter przed wyświetleniem |
Uwagi
RecyclerView
to elastyczny widok zapewniający ograniczone okno na duży zestaw danych.
Przed użyciem RecyclerView
musisz dodać zależność biblioteki wsparcia do pliku build.gradle
:
dependencies {
// Match the version of your support library dependency
compile 'com.android.support:recyclerview-v7:25.3.1'
}
Najnowszy numer wersji recyclinglerview można znaleźć na oficjalnej stronie .
Inne powiązane tematy:
Istnieją inne tematy opisujące komponenty RecyclerView
:
Oficjalna dokumentacja
http://developer.android.com/reference/android/support/v7/widget/RecyclerView.html
Starsza wersja:
//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'
Dodawanie RecyclerView
Dodaj zależność zgodnie z opisem w sekcji Uwaga, a następnie dodaj RecyclerView
do swojego układu:
<android.support.v7.widget.RecyclerView
android:id="@+id/my_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
Po dodaniu widżetu RecyclerView
do układu uzyskaj uchwyt obiektu, podłącz go do menedżera układu i podłącz adapter do danych, które mają być wyświetlane:
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);
Lub po prostu skonfiguruj menedżera układu z XML, dodając następujące wiersze:
xmlns:app="http://schemas.android.com/apk/res-auto"
app:layoutManager="android.support.v7.widget.LinearLayoutManager"
Jeśli wiesz, że zmiany w zawartości RecyclerView
nie zmienią rozmiaru układu RecyclerView
, użyj następującego kodu, aby poprawić wydajność komponentu. Jeśli RecyclerView ma stały rozmiar, wie, że sam RecyclerView nie zmieni rozmiaru ze względu na swoje elementy potomne, więc w ogóle nie wywołuje układu żądania. Po prostu obsługuje samą zmianę. Jeśli unieważniasz, kimkolwiek jest rodzic, koordynatorem, układem lub czymkolwiek. (możesz użyć tej metody nawet przed ustawieniem LayoutManager
i Adapter
):
mRecyclerView.setHasFixedSize(true);
RecyclerView
udostępnia te wbudowane menedżery układu. Aby utworzyć listę, siatkę i siatkę naprzemienną za pomocą RecyclerView
:
- LinearLayoutManager pokazuje elementy na przewijanej liście w pionie lub w poziomie.
- GridLayoutManager pokazuje elementy w siatce.
- StaggeredGridLayoutManager pokazuje elementy w rozłożonej siatce.
Płynniejsze ładowanie przedmiotów
Jeśli elementy w twoim RecyclerView
ładują dane z sieci (zwykle obrazy) lub przeprowadzają inne przetwarzanie, może to zająć znaczną ilość czasu i możesz skończyć z elementami na ekranie, ale nie w pełni załadowanymi. Aby tego uniknąć, możesz rozszerzyć istniejący LinearLayoutManager
o wstępne LinearLayoutManager
pewnej liczby elementów, zanim LinearLayoutManager
się one na ekranie:
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;
}
}
Przeciągnij i upuść oraz przesuń palcem za pomocą RecyclerView
Możesz wdrożyć funkcje przeciągania, aby odrzucić i przeciągnij i upuść za pomocą RecyclerView bez korzystania z bibliotek stron trzecich.
Wystarczy użyć klasy ItemTouchHelper
zawartej w bibliotece obsługi RecyclerView.
ItemTouchHelper
instancję ItemTouchHelper
za pomocą wywołania zwrotnego SimpleCallback
iw zależności od obsługiwanej funkcji powinieneś zastąpić onMove(RecyclerView, ViewHolder, ViewHolder)
i / lub onSwiped(ViewHolder, int)
a na końcu dołączyć do 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);
Warto wspomnieć, że konstruktor SimpleCallback
stosuje tę samą strategię przeciągania do wszystkich elementów w RecyclerView
. W każdym przypadku można zaktualizować domyślny kierunek przeciągania dla określonych elementów, po prostu nadpisując metodę getSwipeDirs(RecyclerView, ViewHolder)
.
Załóżmy na przykład, że nasz RecyclerView
zawiera HeaderViewHolder
i że oczywiście nie chcemy stosować do niego przeciągania. Wystarczy zastąpić getSwipeDirs
w następujący sposób:
@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);
}
Dodaj nagłówek / stopkę do RecyclerView
To jest przykładowy kod adaptera.
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
}
}
}
Oto dobry artykuł na temat implementacji RecyclerView
z nagłówkiem i stopką.
Metoda alternatywna:
Chociaż powyższa odpowiedź zadziała, możesz również użyć tego podejścia, korzystając z widoku recyklera za pomocą NestedScrollView
. Możesz dodać układ nagłówka, stosując następujące podejście:
<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>
Lub możesz także użyć LinearLayout
z wyrównaniem pionowym w swoim NestedScrollView
.
Uwaga: Działa to tylko z RecyclerView
powyżej 23.2.0
compile 'com.android.support:recyclerview-v7:23.2.0'
Używanie kilku ViewHolderów z ItemViewType
Czasami RecyclerView będzie musiał użyć kilku rodzajów widoków, które zostaną wyświetlone na liście wyświetlanej w interfejsie użytkownika, a każdy widok wymaga innego formatu xml układu, aby zostać zawyżonym.
W przypadku tego problemu możesz użyć różnych ViewHolderów w jednym adapterze, używając specjalnej metody w RecyclerView - getItemViewType(int position)
.
Poniżej znajduje się przykład użycia dwóch ViewHolderów:
ViewHolder do wyświetlania wpisów listy
ViewHolder do wyświetlania wielu widoków nagłówka
@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. } }
Filtruj elementy wewnątrz RecyclerView za pomocą SearchView
dodaj metodę filter
w 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
jest inicjowany w konstruktorze adaptera, podobnie jak itemsCopy.addAll(items)
.
Jeśli tak, zadzwoń do filter
z OnQueryTextListener
z 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;
}
});
Menu podręczne z recyclinglerView
umieść ten kod w swoim ViewHolder
Uwaga: W tym kodzie używam btnExpand
click-zdarzenie, na cały recyclerview
kliknij zdarzenie można ustawić słuchacza do obiektu 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();
}
});
}
}
alternatywny sposób wyświetlania ikon w menu
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) {
}
Oto wynik:
Animuj zmianę danych
RecyclerView
wykona odpowiednią animację, jeśli zastosowana zostanie jakakolwiek z metod „notyfikowania”, z wyjątkiem notifyDataSetChanged
; obejmuje to notifyItemChanged
, notifyItemInserted
, notifyItemMoved
, notifyItemRemoved
itp.
Adapter powinien rozszerzyć tę klasę zamiast 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);
}
}
}
}
NIE należy używać tej samej
List
dlasetModels
iList
w adapterze.
Deklarujesz models
jako zmienne globalne. DataModel
jest tylko klasą DataModel
.
private List<DataModel> models;
private YourAdapter adapter;
Zainicjuj models
przed przekazaniem go do adaptera. YourAdapter
to implementacja 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));
Zadzwoń do tego po zaktualizowaniu models
globalnych.
adapter.setModels(new ArrayList(models));
Jeśli nie zastąpisz wartości
equals
, całe porównanie zostanie porównane przez odniesienie.
Przykład użycia SortedList
Android wprowadził klasę SortedList
wkrótce po wprowadzeniu RecyclerView
. Ta klasa obsługuje wszystkie wywołania metody „powiadomienie” do RecyclerView.Adapter
aby zapewnić poprawną animację, a nawet pozwala na grupowanie wielu zmian, dzięki czemu animacje nie drgają.
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 z DataBinding
Oto ogólna klasa ViewHolder, której można używać z dowolnym układem DataBinding. W tym przypadku tworzona jest instancja określonej klasy ViewDataBinding
przy użyciu zawyżonego obiektu View
i klasy narzędzi 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;
}
}
Po utworzeniu tej klasy możesz użyć <layout>
w pliku układu, aby włączyć wiązanie danych dla tego układu w następujący sposób:
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>
a oto twoje przykładowe dane Model:
public class ItemModel {
public String itemLabel;
}
Domyślnie biblioteka Android Data Binding generuje klasę ViewDataBinding
na podstawie nazwy pliku układu, konwertując ją na wielkość liter Pascala i dodając do niej „Binding”. W tym przykładzie byłoby to MyItemBinding
dla pliku układu my_item.xml
. Ta klasa Binding miałaby również metodę ustawiającą, aby ustawić obiekt zdefiniowany jako dane w pliku układu (w tym przykładzie ItemModel
).
Teraz, gdy mamy już wszystkie elementy, możemy zaimplementować nasz adapter w następujący sposób:
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();
}
}
Niekończące się przewijanie w widoku Kosza.
Tutaj udostępniłem fragment kodu służący do implementacji nieskończonego przewijania w widoku kosza.
Krok 1: Najpierw utwórz jedną abstrakcyjną metodę w adapterze Recycleview, jak poniżej.
public abstract class ViewAllCategoryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
public abstract void load();
}
Krok 2: Teraz zastąp metodę onBindViewHolder i getItemCount()
klasy ViewAllCategoryAdapter i wywołaj metodę Load()
jak poniżej.
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
if ((position >= getItemCount() - 1)) {
load();
}
}
@Override
public int getItemCount() {
return YOURLIST.size();
}
Krok 3: Teraz każda logika zaplecza jest zakończona, teraz nadszedł czas na wykonanie tej logiki. To proste, możesz zastąpić metodę ładowania, w której tworzony jest obiekt adaptera. Ta metoda jest wywoływana automatycznie, gdy użytkownik dotrze na końcu listy.
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);
Teraz metoda load()
automatycznie wywołuje podczas przewijania przez użytkownika na końcu listy.
Powodzenia
Pokaż domyślny widok do załadowania elementów lub gdy dane nie są dostępne
Klasa adaptera
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>
Do obrazów używam FontAwesome z Iconics Library. Dodaj to do pliku build.gradle na poziomie aplikacji.
compile 'com.mikepenz:fontawesome-typeface:4.6.0.3@aar'
compile 'com.mikepenz:iconics-core:2.8.1@aar'
Dodaj linie podziału do elementów RecyclerView
Wystarczy dodać te wiersze do inicjalizacji
RecyclerView mRecyclerView = (RecyclerView) view.findViewById(recyclerView);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
mRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL));
Dodaj adapter
i wywołaj .notifyDataSetChanged();
jak zwykle !
To nie jest wbudowana funkcja Recyclerview, ale dodana w bibliotekach pomocniczych. Nie zapomnij więc dołączyć tego do pliku build.gradle na poziomie aplikacji
compile "com.android.support:appcompat-v7:25.3.1"
compile "com.android.support:recyclerview-v7:25.3.1"
Do jednego RecyclerView można dodać wiele elementów dekoracyjnych.
Zmiana koloru dzielnika :
Ustawienie koloru przedmiotu jest dość łatwe.
- krok polega na: utworzeniu pliku
divider.xml
, który znajduje się w folderze z możliwością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>
- krok to: ustawienie rysowania
// 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);
// Create a DividerItemDecoration whose orientation is vertical
DividerItemDecoration vItemDecoration = new DividerItemDecoration(m_jContext,
DividerItemDecoration.VERTICAL);
// Set the drawable on it
vItemDecoration.setDrawable(mDivider);