Android
RecyclerView und LayoutManager
Suche…
GridLayoutManager mit dynamischer Spannenzahl
Beim Erstellen eines Recyclerview mit einem Gridlayout-Layout-Manager müssen Sie die Spannweitenanzahl im Konstruktor angeben. Die Spannweite bezieht sich auf die Anzahl der Spalten. Dies ist ziemlich klobig und berücksichtigt keine größeren Bildschirmgrößen oder Bildschirmausrichtungen. Ein Ansatz besteht darin, mehrere Layouts für die verschiedenen Bildschirmgrößen zu erstellen. Ein weiterer dynamischerer Ansatz ist unten zu sehen.
Zuerst erstellen wir eine benutzerdefinierte RecyclerView-Klasse wie folgt:
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);
}
}
}
Diese Klasse bestimmt, wie viele Spalten in das Recycling-Fenster passen. Um es zu verwenden, müssen Sie es wie folgt in Ihre layout.xml einfügen:
<?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"
/>
Beachten Sie, dass wir das columnWidth-Attribut verwenden. Die Recyclingübersicht benötigt es, um festzustellen, wie viele Spalten in den verfügbaren Platz passen.
In Ihrer Aktivität / Ihrem Fragment erhalten Sie lediglich einen Verweis auf das Recylerview und legen einen Adapter darauf (und alle Elementdekorationen oder Animationen, die Sie hinzufügen möchten) fest. SETZEN SIE KEINEN LAYOUT MANAGER
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.auto_fit_recycler_view);
recyclerView.setAdapter(new MyAdapter());
(wobei MyAdapter Ihre Adapterklasse ist)
Sie haben jetzt eine Übersicht, in der der Spancount (dh die Spalten) an die Bildschirmgröße angepasst wird. Als letzte Ergänzung möchten Sie möglicherweise die Spalten im Recycling-Fenster zentrieren (standardmäßig sind sie an layout_start ausgerichtet). Sie können dies tun, indem Sie die AutofitRecyclerView-Klasse ein wenig ändern. Beginnen Sie mit der Erstellung einer inneren Klasse in der Recyclingübersicht. Dies ist eine Klasse, die sich von GridLayoutManager erstreckt. Es wird links und rechts genug aufgefüllt, um die Zeilen zu zentrieren:
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();
}
}
}
Wenn Sie den LayoutManager in AutofitRecyclerView festlegen, verwenden Sie den CenteredGridLayoutManager wie folgt:
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);
}
Und das ist es! Sie verfügen über eine dynamische, zentrierte Rasterlayoutmanager-basierte Recyclingübersicht.
Quellen:
Headeransicht zum Recycling hinzufügen mit dem Gridlayout-Manager
Um einem Recycling-Fenster mit einem Rasterlayout einen Header hinzuzufügen, muss zunächst dem Adapter mitgeteilt werden, dass die Header-Ansicht die erste Position ist - und nicht die Standardzelle für den Inhalt. Als Nächstes muss dem Layout-Manager mitgeteilt werden, dass die erste Position eine Spannweite haben muss, die der * Spannweite der gesamten Liste entspricht. *
Nehmen Sie eine reguläre RecyclerView.Adapter-Klasse und konfigurieren Sie sie wie folgt:
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
}
}
Dann in der Aktivität / 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);
Der gleiche Ansatz kann verwendet werden, um zusätzlich zu oder anstelle einer Kopfzeile eine Fußzeile hinzuzufügen.
Quelle: Chiu-Ki Chans Platzinsel-Blog
Einfache Liste mit LinearLayoutManager
In diesem Beispiel wird eine Liste von Orten mit Bild und Namen ArrayList
indem eine ArrayList
mit benutzerdefinierten Place
Objekten als Datensatz verwendet wird.
Aktivitätslayout
Das Layout der Aktivität / des Fragments oder des RecyclerView muss nur das RecyclerView enthalten. Es ist kein ScrollView oder ein bestimmtes Layout erforderlich.
<?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>
Definieren Sie das Datenmodell
Sie können jeden Klassen- oder primitiven Datentyp als Modell verwenden, z. B. int
, String
, float[]
oder CustomObject
. Die RecyclerView verweist auf eine List
dieser Objekte / Primitiven.
Wenn ein Listenelement auf verschiedene Datentypen verweist, z. B. Text, Zahlen, Bilder (wie in diesem Beispiel mit Orten), ist es oft ratsam, ein benutzerdefiniertes Objekt zu verwenden.
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;
}
}
Listenelement-Layout
Sie müssen eine XML-Layoutdatei angeben, die für jedes Listenelement verwendet wird. In diesem Beispiel wird eine ImageView
für das Image und eine TextView
für den Namen verwendet. Das LinearLayout
positioniert die ImageView
links und die TextView
rechts neben dem Image.
<?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>
Erstellen Sie einen RecyclerView-Adapter und einen ViewHolder
Als Nächstes müssen Sie den RecyclerView.Adapter
und den RecyclerView.ViewHolder
erben. Eine übliche Klassenstruktur wäre:
public class PlaceListAdapter extends RecyclerView.Adapter<PlaceListAdapter.ViewHolder> {
// ...
public class ViewHolder extends RecyclerView.ViewHolder {
// ...
}
}
Zuerst implementieren wir den ViewHolder
. Es erbt nur den Standardkonstruktor und speichert die erforderlichen Ansichten in einigen Feldern:
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);
}
}
Der Konstruktor des Adapters legt das verwendete Dataset fest:
public class PlaceListAdapter extends RecyclerView.Adapter<PlaceListAdapter.ViewHolder> {
private List<Place> mPlaces;
public PlaceListAdapter(List<Place> contacts) {
mPlaces = contacts;
}
// ...
}
Um unser benutzerdefiniertes Listenelement-Layout zu verwenden, überschreiben wir die Methode onCreateViewHolder(...)
. In diesem Beispiel heißt die Layoutdatei 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);
}
// ...
}
Im onBindViewHolder(...)
legen wir tatsächlich den Inhalt der Ansichten fest. Wir erhalten das verwendete Modell, indem wir es in der List
an der angegebenen Position finden und dann in den Ansichten des ViewHolder
Bild und Namen ViewHolder
.
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());
}
// ...
}
Wir müssen auch getItemCount()
implementieren, das einfach die Größe der List
getItemCount()
.
public class PlaceListAdapter extends RecyclerView.Adapter<PlaceListAdapter.ViewHolder> {
// ...
@Override
public int getItemCount() {
return mPlaces.size();
}
// ...
}
(Zufallsdaten erzeugen)
Für dieses Beispiel generieren wir einige zufällige Orte.
@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;
}
Verbinden Sie die RecyclerView mit dem PlaceListAdapter und der Datenmenge
Das Anschließen eines RecyclerView
mit einem Adapter ist sehr einfach. Sie müssen den LinearLayoutManager
als Layout-Manager einstellen, um das LinearLayoutManager
zu erreichen.
@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));
}
Erledigt!
StaggeredGridLayoutManager
- Erstellen Sie Ihre RecyclerView in Ihrer Layout-XML-Datei:
<android.support.v7.widget.RecyclerView
android:id="@+id/recycleView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Erstellen Sie Ihre Modellklasse zum Speichern Ihrer Daten:
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; }
Erstellen Sie eine Layoutdatei für RecyclerView-Elemente:
<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"/>
Erstellen Sie die Adapterklasse für die 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); } } }
Instanziere die RecyclerView in deiner Aktivität oder deinem 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() );
Vergessen Sie nicht, die Picasso-Abhängigkeit in Ihre build.gradle-Datei aufzunehmen:
compile 'com.squareup.picasso:picasso:2.5.2'