Android RecyclerView

Posted chenxibobo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android RecyclerView相关的知识,希望对你有一定的参考价值。

一、RechclerView简介。

RecyclerView比listview更先进更灵活,对于很多的视图它就是一个容器,可以有效的重用和滚动。

1.可以通过设置LayoutManager可以实现Listview和横向Listview,GridView,横向Gridview和瀑布流等效果。

2.可以通过addItemDecoration添加Item分割线。

3.可以通过setItemAnimator()设置Item的增加和移除动画。

二、RecyclerView相关类介绍。

1、RecyclerView.Adapter:负责托管数据集,为每一项Item创建布局并绑定数据。

2、RecyclerView.ItemDecoration,给Item添加分割线。需要继承该类自定义一个类。

3、RecyclerView.ItemAnimator负责处理Item增加或删除时的动画效果,系统提供了一个默认的动画类DefaultItemAnimator()。

4、RecyclerView.ViewHolder:负责承载Item视图的子布局。

class MyViewHolder extends ViewHolder {
    // Item子布局上的一个元素
    TextView textView;

    public MyViewHolder(View itemView) {
        super(itemView);
        // 关联引动该元素 ,在item.xml中findView,注意不要忘写(itemview.)
        textView = (TextView) itemView.findViewById(R.id.textView);
    }
}

5、RecyclerView.LayoutManager:布局管理器,负责Item视图的布局的显示管理。分为:

(1)、LinearLayoutManager,类似Listview

他有两个构造函数: 
LinearLayoutManager(Context context)//默认方向为垂直方向。

LinearLayoutManager(Context context, int orientation, boolean reverseLayout) 
//其中第二个参数orientation表示布局的方向,可以取两个值:垂直和水平。分别是纵向Listview的效果和横向Listview的效果。第三个参数reverseLayout表示是否反向布局(即纵向Listview上下颠倒),若为true,纵向Listview默认在最底部,而且第一项在最低下。(若是不明白的话,自己写一个Demo看看)

(2)、GridLayoutManager,类似GridView

三种构造函数: 
GridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) //可以直接在XMl中设置RecyclerView 属性”layoutManager”.

GridLayoutManager(Context context, int spanCount) //spanCount为列数,默认方向vertical

GridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) 
//spanCount为列数,orientation为布局方向,reverseLayout决定布局是否反向。

(2)、StaggeredGridLayoutManager流式布局

两个构造函数: 
StaggeredGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)

StaggeredGridLayoutManager(int spanCount, int orientation) //spanCount为列数,orientation为布局方向

三、基本使用

1、导入android-support-v7-recyclerview

2、Activity布局文件

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.raphets.recyclerview.MainActivity" >

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>

3、Item的布局文件

<?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="50dp"
    android:background="#0099ff"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/textView"
        android:layout_width="120dp"
        android:layout_height="match_parent"
        android:layout_gravity="center"
        android:gravity="center" />

</LinearLayout>

4、Activity类,RecyclerView的主要代码

public class MainActivity extends Activity {
    private RecyclerView mRecyclerView;
    private List<String> mDatas;
    private MyRecylerViewAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 初始化数据
        initData();
        mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
        adapter = new MyRecylerViewAdapter(this, mDatas);
        //绑定适配器
        mRecyclerView.setAdapter(adapter);
        // 给每个item添加分割线
        mRecyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST));
        // 设置item增加和移除的动画
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());
        // 设置布局管理器
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
        mRecyclerView.setLayoutManager(linearLayoutManager);

    }

    /*
     * 初始化数据
     */
    private void initData() {
        mDatas = new ArrayList<String>();
        for (int i = 0; i <= 50; i++) {
            mDatas.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();
        switch (id) {
        case R.id.listview:
            mRecyclerView.setLayoutManager(new LinearLayoutManager(this, OrientationHelper.VERTICAL, false));
            break;
        case R.id.gridView:
            mRecyclerView.setLayoutManager(new GridLayoutManager(this, 3));
            break;
        case R.id.horizonalListview:
            mRecyclerView.setLayoutManager(new LinearLayoutManager(this, OrientationHelper.HORIZONTAL, false));
            break;
        case R.id.horizonalGridview:
            mRecyclerView.setLayoutManager(new GridLayoutManager(this, 5, OrientationHelper.HORIZONTAL, false));
            break;

        case R.id.add:
            adapter.notifyItemInserted(1);
            break;
        case R.id.delete:
            adapter.notifyItemRemoved(1);
            break;
        default:
            break;
        }

        return super.onOptionsItemSelected(item);
    }
}

