RecyclerView使用详解

RecyclerView是Android 5.x版本中新添加的一个全新控件,他比ListView,GridView更加的灵活,我们能够使用RecyclerView就完成ListView,GridView所做的工作,同时使用RecyclerView也能非常方便的实现瀑布流的效果。

一.竖屏ListView,横屏GridView效果

MainActivity代码:

public class MainActivity extends Activity implements MyRecyclerViewAdapter.OnItemClickListener{ private RecyclerView recyclerView; private List dataList; private MyRecyclerViewAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Display disPlay = getWindowManager().getDefaultDisplay(); Point size = new Point(); disPlay.getSize(size); int width = size.x; int height = size.y; System.out.println(width+","+height); dataList = new ArrayList(); initValues(); recyclerView = (RecyclerView) findViewById(R.id.recycler_view); if(width < height){ LinearLayoutManager llm = new LinearLayoutManager(this,LinearLayoutManager.VERTICAL, false); initRecyclerView(llm,new DividerItemDecoration(this, LinearLayoutManager.VERTICAL)); }else if(width > height){ LinearLayoutManager llm = new GridLayoutManager(this, 4); initRecyclerView(llm,new DividerGridItemDecoration(this)); } recyclerView.setItemAnimator(new DefaultItemAnimator()); adapter.setOnItemClickListener(this); } private void initRecyclerView(LayoutManager llm,ItemDecoration itemDecoration) { recyclerView.setLayoutManager(llm); adapter = new MyRecyclerViewAdapter(this,dataList); recyclerView.setAdapter(adapter); recyclerView.addItemDecoration(itemDecoration); } private void initValues() { for (int i = 0; i <= 50; i++) { dataList.add("item"+i); } } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } @Override public void onItemClick(View view, int position) { Toast.makeText(MainActivity.this, "点击了"+position, Toast.LENGTH_SHORT).show(); Intent intent = new Intent(MainActivity.this,StaggeredGridLayoutActivity.class); startActivity(intent); } @Override public void onItemLongClick(View view, int position) { Toast.makeText(MainActivity.this, "删除了"+position, Toast.LENGTH_SHORT).show(); adapter.removeData(position); adapter.notifyItemRangeChanged(position, adapter.getItemCount()); } }

首先获取到手机屏幕的宽高,如果widthwidth>height,那就展示一个4列的GridView。

MyRecyclerViewAdapter代码:

class MyRecyclerViewAdapter extends RecyclerView.Adapter { private List dataList; private Context mContext; public interface OnItemClickListener { void onItemClick(View view, int position); void onItemLongClick(View view, int position); } private OnItemClickListener mOnItemClickListener; public void setOnItemClickListener(OnItemClickListener mOnItemClickListener) { this.mOnItemClickListener = mOnItemClickListener; } public MyRecyclerViewAdapter(Context mContext, List dataList) { this.mContext = mContext; this.dataList = dataList; } class MyViewHolder extends ViewHolder { TextView tv; FrameLayout root_view; public MyViewHolder(View view) { super(view); tv = (TextView) view.findViewById(R.id.tv); root_view = (FrameLayout) view.findViewById(R.id.root_view); } } @Override public int getItemCount() { return dataList.size(); } @Override public void onBindViewHolder(final MyViewHolder holder, final int position) { holder.tv.setText(dataList.get(position)); if(null != mOnItemClickListener){ holder.root_view.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { mOnItemClickListener.onItemClick(view, position); } }); holder.root_view.setOnLongClickListener(new OnLongClickListener() { @Override public boolean onLongClick(View view) { mOnItemClickListener.onItemLongClick(view, position); return true; } }); } } public void addData(int position) { dataList.add(position, "Insert One"); notifyItemInserted(position); } public void removeData(int position) { dataList.remove(position); notifyItemRemoved(position); } @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(mContext).inflate( R.layout.recycler_view_item, parent, false); MyViewHolder holder = new MyViewHolder(view); return holder; } }

由于RecyclerView并没有像ListView一样给每个Item设置点击监听事件,所以我们需要自己实现对RecyclerView的点击监听。实现的方法可以是在我们的MyRecyclerViewAdapter中去为点击事件提供一个回调。

DividerItemDecoration代码(为ListView添加分割线):

