标签(空格分隔): 安卓UI
一、RecyclerView的基本用法
1、设置布局
RecyclerView相对于ListView而言提供设置整个数据的展示布局,对应方法源码:
/**
* Set the {@link LayoutManager} that this RecyclerView will use.
*
* <p>In contrast to other adapter-backed views such as {@link android.widget.ListView}
* or {@link android.widget.GridView}, RecyclerView allows client code to provide custom
* layout arrangements for child views. These arrangements are controlled by the
* {@link LayoutManager}. A LayoutManager must be provided for RecyclerView to function.</p>
*
* <p>Several default strategies are provided for common uses such as lists and grids.</p>
*
* @param layout LayoutManager to use
*/
public void setLayoutManager(LayoutManager layout) {
......
}
目前实现RecyclerView.LayoutManager的LayoutManager有: LinerLayoutManager:内部元素以垂直或者水平列表方式展示Item; GridLayoutManager:以网格方式展示Item; StaggeredGridLayoutManager:以瀑布流方式展示Item; 因此可以看到RecyclerView比ListView固定的垂直布局要灵活的多;
2、设置动画和Item间隔
RecyclerView支持对于Item的特殊设定,比如支持item添加和移除的动画、比如支持设置item之间间隔样式;
/**
* Sets the {@link ItemAnimator} that will handle animations involving changes
* to the items in this RecyclerView. By default, RecyclerView instantiates and
* uses an instance of {@link DefaultItemAnimator}. Whether item animations are
* enabled for the RecyclerView depends on the ItemAnimator and whether
* the LayoutManager {@link LayoutManager#supportsPredictiveItemAnimations()
* supports item animations}.
*
* @param animator The ItemAnimator being set. If null, no animations will occur
* when changes occur to the items in this RecyclerView.
*/
public void setItemAnimator(ItemAnimator animator) {
if (mItemAnimator != null) {
mItemAnimator.endAnimations();
mItemAnimator.setListener(null);
}
mItemAnimator = animator;
if (mItemAnimator != null) {
mItemAnimator.setListener(mItemAnimatorListener);
}
}
/**
* Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
* affect both measurement and drawing of individual item views.
*
* <p>Item decorations are ordered. Decorations placed earlier in the list will
* be run/queried/drawn first for their effects on item views. Padding added to views
* will be nested; a padding added by an earlier decoration will mean further
* item decorations in the list will be asked to draw/pad within the previous decoration's
* given area.</p>
*
* @param decor Decoration to add
* @param index Position in the decoration chain to insert this decoration at. If this value
* is negative the decoration will be added at the end.
*/
public void addItemDecoration(ItemDecoration decor, int index) {
if (mLayout != null) {
mLayout.assertNotInLayoutOrScroll("Cannot add item decoration during a scroll or"
+ " layout");
}
if (mItemDecorations.isEmpty()) {
setWillNotDraw(false);
}
if (index < 0) {
mItemDecorations.add(decor);
} else {
mItemDecorations.add(index, decor);
}
markItemDecorInsetsDirty();
requestLayout();
}
3、设置适配器
RecyclerView本身就是用来展示集合数据源的View,适配器本质是一个支持范型的设计模式,即实现将不同的数据和展示(逻辑)做适配,因此适配器的关键在于提供一些方法来绑定数据和展示的特殊关系;每一个具体的使用者都必须自己重写这些方法,来表达如何匹配; RecyclerView.Adapter这个抽象类定义如下:
/**
* 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>
*
* @param <VH> A class that extends ViewHolder that will be used by the adapter.
*/
public abstract static class Adapter<VH extends ViewHolder> {
......
}
在RecyclerView.Adapter会需要用到RecyclerView.ViewHolder,ViewHolder有什么作用?参考Google官方说明:
/**
* 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 abstract static class ViewHolder {
......
}
ViewHolder实际上就是提供将每个Item的View和数据源Data关联起来的桥梁,ViewHolder内部会定义Item View的子元素,后续再根据Adapter提供的方法,将数据源的数据关联到ViewHolder的View子元素 如下使用:
public static class MyViewHolder extends RecyclerView.ViewHolder {
TextView show_tv;
public MyViewHolder(View itemView) {
super(itemView);
show_tv = (TextView) itemView.findViewById(R.id.item_show_tv);
}
}
Adapater提供了两个方法,一个用来表示每个Item对应的View展示,一个用来表示每个Item如何展示数据源的数据:
3.1、onCreateViewHolder方法(将每个Item关联到View展示对应的ViewHolder)
先参考官方定义: /**
- Called when RecyclerView needs a new {@link ViewHolder} of the given type to represent
- an item.
- 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.
- 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. */ public abstract VH onCreateViewHolder(ViewGroup parent, int viewType); 具体使用参考:
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// 每个Item展示的view
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.rv_item_view, parent, false);
// 持有展示View的viewholder,viewholder后续会和数据源对应
MyViewHolder viewHolder = new MyViewHolder(v);
return viewHolder;
}
onCreateViewHolder方法需要定义每个Item对应展示的View,同时将View映射到ViewHolder对象
3.2、onBindViewHolder方法(将每个Item的ViewHolder和数据源关联,每个Item要展示的数据逻辑)
先参考官方定义:
/**
* 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.
* 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.
*/
public abstract void onBindViewHolder(VH holder, int position);
官方文档已经做了说明,onBindViewHolder提供了将对应某个位置的数据源更新到对应到对应的ViewHolder,进而更新到对应的Item View; 具体使用参考:
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
// 将数据绑定到对应位置的ViewHolder
holder.show_tv.setText(mData.get(position));
}
显而易见,onBindViewHolder方法就是将数据源数据绑定更新到ViewHolder,进而将数据映射到每个Item View; onBindViewHolder方法需要解决两个关键问题:1、当数据源更新的时候,每个元素都会再调用这个方法更新样式吗?2、当数据源不变,只需要更新某个Item UI展示,如何操作? 具体的操作可以参考onBindViewHolder的源码实现,即参考方法bindViewHolder、tryBindViewHolderByDeadline的具体实现来解答此问题; 除了以上方法,在具体实现过程中需要设置Adapter的数据源和重写getItemCount方法,来实现Adapter更新数据; RecyclerView的item不像ListView那样提供点击监听,我们需要自定义Item的监听时间;Item本质是一个View,可以自定义实现各种各样的监听事件,比如单击、长按、手势划动等等; 另外上面提供的重写方法是最基本的,针对不同使用场景,各位做不同的定制化;
二、RecyclerView更丰富的功能
2.1、ItemTouchHelper的使用
ItemTouchHelper是一个处理RecyclerView的滑动删除和拖拽的辅助类,RecyclerView 的item拖拽移动和滑动删除就靠它来实现;官方文档介绍如下:
/**
* This is a utility class to add swipe to dismiss and drag & drop support to RecyclerView.
* It works with a RecyclerView and a Callback class, which configures what type of interactions
* are enabled and also receives events when user performs these actions.
* Depending on which functionality you support, you should override
* {@link Callback#onMove(RecyclerView, ViewHolder, ViewHolder)} and / or
* {@link Callback#onSwiped(ViewHolder, int)}.
* This class is designed to work with any LayoutManager but for certain situations, it can be
* optimized for your custom LayoutManager by extending methods in the
* {@link ItemTouchHelper.Callback} class or implementing {@link ItemTouchHelper.ViewDropHandler}
* interface in your LayoutManager.
* By default, ItemTouchHelper moves the items' translateX/Y properties to reposition them. You can
* customize these behaviors by overriding {@link Callback#onChildDraw(Canvas, RecyclerView,
* ViewHolder, float, float, int, boolean)}
* or {@link Callback#onChildDrawOver(Canvas, RecyclerView, ViewHolder, float, float, int,
* boolean)}.
* Most of the time you only need to override <code>onChildDraw</code>.
*/
public class ItemTouchHelper extends RecyclerView.ItemDecoration
implements RecyclerView.OnChildAttachStateChangeListener {
......
}
itemTouchHelper需要与recyclerView绑定才有效果,参考方法attachToRecyclerView:
/**
* Attaches the ItemTouchHelper to the provided RecyclerView. If TouchHelper is already
* attached to a RecyclerView, it will first detach from the previous one. You can call this
* method with {@code null} to detach it from the current RecyclerView.
* @param recyclerView The RecyclerView instance to which you want to add this helper or {@code null} if you want to remove ItemTouchHelper from the current RecyclerView.
*/
public void attachToRecyclerView(@Nullable RecyclerView recyclerView) {
......
}
根据ItemTouchHelper.Callback可以实现对Item的添加、删除的处理;
2.2、addOnScrollListener结合RecyclerView.LayoutManager实现滑动过多UI展示
addOnScrollListener用来监听Scroll滑动,根据对应的LayoutManager可以实现滑动时候UI的各种展示效果;