RecyclerView 是Android 版本中新添加的一个用来取代ListView、GridView的SDK,高度的解耦,异常的灵活,通过设置它提供的不同LayoutManager,ItemDecoration , ItemAnimator实现不同的效果。
1.RecyclerView特性:
1)Item的显示位置,如何显示,通过LayoutManager设置
2)Item间的分割通过ItemDecoration设置
3)Item增加与删除的动画通过ItemAnimator设置(系统提供一种默认动画效果DefaultItemAnimator,可以自定义动画效果)
4)仅仅关注如何回收与复用view
2.RecyclerView相关的重要类
Adapter、ViewHolder、LayoutManager、ItemDecoration、ItemAnimator
Adapter:包装数据集合并且为每个条目创建视图。
ViewHolder:保存用于显示每个数据条目的子View。
LayoutManager:将每个条目的视图放置于适当的位置。
ItemDecoration:在每个条目的视图的周围或上面绘制一些装饰视图。
ItemAnimator:在条目被添加、移除或者重排序时添加动画效果。
3.RecyclerView的功能
1)竖向listview的风格 (依赖LayoutManager)
2)横向listview的风格 (依赖LayoutManager)
3)竖向gridview的风格 (依赖LayoutManager)
4)横向gridview的风格 (依赖LayoutManager)
5)瀑布流 (依赖LayoutManager)
6)定制Item增加与删除动画 (依赖ItemAnimator)
添加的依赖: compile ‘com.android.support:recyclerview-v7:21.0.+’
item.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_margin="3dp" android:background="@drawable/item_selector" android:layout_height="72dp">
<TextView android:id="@+id/item_text" android:layout_width="72dp" android:layout_height="match_parent" android:gravity="center" />
</RelativeLayout>
activity_main.xml布局:
<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:background="#22ff0000"
tools:context=".MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/my_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"/>
</LinearLayout>
瀑布流效果的实现和其他效果的实现只是Adapter有一点不一样,先来看一下其它几种效果的实现:
RecyclerView.ViewHolder的基本作用是缓存视图对象;
RecyclerView.Adapter还没有默认实现,以后可能会添加。由于RecyclerView.Adapter是一个抽象类,所以必须要实现以下三个方法:
- public VH onCreateViewHolder(ViewGroup parent, int viewType)
- public void onBindViewHolder(VH holder, int position)
- public int getItemCount()
其中VH是泛型类,当继承RecyclerView.Adapter时需要使用具体的类来替换。RecyclerViewAdapter 的示例代码如下:
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder>{
protected List<String> mListData;
private Context mContext;
public RecyclerViewAdapter(Context context,List<String> datas){
this.mListData = datas;
this.mContext = context;
mLayoutInflater = LayoutInflater.from(mContext);
}
//创建ViewHolder
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = mLayoutInflater.inflate(R.layout.item,parent,false);
ViewHolder viewHolder = new ViewHolder(v);
return viewHolder;
}
//绑定ViewHolder
@Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
holder.mTextView.setText(mListData.get(position));
}
@Override
public int getItemCount() {
return mListData.size();
}
class ViewHolder extends RecyclerView.ViewHolder{
TextView mTextView;
public ViewHolder(View itemView) {
super(itemView);
mTextView = (TextView) itemView.findViewById(R.id.item_text);
}
}
}
RecyclerView的Adapter和之前使用的listview的Adapter还是有相同之处的,所以使用起来并不陌生;
MainActivity.java
mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
//填充数据
mRecyclerViewAdapter = new RecyclerViewAdapter(mContext,mListData);
//设置adapter
mRecyclerView.setAdapter(mRecyclerViewAdapter);
//设置listview垂直如何显示
mLinearLayoutManager = new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false);
//创建默认的线性布局Linearlayout
mRecyclerView.setLayoutManager(mLinearLayoutManager);
//如果可以确定每个item的高度是固定的,设置这个选项可以提高性能
mRecyclerView.setHasFixedSize(true);
//添加动画
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
//绘制分割线
mRecyclerView.addItemDecoration(new DividerItemDecoration(mContext, DividerItemDecoration.VERTICAL_LIST));
DefaultItemAnimator.java
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
private static final int[] ATTRS = new int[]{
android.R.attr.listDivider
};
public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
private Drawable mDivider;
private int mOrientation;
public DividerItemDecoration(Context context, int orientation) {
//从 获取分割线样式
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
a.recycle();
setOrientation(orientation);
}
public void setOrientation(int orientation) {
if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
throw new IllegalArgumentException("invalid orientation");
}
mOrientation = orientation;
}
@Override
public void onDraw(Canvas c, RecyclerView parent) {
if (mOrientation == VERTICAL_LIST) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
}
/** * * 绘制垂直分割线 * * */
public void drawVertical(Canvas c, RecyclerView parent) {
final int left = parent.getPaddingLeft();
final int right = parent.getWidth() - parent.getPaddingRight();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int top = child.getBottom() + params.bottomMargin;
final int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
/** * * 绘制水平分割线 * * */
public void drawHorizontal(Canvas c, RecyclerView parent) {
final int top = parent.getPaddingTop();
final int bottom = parent.getHeight() - parent.getPaddingBottom();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int left = child.getRight() + params.rightMargin;
final int right = left + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
/** * * 提供绘制分割线的间距 * * */
@Override
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
if (mOrientation == VERTICAL_LIST) {
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
} else {
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
}
}
}
divider.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<size android:height="2dp"/>
<gradient android:startColor="#ff00ff00" android:centerColor="#ff0000ff" android:endColor="#ffff0000"/>
</shape>
编写分割线样式
最后操作style.xml:
分割线就添加完成了,效果如下:
效果还挺不错的吧。一般我们可能不会用到,因为在item中直接添加View比这种方式简单多了,嘻嘻。
完成这些之后,实现其他几种效果就是设置不同的LayoutManager样式了,代码如下:
//ListView
case R.id.action_listview:
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
break;
//GridView
case R.id.action_gridview:
mRecyclerView.setLayoutManager(new GridLayoutManager(this,3));
break;
//水平GridVIew
case R.id.action_hor_gridview:
mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(5,StaggeredGridLayoutManager.HORIZONTAL));
break;
现在我们就完成了垂直、水平的ListView和垂直、水平的GridView的效果了,感觉还不错;
由于RecyclerView没有提供点击事件,所以学要我们自己来实现,代码如下:
//定义接口
public interface OnItemClickListener{
void onItemClick(View v,int position);
void onItemLongClick(View v,int position);
}
public void setOnItemClickListener(OnItemClickListener listener){
this.mOnItemClickListener = listener ;
}
在绑定ViewHolder的时候去触发:
//绑定ViewHolder
@Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
holder.mTextView.setText(mListData.get(position));
setOnListtener(holder);
}
//触发
protected void setOnListtener(final RecyclerView.ViewHolder holder){
if(mOnItemClickListener != null){
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int layoutPosition = holder.getPosition();
mOnItemClickListener.onItemClick(holder.itemView,layoutPosition);
}
});
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
int layoutPosition = holder.getPosition();
mOnItemClickListener.onItemLongClick(holder.itemView,layoutPosition);
return false;
}
});
}
}
点击事件:
//点击事件
mRecyclerViewAdapter.setOnItemClickListener(new RecyclerViewAdapter.OnItemClickListener() {
@Override
public void onItemClick(View v, int position) {
Toast.makeText(mContext, "onclick " + position, Toast.LENGTH_SHORT).show();
}
@Override
public void onItemLongClick(View v, int position) {
Toast.makeText(mContext, "onlongclick " + position, Toast.LENGTH_SHORT).show();
}
});
点击效果就完成了,添加和删除Item效果如下:
//添加布局
public void add(int pos){
mListData.add(pos,"add"+pos);
notifyItemInserted(pos);
}
//删除布局
public void delete(int pos){
mListData.remove(pos);
notifyItemRemoved(pos);
}
注意:
1.添加item的时候使用notifyItemInserted(position);
移除item的时候使用notifyItemRemoved(position); 而不是notifidatasetchanged();
瀑布流的原理就是给每个item重新设置宽和高:
StaggeredRecyclerViewAdapter.java
//绑定ViewHolder
@Override
public void onBindViewHolder(StaggeredRecyclerViewAdapter.ViewHolder holder, int position) {
LayoutParams lp = holder.itemView.getLayoutParams();
lp.height = mHeights.get(position);
holder.itemView.setLayoutParams(lp);
holder.mTextView.setText(mListData.get(position));
setOnListtener(holder);
}
再设置显示方式:
mStaggeredGridLayoutManager = new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);
瀑布流的效果就实现了!
RecyclerView最基本的使用就讲完了,大家肯定了解BGARefreshLayout这个刷新控件,下一篇博客我会讲解RecyclerView+BGARefreshLayout实现下拉刷新、自定义上拉加载和侧滑删除的效果,先给大家看一下效果图:
GitHub下载地址:
https://github.com/fengmaolian/RecyclerView