在使用android提供的組件以列表的格式顯示數據時,使用過ListView組件和RecyclerView組件。目前通常推薦使用RecyclerView,由於RecyclerView自己的緩存和效率比ListView高,且支持靈活的佈局方式,因此會被你們採用。相信你們在使用ListView時,若是要顯示的數據多,確定多會想到優化Adaper的getView()方法,下面給出一個例子:android
public class UsersAdapter extends ArrayAdapter<User> { private static class ViewHolder { TextView name; TextView home; } public UsersAdapter(Context context, ArrayList<User> users) { super(context, R.layout.item_user, users); } @Override public View getView(int position, View convertView, ViewGroup parent) { User user = getItem(position); ViewHolder viewHolder; if (convertView == null) { viewHolder = new ViewHolder(); LayoutInflater inflater = LayoutInflater.from(getContext()); convertView = inflater.inflate(R.layout.item_user, parent, false); viewHolder.name = (TextView) convertView.findViewById(R.id.tvName); viewHolder.home = (TextView)convertView.findViewById(R.id.tvHome); convertView.setTag(viewHolder); } else { viewHolder = (ViewHolder) convertView.getTag(); } viewHolder.name.setText(user.name); viewHolder.home.setText(user.hometown); return convertView; } }
在這邊,咱們在ListView須要用到的Adapter中定義了一個內部類ViewHolder,它存儲了咱們要加載的view的全部子view結構,若是這個view已經被加載過只是暫時被回收, 當須要再次展現的話咱們就不須要從新加載整個view,也不須要經過findViewById()來尋找要加載的view的子view,能夠直接找到這個view,將要展現的數據設置便可返回顯示。緩存
可是在RecyclerView中,咱們並不須要作這麼多,咱們先看一個RecyclerView的簡單使用步驟:app
定義一個RecyclerView的佈局文件以及要展現的item的佈局文件less
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v7.widget.RecyclerView android:id="@+id/t_classList" android:layout_width="match_parent" android:layout_height="match_parent"> </android.support.v7.widget.RecyclerView> </LinearLayout> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginStart="20dp" android:layout_marginLeft="20dp"> <TextView android:id="@+id/t_class_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:textSize="19sp" /> <View android:layout_width="match_parent" android:layout_height="0.5px" android:background="@color/whiteGray" android:layout_marginTop="6dp" android:layout_marginBottom="6dp" android:typeface="serif"/> </LinearLayout>
定義一個adapteride
public class ClassAdapter extends RecyclerView.Adapter<ClassAdapter.ClassViewHolder> { private List<ClassVO> classVOs; static class ClassViewHolder extends RecyclerView.ViewHolder { View classView; TextView className; public ClassViewHolder(View itemView) { super(itemView); classView = itemView; className = (TextView) itemView.findViewById(R.id.t_class_name); } } public ClassAdapter(List<ClassVO> classVOs){ this.classVOs = classVOs; } @Override public ClassViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fragement_class_item, null, false); final ClassViewHolder holder = new ClassViewHolder(view); holder.classView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { int position = holder.getAdapterPosition(); ClassVO classVO = classVOs.get(position); Toast.makeText(view.getContext(), "you click class "+classVO.getId(), Toast.LENGTH_SHORT).show(); } }); return holder; } @Override public void onBindViewHolder(ClassViewHolder holder, int position) { ClassVO classVO = classVOs.get(position); holder.className.setText(classVO.getName()); } @Override public int getItemCount() { return classVOs.size(); }
}佈局
設置RecyclerView的佈局和adapter優化
recyclerView = (RecyclerView) classListView.findViewById(R.id.t_classList); layoutManager = new LinearLayoutManager(this.getContext()); recyclerView.setLayoutManager(layoutManager); if (classVOs!=null){ adapter = new ClassAdapter(classVOs); } recyclerView.setAdapter(adapter);
既然咱們要說的是RecyclerView中的ViewHolder,可是咱們的使用步驟中並無單獨提出ViewHolder,由於在咱們上面說的使用步驟的第二步———「定義一個adapter」,就涉及到ViewHolder。
當咱們自定義一個adapter時,繼承了RecyclerView的一個內部類:ui
/** * Base class for an Adapter * * <p>Adapters provide a binding from an app-specific data set to views that are displayed * within a {@link RecyclerView}.</p> */ public static abstract class Adapter<VH extends ViewHolder> { private final AdapterDataObservable mObservable = new AdapterDataObservable(); private boolean mHasStableIds = false; /** * Called when RecyclerView needs a new {@link ViewHolder} of the given type to represent * an item. * <p> * This new ViewHolder should be constructed with a new View that can represent the items * of the given type. You can either create a new View manually or inflate it from an XML * layout file. * <p> * The new ViewHolder will be used to display items of the adapter using * {@link #onBindViewHolder(ViewHolder, int, List)}. Since it will be re-used to display * different items in the data set, it is a good idea to cache references to sub views of * the View to avoid unnecessary {@link View#findViewById(int)} calls. * * @param parent The ViewGroup into which the new View will be added after it is bound to * an adapter position. * @param viewType The view type of the new View. * * @return A new ViewHolder that holds a View of the given view type. * @see #getItemViewType(int) * @see #onBindViewHolder(ViewHolder, int) */ public abstract VH onCreateViewHolder(ViewGroup parent, int viewType); /** * Called by RecyclerView to display the data at the specified position. This method should * update the contents of the {@link ViewHolder#itemView} to reflect the item at the given * position. * <p> * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method * again if the position of the item changes in the data set unless the item itself is * invalidated or the new position cannot be determined. For this reason, you should only * use the <code>position</code> parameter while acquiring the related data item inside * this method and should not keep a copy of it. If you need the position of an item later * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will * have the updated adapter position. * * Override {@link #onBindViewHolder(ViewHolder, int, List)} instead if Adapter can * handle efficient partial bind. * * @param holder The ViewHolder which should be updated to represent the contents of the * item at the given position in the data set. * @param position The position of the item within the adapter's data set. */ public abstract void onBindViewHolder(VH holder, int position); /** * Called by RecyclerView to display the data at the specified position. This method * should update the contents of the {@link ViewHolder#itemView} to reflect the item at * the given position. * <p> * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method * again if the position of the item changes in the data set unless the item itself is * invalidated or the new position cannot be determined. For this reason, you should only * use the <code>position</code> parameter while acquiring the related data item inside * this method and should not keep a copy of it. If you need the position of an item later * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will * have the updated adapter position. * <p> * Partial bind vs full bind: * <p> * The payloads parameter is a merge list from {@link #notifyItemChanged(int, Object)} or * {@link #notifyItemRangeChanged(int, int, Object)}. If the payloads list is not empty, * the ViewHolder is currently bound to old data and Adapter may run an efficient partial * update using the payload info. If the payload is empty, Adapter must run a full bind. * Adapter should not assume that the payload passed in notify methods will be received by * onBindViewHolder(). For example when the view is not attached to the screen, the * payload in notifyItemChange() will be simply dropped. * * @param holder The ViewHolder which should be updated to represent the contents of the * item at the given position in the data set. * @param position The position of the item within the adapter's data set. * @param payloads A non-null list of merged payloads. Can be empty list if requires full * update. */
能夠看到,RecyclerView內部已經幫咱們定義好了ViewHolder抽象類,當咱們自定義Adapter時,須要定義好要繼承ViewHolder的類(Adapter<VH extends ViewHolder>)。this
ViewHolder的做用咱們在上文介紹使使用ListView時已經說到,簡而言之就是提升消息,下面咱們看一看源碼中對ViewHolder的介紹:idea
/** * A ViewHolder describes an item view and metadata about its place within the RecyclerView. * * <p>{@link Adapter} implementations should subclass ViewHolder and add fields for caching * potentially expensive {@link View#findViewById(int)} results.</p> * * <p>While {@link LayoutParams} belong to the {@link LayoutManager}, * {@link ViewHolder ViewHolders} belong to the adapter. Adapters should feel free to use * their own custom ViewHolder implementations to store data that makes binding view contents * easier. Implementations should assume that individual item views will hold strong references * to <code>ViewHolder</code> objects and that <code>RecyclerView</code> instances may hold * strong references to extra off-screen item views for caching purposes</p> */ public static abstract class ViewHolder { ...... }
能夠看出兩點重要的地方:
Adapter implementations should subclass ViewHolder and add fields for caching potentially expensive {@link View#findViewById(int)} results(adapter應當擁有ViewHolder的子類,而且ViewHolder內部應當存儲一些子view,避免時間代價很大的findViewById操做)
Adapters should feel free to use their own custom ViewHolder implementations to store data that makes binding view content easier
其RecyclerView內部定義的ViewHolder類包含不少複雜的屬性,內部使用場景也有不少,而咱們常常使用的也就是onCreateViewHolder()方法和onBindViewHolder()方法,onCreateViewHolder()方法在RecyclerView須要一個新類型。item的ViewHolder時調用來建立一個ViewHolder,而onBindViewHolder()方法則當RecyclerView須要在特定位置的item展現數據時調用。
參考文章: