1. 程式人生 > >Android RecyclerView

Android RecyclerView

attrs 垂直 urn type 點擊 bind -c 簡單 osi

一、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