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