在使用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展现数据时调用。
参考文章: