RecyclerView(一)最簡單的recyclerview
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的佈局如下
MainActivity程式碼<?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>
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
listview支援多種item,recyclerview同樣支援,寫起來很簡單 寫2個viewholder,然後把資料新增進去,就OK了,效果如下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));
}
}
item_image<?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就可以 了