android 5.0发布已经过去好久了,之前一直都没有怎么注意它。最近闲来无事,所以来学习学习。android 5.0新加了两个新的控件,RecycleView和CardView,今天,我们就一起来看看这两个控件的使用吧。
RecycleView:用于在有限的窗口集显示大量的数据
可能有的同学就要问了,我们不是已经有了ListView,GridView了吗?为什么还要新增RecycleView?难道它比ListView,GridView还要好用?哈哈哈,那是肯定的呀,要不然干嘛弄一个这个出来,既然RecycleView比ListView,GridView更厉害,那么它厉害在哪里呢?
下面我们就一起来看看吧!
整体看RecycleView的架构,提供了一种插拔式的体验,高度的解耦,异常的灵活,通过设置它提供的不同LayoutManager,ItemDecoration,ItemAnimator实现炫酷的效果。
- 你想要控制其显示的方式,请通过布局管理器LayoutManager
- 你想要控制Item间的间隔(可绘制),请通过ItemDecoration
- 你想要控制Item增删的动画,请通过ItemAnimator
- 你想要控制点击、长按事件,请自己写(擦,这点尼玛。)
下面我们就来一个一个的讲解:
1.布局管理器LayoutManager
recycleView在LayoutManager上的最大突破就是通过一行代码来实现布局的任意切换,注意这里指的是LinearLayoutManager(类似于ListView的布局)、GridLayoutManager(类似于GridView的布局)和StaggeredGridLayoutManager这三个布局的切换。有没有感觉很牛逼。当然,写法也很简单,如下:
// 设置布局
// LinearLayoutManager意味着这添加的是ListView这种
// GridLayoutManager意味着这添加的是GridView这种
// StaggeredGridLayoutManager意味着这添加的是瀑布流这种
// recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setLayoutManager(new StaggeredGridLayoutManager(3,
StaggeredGridLayoutManager.VERTICAL));
注意:在使用StaggeredGridLayoutManager的时候,每个Item的高度不能写成固定的,否则就体现不出瀑布流的效果,那么在使用StaggeredGridLayoutManager的时候我们需要将每个Item的高度随机生成。
2.自定义Item的分割线
不同于原来的ListView,RecycleView的分割线需要开发者自己添加,但是系统为我们提供了方法
addItemDecoration(new DividerGridItemDecoration(this));我们只需要在DividerGridItemDecoration去定制我们自己需要的分割线就好
3.Item增删的动画
在动画方面,系统为我们提供了一个默认的实现,方法也很简单
recyclerView.setItemAnimator(new DefaultItemAnimator());注意,这里更新数据集不是用
adapter.notifyDataSetChanged()
而是
notifyItemInserted(position)
与
notifyItemRemoved(position)
否则没有动画效果。在适配器里面添加这两个方法就好
public void addData(int position) {
mDatas.add(position, "Insert One");
notifyItemInserted(position);
}
public void removeData(int position) {
mDatas.remove(position);
notifyItemRemoved(position);
}
4.点击事件(包括长按事件)
RecycleView不像ListView系统提供了点击事件,它需要自己通过接口来实现Item的点击事件。实现方式如下:
1)在适配器里面创建一个接口(包括点击事件和长按事件,注意参数的设置)
2)创建接口并实例化
3)在Item的点击事件上调用接口对应的方法
4)在主Activity的位置实现该接口
在这里我把Adapter贴出来,因为它比较关键
public class GridAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements View.OnClickListener, View.OnLongClickListener {
private Context mContext;
private List<Meizi> datas;//数据
//自定义监听事件
public static interface OnRecyclerViewItemClickListener {
void onItemClick(View view);
void onItemLongClick(View view);
}
private OnRecyclerViewItemClickListener mOnItemClickListener = null;
public void setOnItemClickListener(OnRecyclerViewItemClickListener listener) {
mOnItemClickListener = listener;
}
//适配器初始化
public GridAdapter(Context context,List<Meizi> datas) {
mContext=context;
this.datas=datas;
}
@Override
public int getItemViewType(int position) {
//判断item类别,是图还是显示页数(图片有URL)
if (!TextUtils.isEmpty(datas.get(position).getUrl())) {
return 0;
} else {
return 1;
}
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
//根据item类别加载不同ViewHolder
if(viewType==0){
View view = LayoutInflater.from(mContext)
.inflate(R.layout.grid_meizi_item, parent,false);//这个布局就是一个imageview用来显示图片
MyViewHolder holder = new MyViewHolder(view);
//给布局设置点击和长点击监听
view.setOnClickListener(this);
view.setOnLongClickListener(this);
return holder;
}else{
MyViewHolder2 holder2=new MyViewHolder2(LayoutInflater.from(mContext)
.inflate(R.layout.page_item, parent,false));//这个布局就是一个textview用来显示页数
return holder2;
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
//将数据与item视图进行绑定,如果是MyViewHolder就加载网络图片,如果是MyViewHolder2就显示页数
if(holder instanceof MyViewHolder){
Picasso.with(mContext).load(datas.get(position).getUrl()).into(((MyViewHolder) holder).iv);//加载网络图片
}else if(holder instanceof MyViewHolder2){
((MyViewHolder2) holder).tv.setText(datas.get(position).getPage()+"页");
}
}
@Override
public int getItemCount(){
return datas.size();//获取数据的个数
}
//点击事件回调
@Override
public void onClick(View v) {
if (mOnItemClickListener != null) {
mOnItemClickListener.onItemClick(v);
}
}
@Override
public boolean onLongClick(View v) {
if (mOnItemClickListener!= null) {
mOnItemClickListener.onItemLongClick(v);
}
return false;
}
//自定义ViewHolder,用于加载图片
class MyViewHolder extends RecyclerView.ViewHolder
{
private ImageButton iv;
public MyViewHolder(View view)
{
super(view);
iv = (ImageButton) view.findViewById(R.id.iv);
}
}
//自定义ViewHolder,用于显示页数
class MyViewHolder2 extends RecyclerView.ViewHolder{
private TextView tv;
public MyViewHolder2(View view){
super(view);
tv = (TextView) view.findViewById(R.id.tv);
}
}
//添加一个item
public void addItem(Meizi meizi, int position) {
meizis.add(position, meizi);
notifyItemInserted(position);
recyclerview.scrollToPosition(position);//recyclerview滚动到新加item处
}
//删除一个item
public void removeItem(final int position) {
meizis.remove(position);
notifyItemRemoved(position);
}
}
5.添加头部和尾部
很多时候原生的RecycleView无法满足我们的需求,我们大部分时候需要对它进行扩展,比如添加head和foot,但是当你信心满满的以为和ListView一样添加addHeaderView(View view)和addFooterView(View view)的时候,你就会大吃一惊:什么鬼?为什么没有这两个方法。对的,在recycleView里面系统并没有提供这两个方法,那是不是意味着没有办法来添加头部和尾部呢?当然不是了,只不过添加的方式和ListView不一样而已。RecyclerView实现添加HeaderView和FooterView的核心就是在Adapter里面的onCreateViewHolder根据viewType来判断是列表项还是HeaderView来分别加载不同的布局文件,当然viewType的判断规则也是由我们定义的
public class HeaderBottomAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
//item类型
public static final int ITEM_TYPE_HEADER = 0;
public static final int ITEM_TYPE_CONTENT = 1;
public static final int ITEM_TYPE_BOTTOM = 2;
//模拟数据
public String[] texts = {"java", "python", "C++", "Php", ".NET", "js", "Ruby", "Swift", "OC"};
private LayoutInflater mLayoutInflater;
private Context mContext;
private int mHeaderCount = 1;//头部View个数
private int mBottomCount = 1;//底部View个数
public HeaderBottomAdapter(Context context) {
mContext = context;
mLayoutInflater = LayoutInflater.from(context);
}
//内容长度
public int getContentItemCount() {
return texts.length;
}
//判断当前item是否是HeadView
public boolean isHeaderView(int position) {
return mHeaderCount != 0 && position < mHeaderCount;
}
//判断当前item是否是FooterView
public boolean isBottomView(int position) {
return mBottomCount != 0 && position >= (mHeaderCount + getContentItemCount());
}
//判断当前item类型
@Override
public int getItemViewType(int position) {
int dataItemCount = getContentItemCount();
if (mHeaderCount != 0 && position < mHeaderCount) {
//头部View
return ITEM_TYPE_HEADER;
} else if (mBottomCount != 0 && position >= (mHeaderCount + dataItemCount)) {
//底部View
return ITEM_TYPE_BOTTOM;
} else {
//内容View
return ITEM_TYPE_CONTENT;
}
}
//内容 ViewHolder
public static class ContentViewHolder extends RecyclerView.ViewHolder {
private TextView textView;
public ContentViewHolder(View itemView) {
super(itemView);
textView = (TextView) itemView.findViewById(R.id.tv_item_text);
}
}
//头部 ViewHolder
public static class HeaderViewHolder extends RecyclerView.ViewHolder {
public HeaderViewHolder(View itemView) {
super(itemView);
}
}
//底部 ViewHolder
public static class BottomViewHolder extends RecyclerView.ViewHolder {
public BottomViewHolder(View itemView) {
super(itemView);
}
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == ITEM_TYPE_HEADER) {
return new HeaderViewHolder(mLayoutInflater.inflate(R.layout.rv_header, parent, false));
} else if (viewType == mHeaderCount) {
return new ContentViewHolder(mLayoutInflater.inflate(R.layout.rv_item, parent, false));
} else if (viewType == ITEM_TYPE_BOTTOM) {
return new BottomViewHolder(mLayoutInflater.inflate(R.layout.rv_footer, parent, false));
}
return null;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof HeaderViewHolder) {
} else if (holder instanceof ContentViewHolder) {
((ContentViewHolder) holder).textView.setText(texts[position - mHeaderCount]);
} else if (holder instanceof BottomViewHolder) {
}
}
@Override
public int getItemCount() {
return mHeaderCount + getContentItemCount() + mBottomCount;
}
}
6.Item项的拖动与删除
ItemTouchHelper是一个处理RecyclerView的滑动删除和拖拽的辅助类,RecyclerView 的item拖拽移动和滑动删除就靠它来实现
itemTouchHelper=new ItemTouchHelper(new ItemTouchHelper.Callback() {
//用于设置拖拽和滑动的方向
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
int dragFlags=0,swipeFlags=0;
if(recyclerView.getLayoutManager() instanceof StaggeredGridLayoutManager||recyclerView.getLayoutManager() instanceof GridLayoutManager){
//网格式布局有4个方向
dragFlags=ItemTouchHelper.UP|ItemTouchHelper.DOWN|ItemTouchHelper.LEFT|ItemTouchHelper.RIGHT;
}else if(recyclerView.getLayoutManager() instanceof LinearLayoutManager){
//线性式布局有2个方向
dragFlags=ItemTouchHelper.UP|ItemTouchHelper.DOWN;
swipeFlags = ItemTouchHelper.START|ItemTouchHelper.END; //设置侧滑方向为从两个方向都可以
}
return makeMovementFlags(dragFlags,swipeFlags);//swipeFlags 为0的话item不滑动
}
//长摁item拖拽时会回调这个方法
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
int from=viewHolder.getAdapterPosition();
int to=target.getAdapterPosition();
Meizi moveItem=meizis.get(from);
meizis.remove(from);
meizis.add(to,moveItem);//交换数据链表中数据的位置
mAdapter.notifyItemMoved(from,to);//更新适配器中item的位置
return true;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
//这里处理滑动删除
}
@Override
public boolean isLongPressDragEnabled() {
return false;//返回true则为所有item都设置可以拖拽
}
});
当然,使用的前提是进行绑定
itemTouchHelper.attachToRecyclerView(recyclerview);
如果你想为item设置拖拽和滑动时的响应动画效果,可以利用ItemTouchHelper的下面三个方法。用线性布局示例:
//当item拖拽开始时调用
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
super.onSelectedChanged(viewHolder, actionState);
if(actionState==ItemTouchHelper.ACTION_STATE_DRAG){
viewHolder.itemView.setBackgroundColor(Color.LTGRAY);//拖拽时设置背景色为灰色
}
}
//当item拖拽完成时调用
@Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
viewHolder.itemView.setBackgroundColor(Color.WHITE);//拖拽停止时设置背景色为白色
}
//当item视图变化时调用
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
//根据item滑动偏移的值修改item透明度。screenwidth是我提前获得的屏幕宽度
viewHolder.itemView.setAlpha(1-Math.abs(dX)/screenwidth);
}
好了,关于RecycleView就讲解到这里,欢迎大家在下面进行评论