5、适配器Adapter

public class MyRecylerViewAdapter extends Adapter<MyViewHolder> {
    private Context mContext;
    private List<String> mDatas;

    public MyRecylerViewAdapter(Context context, List<String> datas) {
        this.mContext = context;
        this.mDatas = datas;
    }

    @Override
    public int getItemCount() {
        // TODO Auto-generated method stub
        return mDatas.size();
    }


    @Override
    public void onBindViewHolder(MyViewHolder arg0, int arg1) {
        arg0.textView.setText(mDatas.get(arg1));

    }

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

}

class MyViewHolder extends ViewHolder {
    // Item子布局上的一个元素
    TextView textView;

    public MyViewHolder(View itemView) {
        super(itemView);
        // 关联引动该元素 ,在item.xml中findView,注意不要忘写(itemview.)
        textView = (TextView) itemView.findViewById(R.id.textView);
    }
}

下拉后从上端刷新

(在demo中是名为PullDownRefresh的module) 
下拉从上端刷新,这个比较简单。在布局文件里,用SwipeRefreshLayout把RecyclerView包在里面,然后再在java代码里面写下拉的响应事件就好了。下面直接写代码: 
1.布局文件,把RecyclerView放在SwipeRefreshLayout里:

<android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/srl"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/rv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

2.java代码:

  //列表
        recyclerView= (RecyclerView) findViewById(R.id.rv);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        //添加数据
        list=new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add("第"+i+"项");
        }
        adapter=new ItemAdapter(list,this);
        recyclerView.setAdapter(adapter);

        //下拉加载控件
        swipeRefreshLayout= (SwipeRefreshLayout) findViewById(R.id.srl);
        swipeRefreshLayout.setColorSchemeColors(Color.BLUE);//设置旋转圈的颜色
        //下拉监听
        swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                list.add(0,"下拉加载出现的:"+i++);
                adapter.notifyDataSetChanged();
                swipeRefreshLayout.setRefreshing(false);//设置成true的话,下拉过后就会一直在那里转
            }
        });

(在demo中是名为PullUpRefresh的module)

3.上拉从下端刷新

设置一个监听器,在上拉到开始显示最下面一项时,加载更多项。 
监听器EndLessOnScrollListener代码:


public abstract class EndLessOnScrollListener extends RecyclerView.OnScrollListener {

    private static final String TAG = "EndLessOnScrollListener";

    LinearLayoutManager linearLayoutManager;

    //当前所在页
    private int currentPage=0;

    //已经加载出来的item数
    private int totalItemCount=0;

    //用来存储上一个totalItemCount
    private int previousTotal=0;

    //屏幕可见的item数量
    private int visibleItemCount;

    //屏幕可见第一个Item的位置
    private int firstVisibleItem;

    //是否上拉数据
    private boolean loading=true;

    public EndLessOnScrollListener(LinearLayoutManager linearLayoutManager) {
        this.linearLayoutManager = linearLayoutManager;
    }

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);

        visibleItemCount=recyclerView.getChildCount();
        totalItemCount=linearLayoutManager.getItemCount();
        firstVisibleItem=linearLayoutManager.findFirstVisibleItemPosition();
//去掉loading也可以,但是性能会下降,在每次滑动时都会判断,所以的加上
        if(loading){
            Log.d(TAG, "firstVisibleItem: " + firstVisibleItem);
            Log.d(TAG, "totalItemCount:" + totalItemCount);
            Log.d(TAG, "visibleItemCount:" + visibleItemCount);
            Log.d(TAG, "currentPage:" + currentPage);
            if(totalItemCount>previousTotal){
                //说明数据项已经加载结束
                loading=false;
                previousTotal=totalItemCount;
            }
        }
        //实际效果是滑动到已加载页最后一项可见的瞬间,添加下一页
        if(!loading&&totalItemCount-visibleItemCount<=firstVisibleItem){
            currentPage++;
            onLoadMore(currentPage);
            loading=true;
        }

    }

    /**
     * 提供一个抽闲方法,在Activity中监听到这个EndLessOnScrollListener
     * 并且实现这个方法
     * 这个方法在可见的页的最后一项,可见时调用
     * currentPage是加载到的页面编号
     */
    public abstract void onLoadMore(int currentPage);