public class DividerItemDecoration extends 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 typedArray = context.obtainStyledAttributes(ATTRS); mDivider = typedArray.getDrawable(0); typedArray.recycle(); setOrientation(orientation); } public void setOrientation(int orientation) { if(orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST){ try { throw new InterruptedIOException("invalid orientation"); } catch (InterruptedIOException e) { e.printStackTrace(); } } mOrientation = orientation; } @Override public void onDraw(Canvas c, RecyclerView parent, State state) { if(mOrientation == VERTICAL_LIST){ drawVertical(c,parent); }else{ drawHorizontal(c,parent); } } private void drawHorizontal(Canvas c, RecyclerView parent) { int top = parent.getPaddingTop(); int bottom = parent.getHeight()-parent.getPaddingBottom(); final int childCount = parent.getChildCount(); for(int i =0;i

DividerGridItemDecoration代码(为GridView添加分割线):

public class DividerGridItemDecoration extends ItemDecoration { private static final int[] ATTRS = new int[] { android.R.attr.listDivider }; private Drawable mDivider; public DividerGridItemDecoration(Context context) { final TypedArray typedArray = context.obtainStyledAttributes(ATTRS); mDivider = typedArray.getDrawable(0); typedArray.recycle(); } @Override public void onDraw(Canvas c, RecyclerView parent, State state) { drawHorizontal(c, parent); drawVertical(c, parent); } private void drawVertical(Canvas c, RecyclerView parent) { final int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); RecyclerView.LayoutParams params = (LayoutParams) child .getLayoutParams(); final int top = child.getTop() - params.topMargin; final int bottom = child.getBottom() + params.bottomMargin; final int left = child.getRight() + params.rightMargin; final int right = left + mDivider.getIntrinsicWidth(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } private void drawHorizontal(Canvas c, RecyclerView parent) { final int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); RecyclerView.LayoutParams params = (LayoutParams) child .getLayoutParams(); final int left = child.getLeft() - params.leftMargin; final int right = child.getRight() + params.rightMargin + mDivider.getIntrinsicWidth(); final int top = child.getBottom() + params.bottomMargin; final int bottom = top + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } private int getSpanCount(RecyclerView parent) { int spanCount = -1; LayoutManager layoutManager = parent.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) { spanCount = ((GridLayoutManager) layoutManager).getSpanCount(); } else if (layoutManager instanceof StaggeredGridLayoutManager) { spanCount = ((StaggeredGridLayoutManager) layoutManager) .getSpanCount(); } return spanCount; } private boolean isLastColum(RecyclerView parent, int pos, int spanCount, int childCount) { LayoutManager layoutManager = parent.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) { if ((pos + 1) % spanCount == 0) { return true; } } else if (layoutManager instanceof StaggeredGridLayoutManager) { int orientation = ((StaggeredGridLayoutManager) layoutManager) .getOrientation(); if (orientation == StaggeredGridLayoutManager.VERTICAL) { if ((pos + 1) % spanCount == 0) { return true; } } else { childCount = childCount - childCount % spanCount; if (pos >= childCount) return true; } } return false; } private boolean isLastRaw(RecyclerView parent, int pos, int spanCount, int childCount) { LayoutManager layoutManager = parent.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) { childCount = childCount - childCount % spanCount; if (pos >= childCount) return true; } else if (layoutManager instanceof StaggeredGridLayoutManager) { int orientation = ((StaggeredGridLayoutManager) layoutManager) .getOrientation(); if (orientation == StaggeredGridLayoutManager.VERTICAL) { childCount = childCount - childCount % spanCount; if (pos >= childCount) return true; } else { if ((pos + 1) % spanCount == 0) { return true; } } } return false; } @Override public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) { int spanCount = getSpanCount(parent); int childCount = parent.getAdapter().getItemCount(); if (isLastRaw(parent, itemPosition, spanCount, childCount)) { outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0); } else if (isLastColum(parent, itemPosition, spanCount, childCount)) { outRect.set(0, 0, 0, mDivider.getIntrinsicHeight()); } else { outRect.set(0, 0, mDivider.getIntrinsicWidth(), mDivider.getIntrinsicHeight()); } } }

RecyclerView本身并不支持divider属性,所以如果我们不做定制,RecyclerView的每个Item之间是不会有分割线的,但是我们可以通过addItemDecoration()来定制自己的分割线,addItemDecoration()接收的参数是个ItemDecoration对象,我们可以自定义一个DividerItemDecoration类继承自ItemDecoration,在这个类中完成分割线的定制。

