一、RecyclerView是何物
简单的说RecyclerView就是ListView的升级版,它有一个优点就是具有高可定制性和灵活性。可以轻松实现ListView实现不了的样式和功能:
- 设置布局管理器以控制Item的布局方式,横向、竖向以及瀑布流方式。
- 可设置Item操作的动画(删除或者添加等)
- 可设置Item的间隔样式(可绘制)
当然,万事万物都不可能完美的,它也有很明显的缺点:它的每个Item的点击和长按事件,需要用户自己去实现
刚开始不能很好的理解,不过不用急,在后面的详细讲解中,就能够慢慢的感受到它的优点以及它的缺点。
二、RecyclerView的使用
1.使用前的准备
首先使用RecyclerView需要引入它的依赖库,在app下的build.gradle里添加依赖
dependencies {
compile 'com.android.support:recyclerview-v7:26.0.0-alpha1'
}
2.先看一下Demo的结构和要实现的效果图
(1)Demo结构
image.png可以看出整个Demo很简单,就三个部分:MainActivity、NewsAdapter、News
(2)最终效果
1.jpg image.png
实现的效果也可以很明显的看出来需要实现三个部分:头、身体和尾部
3.首先我们需要为MainActiv设置一个布局,很简单只需要使用Recyclerview这个控件
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.recycleviewdemo.MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
</RelativeLayout>
4.接下来,我们需要写MainActivity里的东西了。在MainActivity做以下事情:
(1)获取RecyclerView的控件实例
(2)设置布局管理器来控制Item的布局方式,横向、竖向、网格布局以及瀑布流方式(优点之一)
(3)准备要展示在Item的数据
(4)给RecyclerView设置设配器
MainActivity
public class MainActivity extends AppCompatActivity {
RecyclerView mRecyclerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//1.获取控件
mRecyclerView=(RecyclerView) findViewById(R.id.recycler_view);
//2.设置布局方式
mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); //线性布局
//mRecyclerView.setLayoutManager(new GridLayoutManager(this,2)); //网格布局
mRecyclerView.setHasFixedSize(true);
//3.准备数据
List<News> newsList=new ArrayList<>();
News news;
for(int i=1;i<=99;i++){
news=new News();
news.setNewsTitle("以习近平同志为核心的党中央坚定不移推进全面深化改革"+i);
news.setNewsSource("新华网"+i);
news.setPublishTime("2018-8-6");
newsList.add(news);
}
//3.设置适配器
NewsAdapter newsAdapter=new NewsAdapter(newsList);
mRecyclerView.setAdapter(newsAdapter);
}
}
从上面我们看到最后一步需要设置适配器,设置设配器是这里最重要且复杂的部分,可以先这么理解:
RecyclerView就是商场里一整排的存储柜。适配器做的就是把自己要放的一大堆东西按照自己想放的方式先摆好,之后设置适配器就是将我们摆好的东西放入到一个个存储柜中。
5.准备自己设配器(准备自己的摆放物品方式)
我的设配器分为三个部分,头部、身体和尾部
下面是三个布局,
[图片上传失败...(image-feaf81-1533699724135)]
(1)分别是头:这里为了方便理解只设置了一张图片,你可以换成更丰富的内容,比如在这里弄一个轮播图之类就比较迪奥一点了~~
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/news_image"
android:layout_width="match_parent"
android:layout_height="150dp" />
</LinearLayout>
(2)身体:新闻内容(包括新闻标题、出处和时间)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="80dp">
<TextView
android:id="@+id/news_title"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="以习近平同志为核心的党中央坚定不移推进全面深化改革"
android:textSize="20sp"
android:layout_marginTop="3dp"
/>
<TextView
android:id="@+id/news_source"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:textColor="#b72a65"
android:text="新闻网"
/>
<TextView
android:id="@+id/news_publishtime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:text="12小时前"/>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="6dp"
android:layout_marginTop="2dp"
android:background="#000000"
android:layout_below="@+id/news_publishtime"
/>
</RelativeLayout>
(3)尾部:新闻到头的提示
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="30dp">
<TextView
android:id="@+id/tv_footer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15sp"
android:layout_centerInParent="true"
/>
</RelativeLayout>
6.开始写自己的适配器
(1)写一个类继承RecyclerView.Adapter<RecyclerView.ViewHolder>,这里尖括号里的东西表示泛型,如果不知道泛型的可以去补一下了,毕竟这个是java里很重要的基础知识点了哈哈。
继承完之后必须要重写父类的几个方法onCreateViewHolder()、onBindViewHolder()、getItemCount()
这里提醒一下:getItemViewType()的目的是区分什么时候该展示哪一种item。
如果你只有一种类型的item,比如只有新闻item,那么getItemViewType()就不需要重写了。
public class NewsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
List<News> newsList=new ArrayList<>(); //数据集合
private static final int HEADER_TYPE=0; //头
private static final int FOOTER_TYPE=-1; //尾
public NewsAdapter(List<News> newsList){
this.newsList=newsList; //数据初始化
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
if (viewType==HEADER_TYPE){
return createHeaderViewHolder(viewGroup);
}else if (viewType==FOOTER_TYPE){
return createFooterViewHolder(viewGroup);
}
else {
return createNewsViewHolder(viewGroup);
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof NewsViewHolder){
//5.把新闻的数据绑定到view中
bindViewForNews(holder,position);
}else if(holder instanceof HeaderViewHolder){
//绑定头
bindViewForHeader(holder);
}else if (holder instanceof FooterViewHolder){
//绑定尾
bindViewForFooter(holder);
}
}
@Override
public int getItemCount() {
//return newsList.size();
return newsList==null?1:newsList.size()+2; //控制position的数目,因为加了一头一尾,所以这里的总数是我的新闻数+2
}
@Override
public int getItemViewType(int position) {
if(HEADER_TYPE==position){
return 0;
}else if(newsList.size()>=position){
return 1;
} else {
return -1;
}
}
}
(2)在获取到展现的类型item时,比如当position==0,也就是我们看到的第一个item时,它返回0,那么之后就会在onCreateViewHolder判断viewType的值来创建需要展现的ViewHolder。这里为了程序的可读性,我把它单独写了一个方法createHeaderViewHolder()。
ViewHolder就是一个容器,它放你要展示的item里面的控件内容。比如这里的头部分我们只有一个ImageView的控件
获取控件的时候要注意:必须通过itemViewitemView.findViewById(),不能直接findViewById()
private HeaderViewHolder createHeaderViewHolder(ViewGroup viewGroup) {
View headerView=LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.news_header_layout,viewGroup,false);
return new HeaderViewHolder(headerView);
}
//初始化头view
static class HeaderViewHolder extends RecyclerView.ViewHolder{
ImageView iv_newsImage;
public HeaderViewHolder(View itemView) {
super(itemView);
iv_newsImage=(ImageView) itemView.findViewById(R.id.news_image);
}
}
(3)创建完头item后(获取到控件实例),这里要为item填充数据了,这就轮到onBindViewHolder上场了
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof NewsViewHolder){
//5.把新闻的数据绑定到view中
bindViewForNews(holder,position);
}else if(holder instanceof HeaderViewHolder){
//绑定头
bindViewForHeader(holder);
}else if (holder instanceof FooterViewHolder){
bindViewForFooter(holder);
}
}
private void bindViewForHeader(RecyclerView.ViewHolder holder) {
HeaderViewHolder headerViewHolder=(HeaderViewHolder) holder;
headerViewHolder.iv_newsImage.setScaleType(ImageView.ScaleType.FIT_XY); //自由拉伸,填充满整个控件空间
headerViewHolder.iv_newsImage.setImageResource(R.drawable.news_header);
}
先判断是哪个viewholder,然后为其绑定相应的数据。比如这里的imageView控件我就设置了一个图片资源
headerViewHolder.iv_newsImage.setImageResource(R.drawable.news_header);
并设置图片自由拉伸,填充满整个控件空间
headerViewHolder.iv_newsImage.setImageResource(R.drawable.news_header);
(4)通过以上步骤,头部分就实现了,身体部分和尾部都是一样的过程。把整个部分的代码贴出来,说一下其中需要注意的点。
public class NewsAdapter extends RecyclerView.Adapter <RecyclerView.ViewHolder>{
List<News> newsList=new ArrayList<>(); //数据集合
private static final int HEADER_TYPE=0; //头
private static final int FOOTER_TYPE=-1; //尾
public NewsAdapter(List<News> newsList){
this.newsList=newsList; //数据初始化
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
if (viewType==HEADER_TYPE){
return createHeaderViewHolder(viewGroup);
}else if (viewType==FOOTER_TYPE){
return createFooterViewHolder(viewGroup);
}
else {
return createNewsViewHolder(viewGroup);
}
}
private RecyclerView.ViewHolder createFooterViewHolder(ViewGroup viewGroup) {
View footerView=LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.news_footer_layout,viewGroup,false);
return new FooterViewHolder(footerView);
}
private NewsViewHolder createNewsViewHolder(ViewGroup viewGroup) {
//2.实例化子布局
View itemView= LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.news_item_layout,viewGroup,false);
//3.获得一个ViewHolder实例
return new NewsViewHolder(itemView);
}
private HeaderViewHolder createHeaderViewHolder(ViewGroup viewGroup) {
View headerView=LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.news_header_layout,viewGroup,false);
return new HeaderViewHolder(headerView);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof NewsViewHolder){
//5.把新闻的数据绑定到view中
bindViewForNews(holder,position);
}else if(holder instanceof HeaderViewHolder){
//绑定头
bindViewForHeader(holder);
}else if (holder instanceof FooterViewHolder){
bindViewForFooter(holder);
}
}
private void bindViewForFooter(RecyclerView.ViewHolder holder) {
FooterViewHolder footerViewHolder=(FooterViewHolder)holder;
footerViewHolder.tv_footer.setText("啊哈哈,这不是无底洞......");
}
private void bindViewForHeader(RecyclerView.ViewHolder holder) {
HeaderViewHolder headerViewHolder=(HeaderViewHolder) holder;
headerViewHolder.iv_newsImage.setScaleType(ImageView.ScaleType.FIT_XY);
headerViewHolder.iv_newsImage.setImageResource(R.drawable.news_header);
}
private void bindViewForNews(RecyclerView.ViewHolder holder, int position) {
NewsViewHolder newsViewHolder=(NewsViewHolder) holder;
final News news=getItem(position);
//将数据填充进去
newsViewHolder.tv_newsTitle.setText(news.getNewsTitle());
newsViewHolder.tv_newsSource.setText(news.getNewsSource());
newsViewHolder.tv_newsPublishTime.setText(news.getPublishTime());
//点击事件
newsViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
}
@Override
public int getItemCount() {
//return newsList.size();
return newsList==null?2:newsList.size()+2; //控制position的数目,因为加了一头一尾,所以这里的总数是我的新闻数+2
}
protected News getItem(int position){
//6.获取每条新闻的内容
return newsList.get(position-1);
}
//1.初始化自己的ViewHolder
static class NewsViewHolder extends RecyclerView.ViewHolder {
public TextView tv_newsTitle;
public TextView tv_newsSource;
public TextView tv_newsPublishTime;
public NewsViewHolder(View itemView) {
super(itemView);
//获取子布局的控件实例
tv_newsTitle=(TextView) itemView.findViewById(R.id.news_title);
tv_newsSource=(TextView) itemView.findViewById(R.id.news_source);
tv_newsPublishTime=(TextView) itemView.findViewById(R.id.news_publishtime);
}
}
//初始化头view
static class HeaderViewHolder extends RecyclerView.ViewHolder{
ImageView iv_newsImage;
public HeaderViewHolder(View itemView) {
super(itemView);
iv_newsImage=(ImageView) itemView.findViewById(R.id.news_image);
}
}
//初始化尾view
static class FooterViewHolder extends RecyclerView.ViewHolder{
TextView tv_footer;
public FooterViewHolder(View itemView) {
super(itemView);
tv_footer=(TextView) itemView.findViewById(R.id.tv_footer);
}
}
@Override
public int getItemViewType(int position) {
if(HEADER_TYPE==position){
return 0;
}else if(newsList.size()>=position){
return 1;
} else {
return -1;
}
}
}
上面提到需要注意的点就是:item的数目问题。
一个是总的item数目:如果新闻数目为null,那么就只有一头一尾也就是两个,如果有新闻,那么就是它的数目加2(一头一尾)
另外一个就是获取新闻数据的时候,由于加了一个头,第一条新闻的position就变成了1,但是我们要获取新闻集合的第0条内容,所以需要get(position-1)
protected News getItem(int position){
//6.获取每条新闻的内容
return newsList.get(position-1);
}
到这里RecyclerView的入门篇就结束了。内容写的有问题或对我的写作方式有建议的,欢迎各位观众老爷批评指正,啊哈哈~~
后面还准备写它的进阶篇和源码解析篇。
网友评论