基本简介:
RecyclerView是support-v7包中的新组件,是一个强大的滑动组件,
与ListView相比,
同样拥有item回收复用的功能,但是直接把viewholder的实现封装起来,用户只要实现自己的viewholder就可以了,该组件会自动帮你回收复用每一个item。它不但变得更精简,也变得更加容易使用,而且更容易组合设计出自己需要的滑动布局
当然,如果只是动态展示数据,listview也可以做到,用它替代listview的原因有几个:
1.它封装了viewholder的回收复用,解耦了生成viewholder跟设置viewholder
2.RecyclerView使用布局管理器管理子view的位置(目前尚只提供了LinearLayoutManager),也就是只要通过管理器(LinearLayoutManga,StaggeredGridLayoutManager,GridLayoutManager )来切换使用GridView,ListView,还是StaggerView.
setItemAnimator)
4.还可以自定义边线(通过addItemDecoration这个来添加)
APi基本简介
1.class RecyclerView.Adapter<VH extends RecyclerView.ViewHolder>
描述:
Base class for an Adapter
Adapters provide a binding from an app-specific data set to views that are displayed within a RecyclerView.
也就是提供一个BaseAdapter
final void | bindViewHolder(VH holder, int position) This method internally calls |
final VH | createViewHolder( ViewGroup parent, int viewType) This method calls |
abstract int | getItemCount() Returns the total number of items in the data set hold by the adapter. |
long | getItemId(int position) Return the stable ID for the item at |
int | getItemViewType(int position) Return the view type of the item at |
(1).bindViewHolder就是把ViewHolder中的每个对应位置的内容设置进去
(2).createViewHolder就是创建ViewHolder
(3).getItemcount就相当于ListView中的getCount
(4).getItemViewType就是根据不同位置返回当前位置的条目类型
(这样一个ListView或者GridView里面可以有多种不同的条目,比如拉到最底下有个刷新)
注意:
因为RecyclerView没有像ListView一样设置setOnItemClickerListener或者setOnLongClickerListener,所以就通过ViewHolder来实现(下面有例子)
2.class RecyclerView.ItemAnimator
This class defines the animations that take place on items as changes are made to the adapter.
定义数据增删时的动画
3.class RecyclerView.ItemDecoration
item views from the adapter's data set.
An ItemDecoration allows the application to add a special drawing and layout offset to specific
边线
4.class RecyclerView.LayoutManager
A LayoutManager is responsible for measuring and positioning item views within a RecyclerView as well as determining the policy for when to recycle item views that are no longer visible to the user.
测量和摆放所有的view,也就是控制显示样式
案例:
一:(一个Vertical方向的ListView)
效果:
代码:
ListViewVertical.java
public class ListViewVertical extends BaseActivity implements SwipeRefreshLayout.OnRefreshListener, MyAdapter.onRecyclerViewListener, View.OnClickListener {
@Bind(R.id.list)
RecyclerView mRecycleList;
@Bind(R.id.bt_add)
Button btAdd;
@Bind(R.id.swipe_refresh_widget)
SwipeRefreshLayout mSwipeRefreshLayout;
private LinearLayoutManager mLLmanager;
private ArrayList<String> mList = new ArrayList<>();
private ArrayList<String> mListExtraOne = new ArrayList<>();
private ArrayList<String> mListExtraTwo = new ArrayList<>();
private MyAdapter mAdapter;
private int mLastVisibleItem;
private int mFirstVisibleItem;
private int ii;
@Override
protected void instantiation() {
setContentView(R.layout.activity_list_view_horizontal_vertical);
ButterKnife.bind(this);
//下拉刷新控制颜色
mSwipeRefreshLayout.setColorSchemeResources(android.R.color.holo_blue_light, android.R.color.holo_red_light, android.R.color.holo_orange_light, android.R.color.holo_green_light);
//设置他的下拉刷新监听器
mSwipeRefreshLayout.setOnRefreshListener(this);
//管理器
mLLmanager = new LinearLayoutManager(ListViewVertical.this, LinearLayoutManager.VERTICAL, false);
//
mRecycleList.setLayoutManager(mLLmanager);
}
@Override
protected void dataBind() {
//数据初始化
for (int i = 0; i < 30; i++) {
mList.add("我是内容" + i);
}
for (int i = 0; i < 3; i++) {
mListExtraOne.add("我是下拉增加的" + i);
}
for (int i = 0; i < 2; i++) {
mListExtraTwo.add("我是上拉增加的" + i);
}
mAdapter = new MyAdapter(mList, ListViewVertical.this);
//使RecyclerView保持固定的大小,这样会提高RecyclerView的性能。????
mRecycleList.setHasFixedSize(true);
mRecycleList.setAdapter(mAdapter);
}
@Override
protected void eventBind() {
mAdapter.setonRecyclerViewListener(this);
btAdd.setOnClickListener(this);
mRecycleList.setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
//滑动的状态一变化就会调用
//这里我们需要的滑到最后一个条目的时候(可见条目是最后一个,而且状态是idle)有一个自动刷新的条目出来
System.out.println("xcqmLastVisibleItem" + mLastVisibleItem);
System.out.println("xcqmList.size()" + mList.size());
//这里的mLastVisibleItem已经算上了 上拉的那个条目的item,所有等于mList.size()
if (newState == RecyclerView.SCROLL_STATE_IDLE && mLastVisibleItem == mList.size()) {
handler.sendEmptyMessageDelayed(1, 3000);
}
//解决recyclerview 的item并没有达到第一个也能下拉刷新
if (newState == recyclerView.SCROLL_STATE_IDLE && mFirstVisibleItem == 0) {
mSwipeRefreshLayout.setEnabled(true);
} else {
mSwipeRefreshLayout.setEnabled(false);
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
//滑动的时候会调用的函数
mLastVisibleItem = mLLmanager.findLastVisibleItemPosition();
mFirstVisibleItem = mLLmanager.findFirstVisibleItemPosition();
}
});
}
private android.os.Handler handler = new android.os.Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
Toast.makeText(ListViewVertical.this, "DOWN", Toast.LENGTH_LONG).show();
mSwipeRefreshLayout.setRefreshing(false);
// mAdapter.getmList().clear();
// mAdapter.setmList(mList);
mAdapter.notifyDataSetChanged();
break;
case 1:
Toast.makeText(ListViewVertical.this, "UP", Toast.LENGTH_LONG).show();
mList.addAll(mListExtraTwo);
mAdapter.setmList(mList);
mAdapter.notifyDataSetChanged();
break;
}
}
};
//下拉刷新
@Override
public void onRefresh() {
System.out.println("xcq下拉");
handler.sendEmptyMessageDelayed(0, 3000);
}
@Override
protected void onDestroy() {
super.onDestroy();
ButterKnife.unbind(this);
}
@Override
public void onItemClick(int position) {
System.out.println("xcqMA单击");
Toast.makeText(ListViewVertical.this, "我是条目" + mList.get(position) + "我被点了", Toast.LENGTH_SHORT).show();
}
@Override
public boolean onLongClick(int position) {
mAdapter.notifyItemRemoved(position);
mList.remove(position);
mAdapter.notifyItemRangeChanged(position, mAdapter.getItemCount());
//记住如果OnlongClick return true 相当于处理了,那么onclick就不会在处理的
return true;
}
@Override
public void onClick(View view) {
if (view == btAdd) {
// Toast.makeText(MainActivity.this,"添加数据",Toast.LENGTH_SHORT).show();
ii++;
//插入到第二条
mAdapter.notifyItemInserted(3);
//真正开始插数据
mList.add(2, "刚加的" + ii);
//要把第二条开始一下的数据全部刷新一下
mAdapter.notifyItemRangeChanged(2, mAdapter.getItemCount());
//全部刷新没有动画了
// mAdapter.notifyDataSetChanged();
}
}
}
MyAdapter.java
public class MyAdapter extends RecyclerView.Adapter {
private List<String> mList;
private Context mContext;
// private List<Integer> mHeightList;
private final static int TYPE_NORMAL_ITEM =1;
private final static int TYPE_FOOTER_ITEM =2;
private onRecyclerViewListener mRecyclerViewListener;
//写一个借口用来实现onitemclick 和 onlongclick
public interface onRecyclerViewListener{
void onItemClick(int position);
boolean onLongClick(int position);
}
//外部给出listener
public void setonRecyclerViewListener(onRecyclerViewListener listener){
this.mRecyclerViewListener = listener;
}
public MyAdapter(List<String> list, Context context) {
this.mList = list;
this.mContext = context;
//为了让瀑布流更明显,高度搞个随机的
// mHeightList = new ArrayList<Integer>();
// for(int i =0;i<50;i++){
// mHeightList.add((int) (50+Math.random()*300));
// }
}
public List<String> getmList() {
return mList;
}
public void setmList(List<String> mList) {
this.mList = mList;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//相当于原来的getView
if (viewType == TYPE_NORMAL_ITEM) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_text, null);
view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
return new ItemViewHolder(view);
} else if (viewType == TYPE_FOOTER_ITEM) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.footerview, null);
view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
return new FooterViewHolder(view);
}
return null;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i) {
//然后在这里给textview设置具体内容
if(viewHolder instanceof ItemViewHolder){
<span style="background-color: rgb(255, 0, 0);"> //设置不同高度,为了让瀑布流更加明显</span>
// LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) ((ItemViewHolder) viewHolder).tv_item.getLayoutParams();
// params.height = mHeightList.get(i);
// viewHolder.itemView.setLayoutParams(params);
((ItemViewHolder) viewHolder).tv_item.setText(mList.get(i));
}
}
@Override
public int getItemViewType(int position) {
//添加上拉刷新的条目
//list的最后一个是刷新条目
if(position == (getItemCount() -1)){
return TYPE_FOOTER_ITEM;
}else{
return TYPE_NORMAL_ITEM;
}
}
@Override
public int getItemCount() {
//相当于普通的listview中的getcount
return mList.size()>0 ? mList.size()+1 : 0;
}
//更多条目的那个条目的holder
class FooterViewHolder extends RecyclerView.ViewHolder{
public FooterViewHolder(View itemView) {
super(itemView);
}
}
//继承系统的ViewHolder
//recycle本身没有onitemclick,和onclick//我们这里就自己给他实现一个
class ItemViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener ,View.OnLongClickListener{
TextView tv_item;
private LinearLayout ll_item;
public ItemViewHolder(View itemView) {
super(itemView);
//然后得到了listView
tv_item = (TextView) itemView.findViewById(R.id.tv_item);
ll_item = (LinearLayout) itemView.findViewById(R.id.ll_item);
ll_item.setOnClickListener(this);
ll_item.setOnLongClickListener(this);
}
@Override
public void onClick(View view) {
System.out.println("xcqMy单击");
if(null != mRecyclerViewListener){
mRecyclerViewListener.onItemClick(this.getPosition());
}
}
@Override
public boolean onLongClick(View view) {
System.out.println("xcqMY长按");
if(null != mRecyclerViewListener){
//这个postition
return mRecyclerViewListener.onLongClick(this.getPosition());
}
return false;
}
}
}
总结:
布局文件就不贴了,最后代码里面会有,这个例子实现简单的四个功能
1.上拉下拉刷新
用了Android自带的下拉刷新SwipeRefreshLayout,然后上拉刷新是是在adapter中通过getItemViewType做了一个第二种类型的条目
//下拉刷新控制颜色
mSwipeRefreshLayout.setColorSchemeResources(android.R.color.holo_blue_light, android.R.color.holo_red_light, android.R.color.holo_orange_light, android.R.color.holo_green_light);
//设置他的下拉刷新监听器
mSwipeRefreshLayout.setOnRefreshListener(this);
2.短按打印当前条目
RecyclerView自身是没有单个条目的点击事件(前面已经讲了),所以通过Adapter中的ViewHolder设置单个条目的点击事件,来实现这一功能
3.长按删除
同上,通过Adapter中的ViewHolder设置长按点击事件(如果长按return true短按就不会在执行了)
4.添加条目
这个就是普通的添加条目
二:(一个horizontal的ListView)
效果:
代码:
只是再上一个例子的基础上,改变了一下管理器
mLLmanager = new LinearLayoutManager(ListViewHorizontal.this, LinearLayoutManager.HORIZONTAL, false);
就是这么简单。
三.(一个Vertical方向的GridView)
效果:
代码:
gdmanager = new GridLayoutManager(this,3,GridLayoutManager.VERTICAL,false);
不过这里貌似出现了一个bug,就是下拉一直刷新停不下来,待解决
四.(瀑布流)
效果:
代码:
Adapter.java
public class StaggeredHomeAdapter extends RecyclerView.Adapter<StaggeredHomeAdapter.MyViewHolder>
{
private List<String> mDatas;
private LayoutInflater mInflater;
private List<Integer> mHeights;
public interface OnItemClickLitener
{
void onItemClick(View view, int position);
void onItemLongClick(View view, int position);
}
private OnItemClickLitener mOnItemClickLitener;
public void setOnItemClickLitener(OnItemClickLitener mOnItemClickLitener)
{
this.mOnItemClickLitener = mOnItemClickLitener;
}
public StaggeredHomeAdapter(Context context, List<String> datas)
{
mInflater = LayoutInflater.from(context);
mDatas = datas;
mHeights = new ArrayList<Integer>();
for (int i = 0; i < mDatas.size(); i++)
{
mHeights.add( (int) (100 + Math.random() * 300));
}
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
MyViewHolder holder = new MyViewHolder(mInflater.inflate(R.layout.item_staggered_home, parent, false));
return holder;
}
@Override
public void onBindViewHolder(final MyViewHolder holder, final int position)
{
<span > </span>//这里就是设置不同条目的高度,这样才有瀑布流的感觉,在案例一也写了(被注释掉了)
LayoutParams lp = holder.tv.getLayoutParams();
lp.height = mHeights.get(position);
holder.tv.setLayoutParams(lp);
holder.tv.setText(mDatas.get(position));
// 如果设置了回调,则设置点击事件
if (mOnItemClickLitener != null)
{
holder.itemView.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
int pos = holder.getLayoutPosition();
mOnItemClickLitener.onItemClick(holder.itemView, pos);
}
});
holder.itemView.setOnLongClickListener(new OnLongClickListener()
{
@Override
public boolean onLongClick(View v)
{
int pos = holder.getLayoutPosition();
mOnItemClickLitener.onItemLongClick(holder.itemView, pos);
removeData(pos);
return false;
}
});
}
}
@Override
public int getItemCount()
{
return mDatas.size();
}
public void addData(int position)
{
mDatas.add(position, "Insert One");
mHeights.add( (int) (100 + Math.random() * 300));
notifyItemInserted(position);
}
public void removeData(int position)
{
mDatas.remove(position);
notifyItemRemoved(position);
}
class MyViewHolder extends ViewHolder
{
TextView tv;
public MyViewHolder(View view)
{
super(view);
tv = (TextView) view.findViewById(R.id.id_num);
}
}
}
StaggViewHorizontal.java
public class StaggViewHorizontal extends Activity
{
private RecyclerView mRecyclerView;
private List<String> mDatas;
private StaggeredHomeAdapter mStaggeredHomeAdapter;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_single_recyclerview);
initData();
mRecyclerView = (RecyclerView) findViewById(R.id.id_recyclerview);
mStaggeredHomeAdapter = new StaggeredHomeAdapter(this, mDatas);
mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(3,
StaggeredGridLayoutManager.VERTICAL));
mRecyclerView.setAdapter(mStaggeredHomeAdapter);
// 设置item动画(这个事默认的)
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
initEvent();
}
private void initEvent()
{
mStaggeredHomeAdapter.setOnItemClickLitener(new StaggeredHomeAdapter.OnItemClickLitener()
{
@Override
public void onItemClick(View view, int position)
{
Toast.makeText(StaggViewHorizontal.this,
position + " click", Toast.LENGTH_SHORT).show();
}
@Override
public void onItemLongClick(View view, int position)
{
Toast.makeText(StaggViewHorizontal.this,
position + " long click", Toast.LENGTH_SHORT).show();
}
});
}
protected void initData()
{
mDatas = new ArrayList<String>();
for (int i = 'A'; i < 'z'; i++)
{
mDatas.add("" + (char) i);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
getMenuInflater().inflate(R.menu.main_staggered, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
switch (item.getItemId())
{
case R.id.id_action_add:
mStaggeredHomeAdapter.addData(1);
break;
case R.id.id_action_delete:
mStaggeredHomeAdapter.removeData(1);
break;
}
return true;
}
}
总结:
其实也只是改了下管理器,主要是随机生成了单个条目的高度