二.实现瀑布流效果

StaggeredGridActivity代码:

public class StaggeredGridActivity extends Activity implements StaggeredGridAdapter.OnItemClickListener{ private RecyclerView recyclerView; private List dataList; private StaggeredGridAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); recyclerView = (RecyclerView) findViewById(R.id.recycler_view); dataList = new ArrayList(); initValues(); adapter = new StaggeredGridAdapter(this, dataList); recyclerView.setLayoutManager(new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL)); recyclerView.setItemAnimator(new DefaultItemAnimator()); recyclerView.setAdapter(adapter); adapter.setOnItemClickListener(this); } private void initValues() { for (int i = 0; i < 50; i++) { dataList.add("item"+i); } } @Override public void onItemClick(View view, int position) { Toast.makeText(StaggeredGridLayoutActivity.this, "点击了"+position, Toast.LENGTH_SHORT).show(); } @Override public void onItemLongClick(View view, int position) { Toast.makeText(StaggeredGridLayoutActivity.this, "删除了"+position, Toast.LENGTH_SHORT).show(); adapter.removeItem(position); adapter.notifyItemRangeChanged(position, adapter.getItemCount()); } }

StaggeredGridAdapter代码:

public class StaggeredGridAdapter extends RecyclerView.Adapter { private List dataList; private List mHeights; private Context mContext; public interface OnItemClickListener{ void onItemClick(View view,int position); void onItemLongClick(View view,int position); } private OnItemClickListener mOnItemClickListener; public void setOnItemClickListener(OnItemClickListener mOnItemClickListener){ this.mOnItemClickListener = mOnItemClickListener; } public StaggeredGridAdapter(Context mContext, List dataList) { this.dataList = dataList; this.mContext = mContext; mHeights = new ArrayList(); for(int i = 0;i

其实瀑布流的实现和GridView的实现基本一致,只是瀑布流的每个Item的高不一样,所以这里在Adapter中动态的设置每个Item的高,在onBindViewHolder()中我们用到了LayoutParams方法,这里需要注意的是LayoutParams有很多不同的包,导包的时候一定要导对应的包。不然会报类型不匹配的错误。

三.添加、删除Item

1.删除:

notifyItemRemoved(int position)

2.添加:

notifyItemInserted(int position)

设置动画:

recyclerView.setItemAnimator(new DefaultItemAnimator())

四.点击监听(onClick,onLongClick)

如何实现RecyclerView的点击事件在上面的代码已经给出,上面的代码中如果是点击,则显示一条Toast,如果是长按则删除对应的Item。

点击事件处理中遇到的2个问题:

1.长按删除后也会显示非长按点击的Toast:

原因:一开始在setOnLongClickListener中返回了false,这样这个点击事件还会在向下传递,就会传递到setClickListener中,如果返回了true,则代表这次点击事件在setOnLongClickListener中就被消费了,不会再往下传递。(个人理解,有待验证...)可以参考下之前的文章 

OnTouch事件ACTION_DOWN,ACTION_MOVE,ACTION_UP的事件拦截

解决:setOnLongClickListener中return true。

2.长按删除后出现数据错乱:

原因:StaggeredGridAdapter的removeItem()方法中,我们对Item的删除使用了notifyItemRemoved(int position)方法,这是RecyclerView为我们提供的带动画的删除方法,但是这个方法只是删除了界面上的Item,为保持数据的一致,我们还需要删除数据源中对应的数据,即dataList.remove(position)。如果对Item的删除仅仅这样处理的话,你会发现再去做删除操作时会发生数据的错乱,导致这个的原因是,我们在做完删除处理后并没有对RecyclerView进行刷新,所以会导致RecyclerView显示的数据与数据源不一致

解决:解决这个问题的办法就是在删除数据后我们就对RecyclerView刷新一次。RecyclerView和ListView一样,有notifyDataSetChanged()方法,但是使用这个方法进行刷新我们就无法看到删除动画了,原因是调用notifyDataSetChanged()刷新屏幕上显示的所有item的话,必然也会刷新当前正在执行动画的那个Item,这样导致的结果是前面的动画还没执行完它马上又被刷新了,动画就看不见了。这里我们可以用notifyItemRangeChanged(int position, int itemCount())方法,只需要从被删除的Item后面开始刷新就可以了,这样就保持了原有的删除动画。







你可能感兴趣的:(Android学习心得)