介绍
RecyclerView是一种高级的ListView,以后可以用它来代替ListView
CardView则是一种更好看的视图,使用比较简单,这里我把他俩放在一起介绍
基本使用步骤
导入依赖
我们得先导入RecyclerView和CardView的依赖
compile 'com.android.support:recyclerview-v7:26.0.0' compile 'com.android.support:cardview-v7:26.0.0' //recyclerView和cardView库
在主布局中加入RecyclerView
在activity_main.xml中加入RecyclerView
<android.support.v7.widget.RecyclerView android:id="@+id/rv_recycler" android:layout_width="match_parent" android:layout_height="wrap_content"> </android.support.v7.widget.RecyclerView>
用法其实跟listView差不多
创建item的布局
为RecyclerView的子项创建布局,名之为iterm.recycler.xml
<android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto" <!-- 注意cardView的命名空间 --> xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" card_view:cardCornerRadius="4dp" <!-- 设置卡片四个角的弧度半径 --> android:layout_margin="10dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="90dp" android:orientation="horizontal"> <ImageView android:id="@+id/iv_img" android:layout_weight="1" android:layout_width="90dp" android:gravity="center" android:layout_height="match_parent" /> <TextView android:id="@+id/tv_img" android:layout_weight="1" android:layout_width="50dp" android:gravity="center" android:layout_height="match_parent" /> <Button android:id="@+id/btn_img" android:text="这是哪个队" android:gravity="center" android:layout_width="wrap_content" android:layout_height="match_parent" /> </LinearLayout> </android.support.v7.widget.CardView>为了好看我用了CardView做为子项的根布局,注意它的用法,参加注释
在MainActivity中设置RecyclerView
@BindView(R.id.rv_recycler) RecyclerView recyclerView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activty_main); ButterKnife.bind(this); recyclerView.setLayoutManager(new LinearLayoutManager(this)); // 设置线性布局 ... }
我使用了ButterKnife来初始化控件(其实就是findViewById()),现在先不管他,以后再介绍用法
通过recyclerView.setLayoutManager()可以指定recyclerView是哪种布局,这里我直接使用线性布局
构造Adapter类
RecyclerView跟ListView一样,也需要适配器。这里我们写一个适配器类,继承RecyclerView.Adapter
public class AdapterForRecyclerVIew extends RecyclerView.Adapter { private LinkedList<String> urls = new LinkedList<>(); private LinkedList<String> descriptions = new LinkedList<>(); private Context context = null; private String TAG = "AdapterForRecyclerVIew"; public AdapterForRecyclerVIew(LinkedList<String> urls,LinkedList<String> descriptions, Context context) { this.urls = urls; this.descriptions = descriptions; this.context = context; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { CardView layout = (CardView) LayoutInflater.from(context).inflate(R.layout.item_recycler, parent, false); // 要inflate子项的根节点 carView return new MyViewHolderForImage(layout); } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { MyViewHolderForImage holderForImage = (MyViewHolderForImage) holder; Picasso.with(context).load(urls.get(position - 1)).into(holderForImage.getImageView()); holderForImage.getTextView().setText(descriptions.get(position - 1)); } @Override public int getItemCount() { return urls.size(); } }
里面的三个方法onCreateViewHolder()、onBindViewHolder()、getItemCount()都是我们必须要实现的。
其中onCreateViewHolder()用来创建子项的viewHolder,所以我们要新建一个ViewHolder类,继承RecyclerView.ViewHolder,这个ViewHolder里面就是子项的所有控件,子项的UI逻辑,都放在ViewHolder中,可以作为mvp设计模式中的presenter(代码下一步再说)
onBindViewHolder()则是加载子项viewHolder的内容,参数里面的holder就是上一个方法onCreateViewHolder()中返回过来的
getItemCount()方法决定了recyclerView要显示多少个子项
创建ViewHolder类
根据上一步说的,我们把子项逻辑都放在ViewHolder中
public class MyViewHolderForImage extends RecyclerView.ViewHolder { @BindView(R.id.iv_img) ImageView imageView; @BindView(R.id.tv_img) TextView textView; public MyViewHolderForImage(View itemView) { super(itemView); ButterKnife.bind(this,itemView); // 根据adapter中的onCreateViewHolder(),这里传进来的itermView就是cardView textView.setVisibility(View.INVISIBLE); } @OnClick(R.id.btn_img) public void changeVisibility(){ textView.setVisibility(View.VISIBLE); } public ImageView getImageView() { return imageView; } public TextView getTextView() { return textView; } }
还是用了ButterKnife绑定控件,设置onClick监听
为recyclerView设置adapter,并初始化数据
回到onCreate(),设置一下adapter,并赋予一些数据
adapter = new AdapterForRecyclerVIew(urls, descriptions, this); addUrls(); addDescriptions(); recyclerView.setAdapter(adapter);
private void addDescriptions() { descriptions.add("南部之星拜仁慕尼黑"); descriptions.add("骄傲的大黄蜂多特蒙德"); descriptions.add("红魔曼联"); descriptions.add("蓝军切尔西"); }
private void addUrls() { urls.add("http://n.sinaimg.cn/sports/transform/20170216/1s3V-fyarzzv2801842.jpg"); urls.add("http://www.zq1.com/Upload/20170415/235459msc9i5yiqracirfw.jpg"); urls.add("http://k.sinaimg.cn/n/sports/transform/20160424/dfCS-fxrqhar9877773.JPG/w570fe9.jpg"); urls.add("http://n.sinaimg.cn/sports/transform/20170423/L9Uj-fyeqcac1387497.jpg"); }
由于我是先设置数据,再设置适配,所以不用notifyDataSetChanged()
效果
ok,这就是recyclerView的基本用法
多种类型子项混排
可以看到,在录屏gif种,最上面最下面是文字,而这些文字也是RecyclerView的子项,怎么实现呢?只需要增加相应子项的布局文件和对应ViewHolder、再改变一下adapter就行
添加新子项的xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:id="@+id/tv_item" android:gravity="center" android:layout_gravity="center" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout>
添加新子项的viewHolder
public class MyViewHolderForText extends RecyclerView.ViewHolder { @BindView(R.id.tv_item) TextView textView; public MyViewHolderForText(View itemView) { super(itemView); ButterKnife.bind(this,itemView); } public TextView getTextView() { return textView; } }
更改adapter
添加子项类型枚举
因为有多种子项,为了鉴别,我们最好定义一种枚举,来区分子项类型
private enum viewType { TYPE_TEXT, TYPE_IMAGE; }
覆写getItemViewType方法
为了获取子项类型,我们需要覆写父类种getItemViewType()方法
@Override public int getItemViewType(int position) { if (position == 0 || position == urls.size() + 1) { return viewType.TYPE_TEXT.ordinal(); } return viewType.TYPE_IMAGE.ordinal(); }
修改getItemCount方法
这时我们要多显示两项,所以返回值要+2
@Override public int getItemCount() { return urls.size() + 2; }
修改onCreateViewHolder方法和onBindViewHolder方法
根据viewType来获取不同的viewHolder或执行不同逻辑
@Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == AdapterForRecyclerVIew.viewType.TYPE_IMAGE.ordinal()) { CardView layout = (CardView) LayoutInflater.from(context).inflate(R.layout.item_recycler, parent, false); return new MyViewHolderForImage(layout); } else { LinearLayout layout = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.text_item_recycler, parent, false); return new MyViewHolderForText(layout); } } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if (holder.getItemViewType() == viewType.TYPE_IMAGE.ordinal()) { MyViewHolderForImage holderForImage = (MyViewHolderForImage) holder; Picasso.with(context).load(urls.get(position - 1)).into(holderForImage.getImageView()); // 因为第一个是textView,所以url的索引是position-1 holderForImage.getTextView().setText(descriptions.get(position - 1)); } else { ((MyViewHolderForText) holder).getTextView().setText("宋泽嶒"); } }
这样,就大功告成了
结语
RecyclerView可以帮助我们更好地实现mvp,也是一种性能更好的listView