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<string> 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<string>();
		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());
	}

}</string></string>
首先获取到手机屏幕的宽高,如果width<height,那就展示一个ListView,如果width>height,那就展示一个4列的GridView。
MyRecyclerViewAdapter代码:
class MyRecyclerViewAdapter extends
		RecyclerView.Adapter<myrecyclerviewadapter myviewholder> {
	private List<string> 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<string> 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;
	}
}
</string></string></myrecyclerviewadapter>
由于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<childCount;i++){
			final View child = parent.getChildAt(i);
			final RecyclerView.LayoutParams params = (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);
			
		}
	}

	private void drawVertical(Canvas c, RecyclerView parent) {
		int left = parent.getPaddingLeft();
		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 = (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);
			
		}
	}

	@Override
	public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
			State state) {
		if(mOrientation == VERTICAL_LIST){
			outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
		}else{
			outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
		}
	}	
}

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<string> 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<string>();
		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());
	}
}
</string></string>
StaggeredGridAdapter代码:
public class StaggeredGridAdapter extends
		RecyclerView.Adapter<staggeredgridadapter myviewholder> {
	private List<string> dataList;
	private List<integer> 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<string> dataList) {
		this.dataList = dataList;
		this.mContext = mContext;
		
		mHeights = new ArrayList<integer>();
		for(int i = 0;i<dataList.size();i++){
			mHeights.add((int)(100+Math.random()*300));
		}
	}

	@Override
	public int getItemCount() {
		return dataList.size();
	}

	@Override
	public void onBindViewHolder(final MyViewHolder holder, final int position) {
		LayoutParams params = (LayoutParams) holder.tv.getLayoutParams();
		params.height = mHeights.get(position);
		holder.tv.setLayoutParams(params);
		holder.tv.setText(dataList.get(position));
		if(mOnItemClickListener != null){
			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 addItem(int position){
		dataList.add(position, "Insert One");
		mHeights.add( (int) (100 + Math.random() * 300));
		notifyItemInserted(position);
	}
	
	public void removeItem(int position){
		dataList.remove(position);
		notifyItemRemoved(position);
	}

	@Override
	public MyViewHolder onCreateViewHolder(ViewGroup parent, int arg1) {
		View view = LayoutInflater.from(mContext).inflate(
				R.layout.stagger_recycler_view_item, parent, false);
		MyViewHolder holder = new MyViewHolder(view);
		return holder;
	}

	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);
		}

	}

}
</integer></string></integer></string></staggeredgridadapter>

其实瀑布流的实现和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中就被消费了,不会再往下传递。(个人理解,有待验证...)可以参考下之前的文章 

解决: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后面开始刷新就可以了,这样就保持了原有的删除动画。