
RecyclerView

RecyclerView

RecyclerView

RecyclerView
- RecyclerView是ListView的升级版,它具备了更好的性能,且更容易使用。和ListView一样,RecyclerView是用来显示大量数据的容器,并通过复用有限数量的View,来提高滚动时的性能。当你的视图上的元素经常动态的且有规律的改变时候,可以使用RecyclerView控件。
- 与ListView不同的是RecyclerView现在不再负责布局,只专注于复用机制,布局交由LayoutManager来管理。 RecyclerView仍然通过Adapter来获取需要显示的对象。

RecyclerView
- 要使用RecyclerView组件,创建Adapter不再继承自BaseAdapter,而是应该继承自RecyclerView.Adapter类,并且最好指定一个继承自RecyclerView.ViewHolder的范型,Adapter不再要求你返回一个View,而是一个ViewHolder。
- 继承自Adapter后,需要实现3个抽象方法:
<pre>
// 当RecyclerView需要一个ViewHolder时会回调该方法,如果有可复用的View则该方法不会得倒回调
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i);
// 当一个View需要出现在屏幕上时,该方法会被回调,你需要在该方法中根据数据来更改视图
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i);
// 用于告诉RecyclerView有多个视图需要显示public int getItemCount();
</pre>
- 新的Adapter和原有的Adapter并没有太多的差别,只是不再需要我们写复用判断的逻辑,因为复用逻辑其实都是相似的,它已经有了自身的实现。和原有的Adapter一样,仍然可以通过notifyDataSetChanged来刷新UI,通过getItemViewType来获取对应位置的类型,但是它不再需要你指定有多少类型了,因为该方法已经能够判断出有多少类型。新增的onViewRecycled方法可以让使用者监听View被移除屏幕的时机,并且还提供了一个AdapterDataObserver的观察者,对外提供数据改变时的回调。
- ViewHolder是对所有的单个item的封装,不仅包含了item需要显示的View,并且还包含和item相关的其它数据,例如:当前的position、之前的position、即将显示的position、被回收的次数、View的类型、是否处于显示中等信息。创建一个ViewHolder需要传递一个View对象,这个View就是该holder的显示视图,该View中通常会包含一些子视图,我们最好把这些子视图都记录在holder中,便于复用时设置不同的数据。
- RecyclerView不再对布局进行管理,而是通过LayoutManager管理布局,我们可以通过继承自LayoutManager来实现特殊的布局,系统提供了三种常用的布局管理器:
LinearLayoutManager 线性布局
GridLayoutManager 九宫格布局
StaggeredGridLayoutManager 瀑布流布局
- 并且每一种都可以设置横行和纵向的布局,可惜的均不能添加header,如果要添加header,我们可以在Adapter中使用不同的类型来达到该效果。
- RecyclerView默认提供了item的增加和删除的动画效果,如果我们使用自定义的动画,需要继承继承RecyclerView.ItemAnimator类,时候时,通过RecyclerView.setItemAnimator()方法来设置我们自定义的动画。
Demo代码:
public class MainActivity extends Activity{
private FrameLayout mDeleteBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// mDeleteBar
mDeleteBar = (FrameLayout) findViewById(R.id.deleteBar);
// 创建右下角圆形按钮
// 创建RecyclerView控件
// 创建上方的Delete面板
ViewOutlineProvider viewOutlineProvider = new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
// 获取按钮的尺寸
int fabSize = getResources().getDimensionPixelSize(
R.dimen.fab_size);
// 设置轮廓的尺寸
outline.setOval(-4, -4, fabSize + 2, fabSize + 2);
}
};
// 获得右下角圆形按钮对象
View fabView = findViewById(R.id.fab_add);
fabView.setOutlineProvider(viewOutlineProvider);
// 获取RecyclerView对象
final RecyclerView recyclerView = (RecyclerView)findViewById(R.id.recycler_view);
// 创建线性布局管理器(默认是垂直方向)
final LinearLayoutManager layoutManager = new LinearLayoutManager(this);
// 为RecyclerView指定布局管理对象
recyclerView.setLayoutManager(layoutManager);
// 创建列表项分隔线对象
final RecyclerView.ItemDecoration itemDecoration = new SampleDivider(this);
// 为RecyclerView控件指定分隔线对象
recyclerView.addItemDecoration(itemDecoration);
String s = "a";
final SampleRecyclerAdapter sampleRecyclerAdapter = new SampleRecyclerAdapter();
recyclerView.setAdapter(sampleRecyclerAdapter);
// 处理添加按钮的单击事件
fabView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v){
// 获取第一个可视的Item的位置
int positionToAdd = layoutManager.findFirstCompletelyVisibleItemPosition();
// 在该位置插入新的Item
sampleRecyclerAdapter.addItem(positionToAdd);
}
});
// 处理删除事件
mDeleteBar.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v){
int positionToRemove = layoutManager.findFirstCompletelyVisibleItemPosition();
// 删除第一个可视的ite
sampleRecyclerAdapter.removeData(positionToRemove);
}
});
// 为RecyclerView控件设置滚动事件
recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener(){
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
// dx:大于0,向右滚动 小于0,向左滚动
// dy:大于0,向上滚动 小于0,向下滚动
if(dy > 0) {
// 列表向上滚动,隐藏删除面板
if(mDeleteBar.getVisibility() == View.VISIBLE) {
hideDeleteBar();
}
}
else {
// 列表向下滚动,显示删除面板
if(mDeleteBar.getVisibility() == View.GONE) {
showDeleteBar();
}
}
}
});
}
private void showDeleteBar() {
mDeleteBar.startAnimation(AnimationUtils.loadAnimation(this,
R.anim.translate_up_on));
mDeleteBar.setVisibility(View.VISIBLE);
}
private void hideDeleteBar(){
mDeleteBar.startAnimation(AnimationUtils.loadAnimation(this,
R.anim.translate_up_off));
mDeleteBar.setVisibility(View.GONE);
}
}
public class DemoApp {
// 获取要显示的数据(初始化数据)
public static ArrayList<SampleModel> getSampleData(int size) {
ArrayList<SampleModel> sampleData = new ArrayList<SampleModel>(size);
for(int i = 0; i < size; i++) {
// 每一项数据后面都有相应的序号
sampleData.add(new SampleModel("新的列表项<" + i + ">"));
}
return sampleData;
}
}
public class SampleDivider extends RecyclerView.ItemDecoratio{
// 默认分隔条Drawable资源的ID
private static final int[] ATTRS ={ android.R.attr.listDivider };
// 分隔条Drawable对象
private Drawable mDivider;
public SampleDivider(Context context) {
TypedArray a = context.obtainStyledAttributes(ATTRS);
// 获取系统提供的分隔条Drawable对象
mDivider = a.getDrawable(0);
// 回收TypedArray所占用的空间
a.recycle();
}
// 在该方法中绘制了所有列表项之间的分隔条
@Override
public void onDrawOver(Canvas c, RecyclerView parent) {
// 获取列表项距离左边缘的距离
int left = parent.getPaddingLeft();
// 获取列表项距离右边缘的距离
int right = parent.getWidth() - parent.getPaddingRight();
// 获取列表项的总数
int childCount = parent.getChildCount();
// 开始绘制所有列表项之间的分隔线
for (int i = 0; i < childCount; i++){
// 获得当前的列表项
View child = parent.getChildAt(i);
// 获取当前列表项的布局参数信息
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
// 计算分隔条左上角的纵坐标
int top = child.getBottom() + params.bottomMargin;
// 计算分隔条右下角的纵坐标
int bottom = top + mDivider.getIntrinsicHeight();
// 设置分隔条绘制的位置
mDivider.setBounds(left, top, right, bottom);
// 开始绘制当前列表项下方的分隔条
mDivider.draw(c);
}
}
}
public class SampleModel {
private String sampleText;
public SampleModel(String sampleText) {
this.sampleText = sampleText;
}
public void setSampleText(String sampleText) {
this.sampleText = sampleText;
}
public String getSampleText() {
return sampleText;
}
}
public class SampleRecyclerAdapter extends
RecyclerView.Adapter<SampleRecyclerAdapter.ViewHolder>{
private final ArrayList<SampleModel> sampleData = DemoApp.getSampleData(20);
// 用于创建控件
@Override
public ViewHolder onCreateViewHolder(ViewGroup parentViewGroup, int i){
// 获得列表项控件(LinearLayer对象)
// list_basic_item.xml布局文件中只包含一个<LinearLayer>标签,在该标签中包含
// 了一个<TextView>标签
// item是LinearLayout对象
View item = LayoutInflater.from(parentViewGroup.getContext()).inflate(
R.layout.list_basic_item, parentViewGroup, false);
return new ViewHolder(item);
}
// 为控件设置数据
@Override
public void onBindViewHolder(ViewHolder viewHolder, final int position) {
// 获取当前item中显示的数据
final SampleModel rowData = sampleData.get(position);
// 设置要显示的数据
viewHolder.textViewSample.setText(rowData.getSampleText());
viewHolder.itemView.setTag(rowData);
}
@Override
public int getItemCount() {
return sampleData.size();
}
// 删除指定的Item
public void removeData(int position) {
sampleData.remove(position);
// 通知RecyclerView控件某个Item已经被删除
notifyItemRemoved(position);
}
// 在指定位置添加一个新的Item
public void addItem(int positionToAdd){
sampleData.add(positionToAdd,new SampleModel("新的列表项" + new Random().nextInt(10000)));
// 通知RecyclerView控件插入了某个Item
notifyItemInserted(positionToAdd);
}
public static class ViewHolder extends RecyclerView.ViewHolder{
private final TextView textViewSample;
public ViewHolder(View itemView) {
super(itemView);
textViewSample = (TextView) itemView
.findViewById(R.id.textViewSample);
}
}
}
实现ListView和GridView
public class MainActivity extends AppCompatActivity {
private RecyclerView mRecyclerView;
private List<String> datas; //数据集合
private SimpleAdapter mAdapter; //RecyclerView的适配器
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.home_activity);
mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview);
//初始化显示的数据
datas = new ArrayList<String>();
for(int i = (int)'A';i < (int)'Z';i++){
char c = (char) i;
datas.add(c + "");
}
//设置适配器
mAdapter = new SimpleAdapter(this,datas);
mRecyclerView.setAdapter(mAdapter);
/* //设置item之间的分割线
mRecyclerView.addItemDecoration(new DividerItemDecoration(this,
DividerItemDecoration.VERTICAL_LIST));*/
/*//设置为ListView形式显示(方向为垂直)
RecyclerView.LayoutManager manager = new LinearLayoutManager(this,
LinearLayoutManager.VERTICAL,false);*/
/*//设置为GridView的样式并制定列数为4列
RecyclerView.LayoutManager manager = new GridLayoutManager(this,4);
*/
//设置为横向滑动的Gridview的样式
RecyclerView.LayoutManager manager = new StaggeredGridLayoutManager(
5,StaggeredGridLayoutManager.HORIZONTAL);
mRecyclerView.setLayoutManager(manager);
}
class SimpleAdapter extends RecyclerView.Adapter<MyViewHolder>{
Context context;
List<String> datas;//数据集合
LayoutInflater inflater;//布局解析器
public SimpleAdapter(Context context,List<String> datas){
this.context = context;
this.datas = datas;
inflater = LayoutInflater.from(context);
}
//创建ViewHolder
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.list_item,parent,false);
MyViewHolder holder = new MyViewHolder(view);
return holder;
}
//绑定ViewHolder
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.tv.setText(datas.get(position));
}
@Override
public int getItemCount() {
return datas.size();
}
}
class MyViewHolder extends RecyclerView.ViewHolder{
public TextView tv;
public View itemView;
public MyViewHolder(View itemView) {
super(itemView);
this.itemView = itemView;
tv = (TextView) itemView.findViewById(R.id.tv_item);
}
}
}
只需要设置不同的LayoutManager就可以实现ListView和GridVIew以及方向的设置
实现瀑布流
public class StaggerActivity extends AppCompatActivity {
private RecyclerView mRecyclerView;
private List<String> datas; //数据集合
private SimpleAdapter mAdapter; //RecyclerView的适配器
private static List<Integer> mHeight;//存放Item的随机高度
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.home_activity);
mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview);
//初始化显示的数据
datas = new ArrayList<String>();
mHeight = new ArrayList<Integer>();
for (int i = (int) 'A'; i < (int) 'Z'; i++) {
char c = (char) i;
datas.add(c + "");
//随机设置每个item的高度
mHeight.add((int) (100 + Math.random() * 80));
}
//设置适配器
mAdapter = new SimpleAdapter(this, datas);
mRecyclerView.setAdapter(mAdapter);
//设置竖直的GridView的样式,然后在Adapter中随机的修改Item的高度就可以实现瀑布流的方式了
RecyclerView.LayoutManager manager = new StaggeredGridLayoutManager(
4, StaggeredGridLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(manager);
//设置默认的动画效果(删除和添加Item时呈现)
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
mAdapter.setOnItemClickListener(new ItemClickListener() {
@Override
public void onItemClick(View view, int position) {
Toast.makeText(StaggerActivity.this,"" + position,Toast.LENGTH_SHORT).show();
}
});
}
public void onClick(View view){
int id = view.getId();
if(id == R.id.add){ //增加Item
mAdapter.addItem(1);
}else if(id == R.id.del){ //删除Item
mAdapter.deleteItem(1);
}
}
static class SimpleAdapter extends RecyclerView.Adapter<MyViewHolder> {
Context context;
List<String> datas;
LayoutInflater inflater;
public SimpleAdapter(Context context, List<String> datas) {
this.context = context;
this.datas = datas;
inflater = LayoutInflater.from(context);
}
//创建ViewHolder
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.list_item, parent, false);
MyViewHolder holder = new MyViewHolder(view);
return holder;
}
//绑定ViewHolder
@Override
public void onBindViewHolder(final MyViewHolder holder, final int position) {
holder.tv.setText(datas.get(position));
ViewGroup.LayoutParams params = holder.itemView.getLayoutParams();
params.height = mHeight.get(position);
holder.itemView.setLayoutParams(params);
//RecycleVie没有提供Item的点击事件,需要自己设置,有多中方法
//此处采用的是在Adapter中设置
if(listener != null){
/**
* 刷新数据用的是notifyItemChanged:不会刷新有的,只会刷新指定的项
* 所以直接用函数中的position在有新添加或者删除的item时,位置就不准确了
* 所以调用holder自带的getLayoutPosition来获取在布局中的位置
*/
final int layoutPosition = holder.getLayoutPosition();
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
listener.onItemClick(holder.itemView,layoutPosition);
}
});
}
}
@Override
public int getItemCount() {
return datas.size();
}
public void addItem(int pos){
datas.add(pos,"新添加的项");
//调用这个通知才有动画效果
notifyItemChanged(pos);
}
public void deleteItem(int pos){
datas.remove(pos);
notifyItemChanged(pos);
}
private ItemClickListener listener;
public void setOnItemClickListener(ItemClickListener listener){
this.listener = listener;
}
}
自定义Item点击监听接口
interface ItemClickListener{
public void onItemClick(View view,int position);
}
static class MyViewHolder extends RecyclerView.ViewHolder {
public TextView tv;
public View itemView;
public MyViewHolder(View itemView) {
super(itemView);
this.itemView = itemView;
tv = (TextView) itemView.findViewById(R.id.tv_item);
}
}
}
所谓瀑布流的实现:就是在GridView的基础上动态随机的设置每个Item的高度
注意点
RecyclerView没有实现Item点击监听,需要自己去实现
当有增加和删除Item时,Adapter函数中的position与真实的位置就会冲突,所以通过 holder自带的API:getLayoutPosition()来获取相应的位置
设置Item之间的分割线:也可以这样实现
设置item布局的背景颜色,然后再设置个margin也能区分
资源链接(GitHub)
Item分割线 :DividerItemDecoration
https://gist.github.com/alexfu/0f464fc3742f134ccd1e
DividerItemDecoration:里面用到了系统的属性:通过在Style中增加Item可以实现自己设置的效果
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<!--定义RecylerView的Item之间的分割线-->
<item name="android:listDivider">@drawable/divider</item>
</style>
//divider:自定义的shape
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size android:height="5dp" />
<gradient
android:centerColor="@android:color/holo_red_light"
android:endColor="@android:color/holo_green_light"
android:startColor="@android:color/holo_blue_light" />
</shape>
Item删除和添加的动画
https://github.com/gabrielemariotti/RecyclerViewItemAnimators