RecyclerView相比listview有很多灵活性,可以完全取代listview。
RecyclerView is a more advanced and flexible version of ListView. This widget is a container for large sets of views that can be recycled and scrolled very efficiently.
最简单例子 Project: RecycleViewTest
先来看个最简单的例子,首先在项目的依赖库内加入recycleview的库。
activity的layout如下
<?xml version="1.0" encoding="utf-8"?>
<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"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.fish.recycleviewtest.MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycle_view"
android:layout_width="match_parent"
android:layout_height="match_parent"></android.support.v7.widget.RecyclerView>
</RelativeLayout>
recycleview的item的布局如下
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:background="#44ff0000"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/id_num"
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:text="1" />
</FrameLayout>
MainActivity代码
package com.fish.recycleviewtest;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private List<String> mDatas;
private RecyclerView recyclerView;
private FishAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = (RecyclerView) findViewById(R.id.recycle_view);
initData();
initRecycleView();
}
private void initRecycleView() {
adapter = new FishAdapter(this);
//必须指定adaoter
recyclerView.setAdapter(adapter);
//必须指定layoutmanager
recyclerView.setLayoutManager(new LinearLayoutManager(this));
adapter.setData(mDatas);
}
protected void initData() {
mDatas = new ArrayList<String>();
for (int i = 'A'; i < 'z'; i++) {
mDatas.add("" + (char) i);
}
}
}
FishAdapter代码
package com.fish.recycleviewtest;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import java.util.List;
/**
* Created by fish on 16/6/4.
*/
public class FishAdapter extends RecyclerView.Adapter<FishViewHolder> {
private List<String> data;
private LayoutInflater inflater;
public FishAdapter(Context context) {
inflater = LayoutInflater.from(context);
}
@Override
public FishViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LogUtil.fish("onCreateViewHolder");
FishViewHolder holder = new FishViewHolder(inflater.inflate(
R.layout.item, parent, false));
return holder;
}
@Override
public void onBindViewHolder(FishViewHolder holder, int position) {
LogUtil.fish("onBindViewHolder " + position);
holder.tv.setText(data.get(position));
}
@Override
public int getItemCount() {
return data.size();
}
public void setData(List<String> pDatas) {
data = pDatas;
}
}
FishViewHolder代码
package com.fish.recycleviewtest;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.TextView;
public class FishViewHolder extends RecyclerView.ViewHolder {
TextView tv;
public FishViewHolder(View view) {
super(view);
tv = (TextView) view.findViewById(R.id.id_num);
}
}
最简单的recycleview就这么诞生了,非常简单。效果如下
看看MainActivity的initRecycleView,为recycleview依次设置了adapter,layoutmanager,recycleview必须要设置adapter和layoutManager。adapter用来管理数据,layoutmanager用来管理各个item view的布局,比如布局成listview或者gridview或者瀑布流。
adapter内部的onCreateViewHolder负责为某个item创建viewholder(和listview里面的viewholder概念一样),在使用listview的时候我们经常还要getTag,setTag,这里系统帮我们做了,我们只要往里面放一个view,就得到了一个viewholder。
FishViewHolder holder = new FishViewHolder(inflater.inflate(
R.layout.item, parent, false));
如果没有设置adapter,会怎么样呢?
可以看下这个方法RecyclerView#dispatchLayout,这个方法会在layout过程中调用,如果发现没有adapter会报错No adapter attached; skipping layout
如果发现没有layoutManager会报错 No layout manager attached; skipping layout
void dispatchLayout() {
if (mAdapter == null) {
Log.e(TAG, "No adapter attached; skipping layout");
// leave the state in START
return;
}
if (mLayout == null) {
Log.e(TAG, "No layout manager attached; skipping layout");
// leave the state in START
return;
}
mState.mIsMeasuring = false;
onEnterLayoutOrScroll();
if (mState.mLayoutStep == State.STEP_START) {
dispatchLayoutStep1();
mLayout.setExactMeasureSpecsFrom(this);
dispatchLayoutStep2();
} else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth() ||
mLayout.getHeight() != getHeight()) {
// First 2 steps are done in onMeasure but looks like we have to run again due to
// changed size.
mLayout.setExactMeasureSpecsFrom(this);
dispatchLayoutStep2();
} else {
// always make sure we sync them (to ensure mode is exact)
mLayout.setExactMeasureSpecsFrom(this);
}
dispatchLayoutStep3();
onExitLayoutOrScroll();
}
TO BE SMART
上边的代码虽然没问题,但是我们完全可以写的更好一点,比如把和viewholder相关的东西都放到Viewholder里面去,而不是在adapter内部处理。再比如,把recycleview的设置layoutmanager放到adapter内部(onAttachedToRecyclerView方法)去。
修改之后的代码如下所示
adapter的代码
package com.fish.recycleviewtest;
import android.content.Context;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import java.util.List;
/**
* Created by fish on 16/6/4.
*/
public class FishAdapter extends RecyclerView.Adapter<FishViewHolder> {
private List<String> data;
public FishAdapter() {
}
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
//必须指定layoutmanager
recyclerView.setLayoutManager(new LinearLayoutManager(recyclerView.getContext()));
}
@Override
public FishViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LogUtil.fish("onCreateViewHolder");
FishViewHolder holder = new FishViewHolder(parent);
return holder;
}
@Override
public void onBindViewHolder(FishViewHolder holder, int position) {
LogUtil.fish("onBindViewHolder " + position);
holder.bind(data.get(position));
}
@Override
public int getItemCount() {
return data.size();
}
public void setData(List<String> pDatas) {
data = pDatas;
}
}
viewholder的代码
package com.fish.recycleviewtest;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class FishViewHolder extends RecyclerView.ViewHolder {
TextView tv;
public FishViewHolder(ViewGroup parent) {
super(LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false));
tv = (TextView) itemView.findViewById(R.id.id_num);
}
public void bind(String s){
tv.setText(s);
}
}
这样看起来思路更清晰一点, FishViewHolder负责所有和viewholder相关的操作,主要是一个创建一个bind,而adapter不直接操作viewholder。和recycleview初始化相关的操作放入adapter的onAttachedToRecyclerView内部,而不是放在activity里面
多种item project:FishRecycleViewMultiType
package com.fish.fishrecycleviewmultitype;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;
/**
* Created by fish on 16/6/4.
*/
public class MyAdapter extends RecyclerView.Adapter<FishViewHolder> {
private List<FishData> data = new ArrayList<>();
public static final int TYPE_TEXT = 1;
public static final int TYPE_IMAGE = 2;
public MyAdapter() {
}
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
//必须指定layoutmanager
recyclerView.setLayoutManager(new LinearLayoutManager(recyclerView.getContext()));
}
@Override
public FishViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LogUtil.fish("onCreateViewHolder");
switch (viewType) {
case TYPE_TEXT:
return new TextViewHolder(parent);
case TYPE_IMAGE:
return new ImageViewHolder(parent);
}
return null;
}
@Override
public void onBindViewHolder(FishViewHolder holder, int position) {
holder.bind(data.get(position).data);
}
public void addData(int type, Object o) {
data.add(new FishData(type, o));
}
@Override
public int getItemCount() {
return data.size();
}
@Override
public int getItemViewType(int position) {
return data.get(position).type;
}
}
package com.fish.fishrecycleviewmultitype;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.TextView;
public class TextViewHolder extends FishViewHolder {
TextView tv;
public TextViewHolder(ViewGroup parent) {
super(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_text, parent, false));
tv = (TextView) itemView.findViewById(R.id.id_num);
}
@Override
protected void bind(Object object) {
String s = (String) object;
tv.setText(s);
}
}
package com.fish.fishrecycleviewmultitype;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
public class ImageViewHolder extends FishViewHolder {
ImageView tv;
Context context;
public ImageViewHolder(ViewGroup parent) {
super(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_image, parent, false));
context = parent.getContext();
tv = (ImageView) itemView.findViewById(R.id.image);
}
@Override
protected void bind(Object object) {
int drawableId = (int) object;
tv.setImageDrawable(context.getDrawable(drawableId));
}
}
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#44ff77">
<ImageView
android:id="@+id/image"
android:layout_width="200dp"
android:layout_height="200dp"
android:gravity="center"
android:text="1" />
</FrameLayout>
item_text
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:background="#44ff0000"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/id_num"
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:text="1" />
</FrameLayout>
以后如果需要更多种类的布局,就再添加viewholder就可以 了