RecyclerView和RecyclerView.Adapter是现在Android使用列表时是用的类;前者是列表整个布局,后者是处理数据和单个行/列的布局进行绑定显示工作。
1. 基础用法
RecyclerView写的时候直接扔在布局里。使用时需要设置列表的排列方式。设置方式很简单,例如:
recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
RecyclerView.Adapter是个抽象类,这个类定义了泛型的ViewHolder,在Adapter中使用。
Adapter<VH extends ViewHolder>
RecyclerView.Adapter有三个抽象方法需要实现(摘取了部分源码注释):
第一个方法
/**
* Called when RecyclerView needs a new {@link ViewHolder} of the given type to represent
* an item.
* <p>
* This new ViewHolder should be constructed with a new View that can represent the items
* of the given type. You can either create a new View manually or inflate it from an XML
* layout file.
*/
@NonNull
public abstract VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType);
注释中可看出,当需要一个新的ViewHolder加载时,会用这个方法来获取。也就是旧的ViewHolder和需要的新的ViewHolder类型不同时调用(此处涉及ViewHolder复用机制。ViewHolder中主要是进行的findViewById从布局中获取控件的方法,复用ViewHolder也是为了减少使用findViewById时的性能消耗)。
第二个方法
/**
* Called by RecyclerView to display the data at the specified position. This method should
* update the contents of the {@link ViewHolder#itemView} to reflect the item at the given
* position.
*/
public abstract void onBindViewHolder(@NonNull VH holder, int position);
用于数据和Item布局的绑定。具体的显示逻辑写在这个方法中。这里的参数position是列表中Item的位置,从0开始计数,类比数组。holder就是获取到的ViewHolder。
第三个方法
/**
* Returns the total number of items in the data set held by the adapter.
*
* @return The total number of items in this adapter.
*/
public abstract int getItemCount();
返回列表中Item个数
另外有个方法
有个方法要注意,这个方法返回列表中Item的类型。可以重写这个方法来自定义Item的Type。这里的Type就是onCreateViewHolder中的参数viewType。
/**
* Return the view type of the item at <code>position</code> for the purposes
* of view recycling.
*/
public int getItemViewType(int position) {
return 0;
}
2.特殊场景
场景一
(Activity启动模式为standard)点击Item后进入下一个页面,从下一个页面回来后,刷新列表并将焦点放在特定的一个Item上。此时注意,刷新列表调用notifyDataSetChanged()方法。列表刷新顺序为:从之前选中的Item向后刷新,类似循环链表,将Item顺序刷新一遍。
场景二
在Android TV上,Item获取焦点使用方法为itemView.requestFocus()。需要注意ViewHolder中调用此方法:itemView.setFocusable(true)。
场景三
选中Item后,Item的布局显示放大效果,此Item会覆盖其他的Item显示。
默认情况Item会显示在RecyclerView的高度范围内,放大后Item布局超出的部分会被裁减掉显示不出来。可以调用方法recyclerView.setClipToPadding(false)【让控件可以绘制在padding里】和recyclerView.setClipChildren(false)【让子布局可以超出父布局显示】,使Item显示完全。这两个属性也可以在XML布局文件的RecyclerView的属性设置中进行设置(android:clipChildren和 android:clipToPadding)。
显示的Item如果要正常显示,需要将此Item设置为RecyclerView的Item中最后绘制的一项。调用recyclerView.setChildDrawingOrderCallback使用自定义的顺序绘制方式。这个方法的参数为ChildDrawingOrderCallback。
/**
* A callback interface that can be used to alter the drawing order of RecyclerView children.
* <p>
* It works using the {@link ViewGroup#getChildDrawingOrder(int, int)} method, so any case
* that applies to that method also applies to this callback. For example, changing the drawing
* order of two views will not have any effect if their elevation values are different since
* elevation overrides the result of this callback.
*/
public interface ChildDrawingOrderCallback {
/**
* Returns the index of the child to draw for this iteration. Override this
* if you want to change the drawing order of children. By default, it
* returns i.
*
* @param i The current iteration.
* @return The index of the child to draw this iteration.
*
* @see RecyclerView#setChildDrawingOrderCallback(RecyclerView.ChildDrawingOrderCallback)
*/
int onGetChildDrawingOrder(int childCount, int i);
}
注释中可以看出,android的elevation属性会影响绘制顺序。
场景四
对在Adapter中对RecyclerView的Item进行隐藏处理。直接设置itemView.setVisible(View.GONE)隐藏后,itemView所在区域会出现一块儿空白。正确做法参考这篇文章:https://blog.csdn.net/u011060103/article/details/52780844
方案代码(摘自上面博客):
public void setVisibility(boolean isVisible){
RecyclerView.LayoutParams param = (RecyclerView.LayoutParams)itemView.getLayoutParams();
if (isVisible){
param.height = LinearLayout.LayoutParams.WRAP_CONTENT;
param.width = LinearLayout.LayoutParams.MATCH_PARENT;
itemView.setVisibility(View.VISIBLE);
}else{
itemView.setVisibility(View.GONE);
param.height = 0;
param.width = 0;
}
itemView.setLayoutParams(param);
}