Android
RecyclerView en LayoutManagers
Zoeken…
GridLayoutManager met dynamische overspanning
Bij het maken van een recyclerview met een lay-outbeheer voor rasterlayout, moet u het aantal reeksen opgeven in de constructor. Bereiktelling verwijst naar het aantal kolommen. Dit is vrij onhandig en houdt geen rekening met grotere schermformaten of schermoriëntatie. Een benadering is om meerdere indelingen te maken voor de verschillende schermformaten. Een andere meer dynamische benadering is hieronder te zien.
Eerst maken we een aangepaste RecyclerView-klasse als volgt:
public class AutofitRecyclerView extends RecyclerView {
private GridLayoutManager manager;
private int columnWidth = -1;
public AutofitRecyclerView(Context context) {
super(context);
init(context, null);
}
public AutofitRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public AutofitRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
if (attrs != null) {
int[] attrsArray = {
android.R.attr.columnWidth
};
TypedArray array = context.obtainStyledAttributes(attrs, attrsArray);
columnWidth = array.getDimensionPixelSize(0, -1);
array.recycle();
}
manager = new GridLayoutManager(getContext(), 1);
setLayoutManager(manager);
}
@Override
protected void onMeasure(int widthSpec, int heightSpec) {
super.onMeasure(widthSpec, heightSpec);
if (columnWidth > 0) {
int spanCount = Math.max(1, getMeasuredWidth() / columnWidth);
manager.setSpanCount(spanCount);
}
}
}
Deze klasse bepaalt hoeveel kolommen in de recyclerview passen. Om het te gebruiken, moet u het als volgt in uw layout.xml plaatsen:
<?xml version="1.0" encoding="utf-8"?>
<com.path.to.your.class.autofitRecyclerView.AutofitRecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/auto_fit_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:columnWidth="200dp"
android:clipToPadding="false"
/>
Merk op dat we het kenmerk columnWidth gebruiken. De recyclerview heeft het nodig om te bepalen hoeveel kolommen in de beschikbare ruimte passen.
In je activiteit / fragment krijg je gewoon een verwijzing naar de recylerview en stel je een adapter in (en alle itemdecoraties of animaties die je wilt toevoegen). STEL GEEN LAY-OUT MANAGER IN
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.auto_fit_recycler_view);
recyclerView.setAdapter(new MyAdapter());
(waar MyAdapter uw adapterklasse is)
U hebt nu een recyclerview die de hoeveelheid (dwz kolommen) aanpast aan de schermgrootte. Als laatste toevoeging wilt u misschien de kolommen in de recyclerview centreren (standaard zijn ze uitgelijnd met layout_start). U kunt dat doen door de AutofitRecyclerView-klasse een beetje aan te passen. Begin met het creëren van een binnenklasse in de recyclerview. Dit wordt een klasse die zich uitstrekt van GridLayoutManager. Het voegt voldoende opvulling links en rechts toe om de rijen te centreren:
public class AutofitRecyclerView extends RecyclerView {
// etc see above
private class CenteredGridLayoutManager extends GridLayoutManager {
public CenteredGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public CenteredGridLayoutManager(Context context, int spanCount) {
super(context, spanCount);
}
public CenteredGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) {
super(context, spanCount, orientation, reverseLayout);
}
@Override
public int getPaddingLeft() {
final int totalItemWidth = columnWidth * getSpanCount();
if (totalItemWidth >= AutofitRecyclerView.this.getMeasuredWidth()) {
return super.getPaddingLeft(); // do nothing
} else {
return Math.round((AutofitRecyclerView.this.getMeasuredWidth() / (1f + getSpanCount())) - (totalItemWidth / (1f + getSpanCount())));
}
}
@Override
public int getPaddingRight() {
return getPaddingLeft();
}
}
}
Wanneer u vervolgens de LayoutManager in AutofitRecyclerView instelt, gebruikt u CenteredGridLayoutManager als volgt:
private void init(Context context, AttributeSet attrs) {
if (attrs != null) {
int[] attrsArray = {
android.R.attr.columnWidth
};
TypedArray array = context.obtainStyledAttributes(attrs, attrsArray);
columnWidth = array.getDimensionPixelSize(0, -1);
array.recycle();
}
manager = new CenteredGridLayoutManager(getContext(), 1);
setLayoutManager(manager);
}
En dat is het! U hebt een dynamische, centraal uitgelijnde op gridlayoutmanager gebaseerde recyclerview.
bronnen:
Kopweergave toevoegen aan recyclerview met gridlayout manager
Om een koptekst toe te voegen aan een recyclerview met een gridlayout, moet de adapter eerst worden verteld dat de koptekstweergave de eerste positie is en niet de standaardcel die wordt gebruikt voor de inhoud. Vervolgens moet de lay-outmanager worden verteld dat de eerste positie een overspanning moet hebben die gelijk is aan de * overspanning van de hele lijst. *
Neem een normale RecyclerView.Adapter-klasse en configureer deze als volgt:
public class HeaderAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int ITEM_VIEW_TYPE_HEADER = 0;
private static final int ITEM_VIEW_TYPE_ITEM = 1;
private List<YourModel> mModelList;
public HeaderAdapter (List<YourModel> modelList) {
mModelList = modelList;
}
public boolean isHeader(int position) {
return position == 0;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
if (viewType == ITEM_VIEW_TYPE_HEADER) {
View headerView = inflater.inflate(R.layout.header, parent, false);
return new HeaderHolder(headerView);
}
View cellView = inflater.inflate(R.layout.gridcell, parent, false);
return new ModelHolder(cellView);
}
@Override
public int getItemViewType(int position) {
return isHeader(position) ? ITEM_VIEW_TYPE_HEADER : ITEM_VIEW_TYPE_ITEM;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder h, int position) {
if (isHeader(position)) {
return;
}
final YourModel model = mModelList.get(position -1 ); // Subtract 1 for header
ModelHolder holder = (ModelHolder) h;
// populate your holder with data from your model as usual
}
@Override
public int getItemCount() {
return _categories.size() + 1; // add one for the header
}
}
Vervolgens in de activiteit / fragment:
final HeaderAdapter adapter = new HeaderAdapter (mModelList);
final GridLayoutManager manager = new GridLayoutManager();
manager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
return adapter.isHeader(position) ? manager.getSpanCount() : 1;
}
});
mRecyclerView.setLayoutManager(manager);
mRecyclerView.setAdapter(adapter);
Dezelfde aanpak kan worden gebruikt om een voettekst toe te voegen naast of in plaats van een koptekst.
Bron: blog van Chiu-Ki Chan Square Island
Eenvoudige lijst met LinearLayoutManager
Dit voorbeeld wordt een lijst van plaatsen met afbeelding en de naam met behulp van een ArrayList
van aangepaste Place
objecten als dataset.
Activiteitsindeling
De lay-out van de activiteit / het fragment of waar de RecyclerView wordt gebruikt, hoeft alleen de RecyclerView te bevatten. Er is geen ScrollView of een specifieke lay-out nodig.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/my_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
Definieer het gegevensmodel
U kunt elk klasse- of primitief gegevenstype als model gebruiken, zoals int
, String
, float[]
of CustomObject
. De RecyclerView verwijst naar een List
met deze objecten / primitieven.
Wanneer een lijstitem verwijst naar verschillende gegevenstypen zoals tekst, getallen, afbeeldingen (zoals in dit voorbeeld met plaatsen), is het vaak een goed idee om een aangepast object te gebruiken.
public class Place {
// these fields will be shown in a list item
private Bitmap image;
private String name;
// typical constructor
public Place(Bitmap image, String name) {
this.image = image;
this.name = name;
}
// getters
public Bitmap getImage() {
return image;
}
public String getName() {
return name;
}
}
Lijstitem lay-out
U moet een XML-lay-outbestand opgeven dat voor elk lijstitem wordt gebruikt. In dit voorbeeld wordt een ImageView
gebruikt voor de afbeelding en een TextView
voor de naam. De LinearLayout
plaatst de ImageView
links en de TextView
rechts van de afbeelding.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="8dp">
<ImageView
android:id="@+id/image"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp" />
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
Maak een RecyclerView-adapter en ViewHolder
Vervolgens moet u de RecyclerView.Adapter
en de RecyclerView.ViewHolder
erven. Een gebruikelijke klassenstructuur zou zijn:
public class PlaceListAdapter extends RecyclerView.Adapter<PlaceListAdapter.ViewHolder> {
// ...
public class ViewHolder extends RecyclerView.ViewHolder {
// ...
}
}
Eerst implementeren we de ViewHolder
. Het neemt alleen de standaardconstructor over en slaat de benodigde weergaven op in sommige velden:
public class ViewHolder extends RecyclerView.ViewHolder {
private ImageView imageView;
private TextView nameView;
public ViewHolder(View itemView) {
super(itemView);
imageView = (ImageView) itemView.findViewById(R.id.image);
nameView = (TextView) itemView.findViewById(R.id.name);
}
}
De constructor van de adapter stelt de gebruikte gegevensset in:
public class PlaceListAdapter extends RecyclerView.Adapter<PlaceListAdapter.ViewHolder> {
private List<Place> mPlaces;
public PlaceListAdapter(List<Place> contacts) {
mPlaces = contacts;
}
// ...
}
Om onze aangepaste lijstitemlay-out te gebruiken, overschrijven we de methode onCreateViewHolder(...)
. In dit voorbeeld wordt het lay- place_list_item.xml
.
public class PlaceListAdapter extends RecyclerView.Adapter<PlaceListAdapter.ViewHolder> {
// ...
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(
R.layout.place_list_item,
parent,
false
);
return new ViewHolder(view);
}
// ...
}
In de onBindViewHolder(...)
hebben we de inhoud van de weergaven ingesteld. We krijgen het gebruikte model door het in de List
op de gegeven positie te vinden en vervolgens afbeelding en naam in de ViewHolder
weergaven in te stellen.
public class PlaceListAdapter extends RecyclerView.Adapter<PlaceListAdapter.ViewHolder> {
// ...
@Override
public void onBindViewHolder(PlaceListAdapter.ViewHolder viewHolder, int position) {
Place place = mPlaces.get(position);
viewHolder.nameView.setText(place.getName());
viewHolder.imageView.setImageBitmap(place.getImage());
}
// ...
}
We moeten ook getItemCount()
implementeren, die eenvoudig de grootte van de List
retourneert.
public class PlaceListAdapter extends RecyclerView.Adapter<PlaceListAdapter.ViewHolder> {
// ...
@Override
public int getItemCount() {
return mPlaces.size();
}
// ...
}
(Genereer willekeurige gegevens)
Voor dit voorbeeld genereren we enkele willekeurige plaatsen.
@Override
protected void onCreate(Bundle savedInstanceState) {
// ...
List<Place> places = randomPlaces(5);
// ...
}
private List<Place> randomPlaces(int amount) {
List<Place> places = new ArrayList<>();
for (int i = 0; i < amount; i++) {
places.add(new Place(
BitmapFactory.decodeResource(getResources(), Math.random() > 0.5 ?
R.drawable.ic_account_grey600_36dp :
R.drawable.ic_android_grey600_36dp
),
"Place #" + (int) (Math.random() * 1000)
));
}
return places;
}
Verbind de RecyclerView met de PlaceListAdapter en de dataset
Een RecyclerView
met een adapter verbinden is heel eenvoudig. U moet de LinearLayoutManager
als lay- LinearLayoutManager
om de LinearLayoutManager
out te bereiken.
@Override
protected void onCreate(Bundle savedInstanceState) {
// ...
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
recyclerView.setAdapter(new PlaceListAdapter(places));
recyclerView.setLayoutManager(new LinearLayoutManager(this));
}
Gedaan!
StaggeredGridLayoutManager
- Maak uw RecyclerView in uw lay-out xml-bestand:
<android.support.v7.widget.RecyclerView
android:id="@+id/recycleView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Maak uw modelklasse voor het bewaren van uw gegevens:
public class PintrestItem { String url; public PintrestItem(String url,String name){ this.url=url; this.name=name; } public String getUrl() { return url; } public String getName(){ return name; } String name; }
Maak een lay-outbestand voor RecyclerView-items:
<ImageView android:layout_width="match_parent" android:layout_height="wrap_content" android:adjustViewBounds="true" android:scaleType="centerCrop" android:id="@+id/imageView"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:id="@+id/name" android:layout_gravity="center" android:textColor="@android:color/white"/>
Maak de adapterklasse voor de RecyclerView:
public class PintrestAdapter extends RecyclerView.Adapter<PintrestAdapter.PintrestViewHolder>{ private ArrayList<PintrestItem>images; Picasso picasso; Context context; public PintrestAdapter(ArrayList<PintrestItem>images,Context context){ this.images=images; picasso=Picasso.with(context); this.context=context; } @Override public PintrestViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.pintrest_layout_item,parent,false); return new PintrestViewHolder(view); } @Override public void onBindViewHolder(PintrestViewHolder holder, int position) { picasso.load(images.get(position).getUrl()).into(holder.imageView); holder.tv.setText(images.get(position).getName()); } @Override public int getItemCount() { return images.size(); } public class PintrestViewHolder extends RecyclerView.ViewHolder{ ImageView imageView; TextView tv; public PintrestViewHolder(View itemView) { super(itemView); imageView=(ImageView)itemView.findViewById(R.id.imageView); tv=(TextView)itemView.findViewById(R.id.name); } } }
Instantiëren van de RecyclerView in uw activiteit of fragment:
RecyclerView recyclerView = (RecyclerView)findViewById(R.id.recyclerView); //Create the instance of StaggeredGridLayoutManager with 2 rows i.e the span count and provide the orientation StaggeredGridLayoutManager layoutManager=new new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL); recyclerView.setLayoutManager(layoutManager); // Create Dummy Data and Add to your List<PintrestItem> List<PintrestItem>items=new ArrayList<PintrestItem> items.add(new PintrestItem("url of image you want to show","imagename")); items.add(new PintrestItem("url of image you want to show","imagename")); items.add(new PintrestItem("url of image you want to show","imagename")); recyclerView.setAdapter(new PintrestAdapter(items,getContext() );
Vergeet niet om de Picasso-afhankelijkheid toe te voegen aan uw build.gradle-bestand:
compile 'com.squareup.picasso:picasso:2.5.2'