RecyclerView 是个列表,从android版本5.0引进的,可以替代 ListView 实现一些功能,用法和 ListView 差不多,但解耦效果更好,下面在 Activity 中写一个简单的例子
Activity:
private void init(){
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycle);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(linearLayoutManager);
NumAdapter adapter = new NumAdapter(this);
recyclerView.setAdapter(adapter);
}
public class NumAdapter extends RecyclerView.Adapter
private Context context;
public NumAdapter(Context context) {
this.context = context;
}
@Override
public NumViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
TextView tv = new TextView(context);
tv.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 100));
NumViewHolder holder = new NumViewHolder(tv);
return holder;
}
@Override
public void onBindViewHolder(final NumViewHolder holder, final int position) {
holder.tv.setText(" this is " + position );
}
@Override
public int getItemCount() {
return 15;
}
class NumViewHolder extends RecyclerView.ViewHolder {
TextView tv;
public NumViewHolder(final View itemView) {
super(itemView);
tv = (TextView) itemView;
}
}
}
这个里面,我写的比较简单,固定的15个条目,每个item都是一个 TextView,宽度为铺满父容器,高度为100px。注意到 onCreateViewHolder() 方法中,在这个方法中创建 itemView 和 ViewHolder,我们直接创建,然后在 onBindViewHolder() 中使用;ListView 的适配器中 getView() 方法中,我们也会创建布局,使用ViewHolder是为了复用,并且还需要我们对
convertView 进行判空,自己实现复用功能,而 RecyclerView 的适配器就没这么麻烦了,它把 getView() 方法中的功能一拆为二,并且源码中已经替我们是想了 ViewHolder 的复用功能,我们只需要把创建的功能和UI改变的功能写好就行。
上述方法中,我们如果想实现item的点击事件和长点击事件怎么办?这就显出 RecyclerView 的不足处了,系统没有直接提供这些方法,还得我们自己动手来封装,最粗糙的写法,定义回调,每个item设置点击事件。比如设置个回调接口
public interface OnItemClickListener {
void onItemClick(View view, int position);
boolean onItemLongClick(View view, int position);
}
OnItemClickListener simpleClike = new OnItemClickListener(){
@Override
public void onItemClick(View view, int position) {
Toast.makeText(getApplicationContext(), " onItemClick " + position,Toast.LENGTH_LONG).show();
}
@Override
public boolean onItemLongClick(View view, int position) {
Toast.makeText(getApplicationContext(), " onItemLongClick " + position,Toast.LENGTH_LONG).show();
return true;
}
};
这样回调就定义好了,我们在回调中写个Toast事件,现在把回调设置到适配器中,适配器对外暴露接口;然后针对 item 设置点击事件和长按点击事件,在事件中写入回调,如下
public class NumAdapter extends RecyclerView.Adapter
private Context context;
private OnItemClickListener mOnItemClickListener;
public NumAdapter(Context context) {
this.context = context;
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.mOnItemClickListener = onItemClickListener;
}
@Override
public NumViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
TextView tv = new TextView(context);
tv.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 100));
NumViewHolder holder = new NumViewHolder(tv);
return holder;
}
@Override
public void onBindViewHolder(final NumViewHolder holder, final int position) {
holder.tv.setText(" this is " + position );
holder.tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mOnItemClickListener != null){
mOnItemClickListener.onItemClick(v, position);
}
}
});
holder.tv.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if(mOnItemClickListener != null){
return mOnItemClickListener.onItemLongClick(v, position);
}
return false;
}
});
}
@Override
public int getItemCount() {
return 15;
}
class NumViewHolder extends RecyclerView.ViewHolder {
TextView tv;
public NumViewHolder(final View itemView) {
super(itemView);
tv = (TextView) itemView;
}
}
}
这样写是实现了需求,但有个问题。系统源码为我们实现了 ViewHolder 的复用,但 onBindViewHolder() 方法是每个item出现时都会调用,也就是说,如果我们不停的上下滑动 RecyclerView, onBindViewHolder() 会被不停的调用,setOnClickListener 方法会不停创建对象,怎么减少无谓的对象创建呢?这便提到了优化的策略。由于 onCreateViewHolder() 方法有缓存策略,所以我们可以把点击事件写在这个里面,在创建 ViewHolder 中想想办法,这时候需要注意的是如何获取 position,没办法直接穿进去,好在适配器有个方法
public class NumAdapter extends RecyclerView.Adapter
private Context context;
private OnItemClickListener mOnItemClickListener;
public NumAdapter(Context context) {
this.context = context;
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.mOnItemClickListener = onItemClickListener;
}
@Override
public NumViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
TextView tv = new TextView(context);
tv.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 100));
NumViewHolder holder = new NumViewHolder(tv);
return holder;
}
@Override
public void onBindViewHolder(final NumViewHolder holder, final int position) {
holder.tv.setText(" this is " + position );
}
@Override
public int getItemCount() {
return 15;
}
class NumViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener{
TextView tv;
public NumViewHolder(final View itemView) {
super(itemView);
tv = (TextView) itemView;
tv.setOnClickListener(this);
tv.setOnLongClickListener(this);
}
@Override
public void onClick(View v) {
if(mOnItemClickListener != null){
mOnItemClickListener.onItemClick(v, getLayoutPosition());
}
}
@Override
public boolean onLongClick(View v) {
if(mOnItemClickListener != null){
return mOnItemClickListener.onItemLongClick(v, getLayoutPosition());
}
return false;
}
}
}
如果说我们想把集合中的对象传递出来怎么办?这里就要用到 View 的 setTag() 这个方法了,我们先在 styles.xml 中自定义一个属性:
@Override
public void onBindViewHolder(final NumViewHolder holder, final int position) {
holder.tv.setText(" this is " + position );
holder.tv.setTag(R.id.holder_id, position);
}
class NumViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener{
TextView tv;
public NumViewHolder(final View itemView) {
super(itemView);
tv = (TextView) itemView;
tv.setOnClickListener(this);
tv.setOnLongClickListener(this);
}
@Override
public void onClick(View v) {
if(mOnItemClickListener != null){
mOnItemClickListener.onItemClick(v, (Integer) tv.getTag(R.id.holder_id));
}
}
@Override
public boolean onLongClick(View v) {
if(mOnItemClickListener != null){
return mOnItemClickListener.onItemLongClick(v, (Integer) tv.getTag(R.id.holder_id));
}
return false;
}
}
这样,就可以了。如果说我们想传递的是其他类型,比如 String 或是 对象等,怎么办?同样道理,先修改回调接口。
public interface OnItemClickListener
void onItemClick(View view, T value);
void onItemLongClick(View view, T value);
}
OnItemClickListener simpleClike = new OnItemClickListener(){
@Override
public void onItemClick(View view, Object value) {
Toast.makeText(getApplicationContext(), " onItemClick " + value,Toast.LENGTH_LONG).show();
}
@Override
public void onItemLongClick(View view, Object value) {
Toast.makeText(getApplicationContext(), " onItemLongClick " + value,Toast.LENGTH_LONG).show();
}
};
NumAdapter:
@Override
public void onBindViewHolder(final NumViewHolder holder, final int position) {
holder.tv.setText(" this is " + position );
holder.tv.setTag(R.id.holder_id, holder.tv.getText());
}
class NumViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener{
TextView tv;
public NumViewHolder(final View itemView) {
super(itemView);
tv = (TextView) itemView;
tv.setOnClickListener(this);
tv.setOnLongClickListener(this);
}
@Override
public void onClick(View v) {
if(mOnItemClickListener != null){
mOnItemClickListener.onItemClick(v, tv.getTag(R.id.holder_id));
}
}
@Override
public boolean onLongClick(View v) {
if(mOnItemClickListener != null){
return mOnItemClickListener.onItemLongClick(v, tv.getTag(R.id.holder_id));
}
return false;
}
}
我们定义回调时用到了泛型,大部分情况,我们在使用 RecyclerView 的时候已经知道集合中的对象的类型,所以可以再稍作修改
OnItemClickListener simpleClike = new OnItemClickListener
@Override
public void onItemClick(View view, String value) {
Toast.makeText(getApplicationContext(), " onItemClick " + value,Toast.LENGTH_LONG).show();
}
@Override
public void onItemLongClick(View view, String value) {
Toast.makeText(getApplicationContext(), " onItemLongClick " + value,Toast.LENGTH_LONG).show();
}
});
适配器里面不用修改,这样就可以了。适配器里返回的对象,我们只需要考虑回调中实现自己的逻辑即可,即 Toast 的部分。