给recyclerview添加上拉监听事件即可,这里我让它每次加5项:

  recyclerView.addOnScrollListener(new EndLessOnScrollListener(linearLayoutManager) {
            @Override
            public void onLoadMore(int currentPage) {
                for (int i = count; i < 5+count; i++) {
                    list.add("上拉加载"+i);
                }
                adapter.notifyDataSetChanged();
                count+=5;
            }
        });

4.添加尾部首部分别添加footer和Head

(在demo中是名为HeaderAndFooter的module) 
实现方法,主要是在适配器里实现。要在适配器必须写的方法里面和getItemViewType()方法里,考虑可能最前和最后一项分别是header和footer情况。

1.temAdapter里的代码


    private static final int TYPE_HEADER = 0;
    private static final int TYPE_FOOTER = 1;
    private static final int TYPE_NORMAL = 2;


    public ItemAdapter(List<String> list, Context context) {
        this.list = list;
        this.context = context;
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (headerView != null && viewType == TYPE_HEADER) {
            return new MyViewHolder(headerView);
        }
        if (footerView != null && viewType == TYPE_FOOTER) {
            return new MyViewHolder(footerView);
        }

        MyViewHolder holder = new MyViewHolder(LayoutInflater.from(context).
                inflate(R.layout.item_layout, parent, false));
        return holder;
    }

    @Override
    public void onBindViewHolder(MyViewHolder holder, final int position) {
        if (getItemViewType(position) == TYPE_NORMAL) {
            holder.tv.setText(list.get(position - 1));
            return;
        } else if (getItemViewType(position) == TYPE_HEADER) {
            return;
        } else
            return;
    }

    /**
     * 重写这个方法,很重要,是加入Header和Footer的关键,我们通过判断item的类型,从而绑定不同的view
     */
    @Override
    public int getItemViewType(int position) {
        if (headerView == null && footerView == null) {
            return TYPE_NORMAL;
        }
        if (position == 0) {
            //第一个item应该加载Header
            return TYPE_HEADER;
        }
        if (position == getItemCount() - 1) {
            //最后一个,应该加载Footer
            return TYPE_FOOTER;
        }
        return TYPE_NORMAL;
    }

    @Override
    public int getItemCount() {
        if (headerView == null && footerView == null) {
            return list.size();
        } else if (headerView == null && footerView != null) {
            return list.size() + 1;
        } else if (headerView != null && footerView == null) {
            return list.size() + 1;
        } else {
            return list.size() + 2;
        }
    }

    public View getHeaderView() {
        return headerView;
    }

    public void setHeaderView(View headerView) {
        this.headerView=headerView;
        notifyItemInserted(0);
    }

    public View getFooterView() {
        return footerView;
    }

    public void setFooterView(View footerView) {
        this.footerView=footerView;
        notifyItemInserted(getItemCount()-1);
    }

    class MyViewHolder extends RecyclerView.ViewHolder {

        TextView tv;

        public MyViewHolder(View itemView) {
            super(itemView);
            tv = itemView.findViewById(R.id.tv);
        }
    }

活动里面的代码:

RecyclerView recyclerView;
    ItemAdapter adapter;
    List<String>list;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }
 private void initView() {
        list= new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add("第"+i+"项");
        }
        adapter=new ItemAdapter(list,this);

        recyclerView= (RecyclerView) findViewById(R.id.rv);
        recyclerView.setAdapter(adapter);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        //注意,以下两个方法必须在setAdapter()之后调用,否则长和宽会变成wrap_content
        addHeader();
        addFooter();

    }

    private void addHeader(){
        View header= LayoutInflater.from(this).inflate(R.layout.header_layout,recyclerView,false);
        adapter.setHeaderView(header);
    }

    private  void addFooter(){
        View footer= LayoutInflater.from(this).inflate(R.layout.footer_layout,recyclerView,false);
        adapter.setFooterView(footer);
    }










以上是关于Android RecyclerView的主要内容,如果未能解决你的问题,请参考以下文章

RecyclerView holder中的Android Google Maps动态片段

Android:以编程方式在片段中添加多个 RecyclerView

RecyclerView 内容未使用片段父级的全宽

我们如何在 android 的 RecyclerView 片段中使用 bottomSheet?

多个 RecyclerView 不在同一个片段中工作

android studio中片段内的RecyclerView使我的应用程序崩溃