Android控件之RecyclerView
一.RecyclerView
作用:用来替换ListView和GridView。
好处:具有高度的解耦和效率。可以实现丰富多样的效果;
缺点:列表分割线需要自定义;列表的点击事件需要自行实现;
1.基本用法:
一般在xml文件中定义一个recyclerview,java中去实现(调用setAdap
ter加载适配器,显示recyclerview的layout及加载数据)。这个和ListView的方法是相同的,这个也是最基础的。
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
TextView textView;
RecyclerView.ViewHolder viewHolder;
private Context mContext;
public MyAdapter(Context context){
this.mContext = context;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(mContext).inflate(R.layout.itemview, null, false);
textView = view.findViewById(R.id.item_text);
viewHolder = new RecyclerView.ViewHolder(view) {
@Override
public String toString() {
return super.toString();
}
};
viewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
return false;
}
});
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
return viewHolder;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
}
@Override
public int getItemCount() {
return 30;
}
}
主要方法:
(1)onCreateViewHolder()
这个方法作用映射Item Layout Id,创建VH并返回。
/**
* 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);
(2)onBindViewHolder
这个方法的作用是将视图和数据进行绑定。为holder设置指定数据。
/**
* 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);
2.布局
这里加载RecyclerView的布局可以如上面这个例子中所写的,我们加
载xml。而RecyclerView则为我们提供了LayouManager来负责布局,包括对item的回收与获取
LayoutManager中主要的方法:
1.onLayoutChildren()
对RecyclerView进行布局的入口方法。这里在LayoutManager中仅是打
印出一个log信息:
public void onLayoutChildren(Recycler recycler, State state) {
Log.e(TAG, "You must override onLayoutChildren(Recycler recycler, State state) ");
}
那就是各个继承子类中会去实现onLayoutChildren(),我们以上例子
中的LinearLayoutManager来看:
注释写的很明确:
布局算法:
(1)通过检查子项和其他变量,找到锚点坐标和锚点项目位置。
(2)填充朝向开始,从底部堆叠
(3)向末端填充,从顶部堆叠
(4)滚动以满足从底部堆栈的要求。
/**
* {@inheritDoc}
*/
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
// layout algorithm:
// 1) by checking children and other variables, find an anchor coordinate and an anchor
// item position.
// 2) fill towards start, stacking from bottom
// 3) fill towards end, stacking from top
// 4) scroll to fulfill requirements like stack from bottom.
// create layout state
if (DEBUG) {
Log.d(TAG, "is pre layout:" + state.isPreLayout());
}
if (mPendingSavedState != null || mPendingScrollPosition != NO_POSITION) {
if (state.getItemCount() == 0) {
removeAndRecycleAllViews(recycler);
return;
}
}
其中关键的是方法为:
(1).fill():这个是对view的填充,看具体实现:
/**
* The magic functions :). Fills the given layout, defined by the layoutState. This is fairly
* independent from the rest of the {@link android.support.v7.widget.LinearLayoutManager}
* and with little change, can be made publicly available as a helper class.
*
* @param recycler Current recycler that is attached to RecyclerView
* @param layoutState Configuration on how we should fill out the available space.
* @param state Context passed by the RecyclerView to control scroll steps.
* @param stopOnFocusable If true, filling stops in the first focusable new child
* @return Number of pixels that it added. Useful for scroll functions.
*/
int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
RecyclerView.State state, boolean stopOnFocusable) {
// max offset we should set is mFastScroll + available
final int start = layoutState.mAvailable;
if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
// TODO ugly bug fix. should not happen
if (layoutState.mAvailable < 0) {
layoutState.mScrollingOffset += layoutState.mAvailable;
}
recycleByLayoutState(recycler, layoutState);
}
int remainingSpace = layoutState.mAvailable + layoutState.mExtra;
LayoutChunkResult layoutChunkResult = mLayoutChunkResult;
while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
layoutChunkResult.resetInternal();
if (VERBOSE_TRACING) {
TraceCompat.beginSection("LLM LayoutChunk");
}
layoutChunk(recycler, state, layoutState, layoutChunkResult);
if (VERBOSE_TRACING) {
TraceCompat.endSection();
}
if (layoutChunkResult.mFinished) {
break;
}
layoutState.mOffset += layoutChunkResult.mConsumed * layoutState.mLayoutDirection;
/**
* Consume the available space if:
* * layoutChunk did not request to be ignored
* * OR we are laying out scrap children
* * OR we are not doing pre-layout
*/
if (!layoutChunkResult.mIgnoreConsumed || mLayoutState.mScrapList != null
|| !state.isPreLayout()) {
layoutState.mAvailable -= layoutChunkResult.mConsumed;
// we keep a separate remaining space because mAvailable is important for recycling
remainingSpace -= layoutChunkResult.mConsumed;
}
if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
layoutState.mScrollingOffset += layoutChunkResult.mConsumed;
if (layoutState.mAvailable < 0) {
layoutState.mScrollingOffset += layoutState.mAvailable;
}
recycleByLayoutState(recycler, layoutState);
}
if (stopOnFocusable && layoutChunkResult.mFocusable) {
break;
}
}
if (DEBUG) {
validateChildOrder();
}
return start - layoutState.mAvailable;
}
很明显,这里就是对剩余空间不断的调用layoutChunk()方法,那接着看这个方法的实现:
void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
LayoutState layoutState, LayoutChunkResult result) {
View view = layoutState.next(recycler);
if (view == null) {
if (DEBUG && layoutState.mScrapList == null) {
throw new RuntimeException("received null view when unexpected");
}
// if we are laying out views in scrap, this may return null which means there is
// no more items to layout.
result.mFinished = true;
return;
}
LayoutParams params = (LayoutParams) view.getLayoutParams();
if (layoutState.mScrapList == null) {
if (mShouldReverseLayout == (layoutState.mLayoutDirection
== LayoutState.LAYOUT_START)) {
addView(view);
} else {
addView(view, 0);
}
} else {
if (mShouldReverseLayout == (layoutState.mLayoutDirection
== LayoutState.LAYOUT_START)) {
addDisappearingView(view);
} else {
addDisappearingView(view, 0);
}
}
measureChildWithMargins(view, 0, 0);
result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view);
int left, top, right, bottom;
if (mOrientation == VERTICAL) {
if (isLayoutRTL()) {
right = getWidth() - getPaddingRight();
left = right - mOrientationHelper.getDecoratedMeasurementInOther(view);
} else {
left = getPaddingLeft();
right = left + mOrientationHelper.getDecoratedMeasurementInOther(view);
}
if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
bottom = layoutState.mOffset;
top = layoutState.mOffset - result.mConsumed;
} else {
top = layoutState.mOffset;
bottom = layoutState.mOffset + result.mConsumed;
}
} else {
top = getPaddingTop();
bottom = top + mOrientationHelper.getDecoratedMeasurementInOther(view);
if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
right = layoutState.mOffset;
left = layoutState.mOffset - result.mConsumed;
} else {
left = layoutState.mOffset;
right = layoutState.mOffset + result.mConsumed;
}
}
// We calculate everything with View's bounding box (which includes decor and margins)
// To calculate correct layout position, we subtract margins.
layoutDecoratedWithMargins(view, left, top, right, bottom);
if (DEBUG) {
Log.d(TAG, "laid out child at position " + getPosition(view) + ", with l:"
+ (left + params.leftMargin) + ", t:" + (top + params.topMargin) + ", r:"
+ (right - params.rightMargin) + ", b:" + (bottom - params.bottomMargin));
}
// Consume the available space if the view is not removed OR changed
if (params.isItemRemoved() || params.isItemChanged()) {
result.mIgnoreConsumed = true;
}
result.mFocusable = view.hasFocusable();
}
这个方法主要实现:
(1)addView():加入view;
(2)measureChildWithMargins():计算view的大小;
(3)layoutDecoratedWithMargins():View布局
3.分割线
这里可以从截图中看到,这个列表是一个类似于listView的,但是是分割线的。这里我们可以调用addItemDecoration()方法来加入。Google默认是没有分割线的,因此需要自定义,这里就体现了灵活性。
主要需要重写如下两个方法:
1.onDraw(): 绘制分割线。
2.getItemOffsets(): 设置分割线的宽、高。
mRecyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
float left = parent.getPaddingLeft();
float right = parent.getWidth() - parent.getPaddingRight();
int childCount = parent.getChildCount();
Paint paint = new Paint();
paint.setColor(Color.RED);
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
float top = child.getBottom();
float bottom = child.getBottom() + 1.0f;
c.drawRect(left, top, right, bottom, paint);
}
}
});
这里:
A.分割线可以叠加的,即可以调用多次addItemDecoration方法;
B.分割线可以做横向的分割也可以纵向的分割;
C.onDraw()和onDrawOver的区别:
RecyclerView首先重写draw()方法,随后super.draw()调用View的draw()
,这个方法会先调用onDraw()方法,而RecyclerView中重写了onDraw(),再调用dispatchDraw()绘制children。
因此,区别在于:onDraw()是在绘制item之前调用;
onDrawover()是在绘制item之后调用;
@Override
public void draw(Canvas c) {
super.draw(c);
final int count = mItemDecorations.size();
for (int i = 0; i < count; i++) {
mItemDecorations.get(i).onDrawOver(c, this, mState);
}
// TODO If padding is not 0 and clipChildrenToPadding is false, to draw glows properly, we
// need find children closest to edges. Not sure if it is worth the effort.
boolean needsInvalidate = false;
if (mLeftGlow != null && !mLeftGlow.isFinished()) {
final int restore = c.save();
final int padding = mClipToPadding ? getPaddingBottom() : 0;
c.rotate(270);
c.translate(-getHeight() + padding, 0);
needsInvalidate = mLeftGlow != null && mLeftGlow.draw(c);
c.restoreToCount(restore);
}
4.点击事件
上述的方法实现好,可以看到一个和ListView一样的界面展示出来了,可以上下拖动,这时发现无法去点击。我们需要添加点击事件实现。由于这里没有ListView的onItemClickListener的实现,我们需要在adapter中定义接口并提供回调。
Adapter中实现:
private OnItemClickListener mItemClickListener;
public static interface OnItemClickListener {
void onItemClick(View view);
void onItemLongClick(View view);
}
public void setItemClickListener(OnItemClickListener itemClickListener) {
mItemClickListener = itemClickListener;
}
在重写onBindViewHolder的地方对item中的控件进行事件监听并且回调我们的自定义的监听:
viewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
mItemClickListener.onItemLongClick(v);
return true;
}
});
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mItemClickListener.onItemClick(v);
}
});
在Activity中进行监听:
adapter.setItemClickListener(new MyAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view) {
int position = mRecyclerView.getChildAdapterPosition(view);
Toast.makeText(MainActivity.this,"点击第" + (position+1)+ "条",Toast.LENGTH_SHORT).show();
}
@Override
public void onItemLongClick(View view) {
int position = mRecyclerView.getChildAdapterPosition(view);
Toast.makeText(MainActivity.this,"长按第" + (position+1)+ "条",Toast.LENGTH_SHORT).show();
}
});
5.动画效果
RecyclerView可以通过setItemAnimator()方法来实现动画效果,RecyclerView默认的是DefaultItemAnimator。
那我们来看DefaultItemAnimator的继承关系如下:
它提供了有关删除,添加和移动RecyclerView中的项目所发生事件的基本动画。
其中主要的方法为:
(1)animateAdd
boolean animateAdd (RecyclerView.ViewHolder holder)
将项目添加到RecyclerView时调用。 实现者可以选择是否以及如何为该更改设置动画,但必须在完成后立即调用dispatchAddFinished(ViewHolder),或者立即调用(如果没有动画),或者在动画实际完成之后调用。 返回值指示是否已设置动画以及是否应在下一次机会调用ItemAnimator的runPendingAnimations()方法。
(2)animateChange
boolean animateChange (RecyclerView.ViewHolder oldHolder,
RecyclerView.ViewHolder newHolder,
int fromX,
int fromY,
int toX,
int toY)
在RecyclerView中更改项目时调用,如对notifyItemChanged(int)或notifyItemRangeChanged(int,int)的调用所示。
实现者可以选择是否以及如何为更改设置动画,但必须始终为每个非空的不同ViewHolder调用dispatchChangeFinished(ViewHolder,boolean),或者立即调用(如果没有动画),或者在动画实际完成之后调用。如果oldHolder与newHolder是同一个ViewHolder,则必须只调用一次dispatchChangeFinished(ViewHolder,boolean)。在这种情况下,dispatchChangeFinished的第二个参数将被忽略。
返回值指示是否已设置动画以及是否应在下一次机会调用ItemAnimator的runPendingAnimations()方法。
(3)animateMove
boolean animateMove (RecyclerView.ViewHolder holder,
int fromX,
int fromY,
int toX,
int toY)
在RecyclerView中移动项目时调用。 实现者可以选择是否以及如何为该更改设置动画,但必须始终在完成后立即调用dispatchMoveFinished(ViewHolder)(如果不会发生动画)或动画实际完成后调用。 返回值指示是否已设置动画以及是否应在下一次机会调用ItemAnimator的runPendingAnimations()方法。
(4)animateRemove
boolean animateRemove (RecyclerView.ViewHolder holder)
从RecyclerView中删除项目时调用。实现者可以选择是否以及如何为该更改设置动画,但必须始终在调用dispatchRemoveFinished(ViewHolder)时立即调用(如果不会发生动画)或动画实际完成后调用。返回值指示是否已设置动画以及是否应在下一次机会调用ItemAnimator的runPendingAnimations()方法。
(5)canReuseUpdatedViewHolder
boolean canReuseUpdatedViewHolder (RecyclerView.ViewHolder viewHolder,
List payloads)
当项目被更改时,ItemAnimator可以决定是否要为动画重复使用相同的ViewHolder,或者RecyclerView应该创建项目的副本,而ItemAnimator将使用它们来运行动画(例如交叉淡入淡出)。
请注意,只有在RecyclerView.ViewHolder仍具有相同类型(getItemViewType(int))时才会调用此方法。 否则,ItemAnimator将始终在animateChange(ViewHolder,ViewHolder,ItemHolderInfo,ItemHolderInfo)方法中接收RecyclerView.ViewHolders。
(6)endAnimation
void endAnimation (RecyclerView.ViewHolder item)
应立即结束视图上的动画时调用的方法。 当其他事件(如滚动)发生时,可能会发生这种情况,因此可以将动画视图快速放入正确的结束位置。 实现应确保取消在项目上运行的任何动画,并将受影响的属性设置为其最终值
(7)runPendingAnimations
void runPendingAnimations ()
等待启动等待动画时调用。 此状态由animateAppearance(),animateChange()animatePersistence()和animateDisappearance()的返回值控制,它们告知RecyclerView以后要调用ItemAnimator以启动关联的动画。 runPendingAnimations()将被安排在下一帧上运行。
这个方法要多看下,这里在上述的一些方法的返回值为true后都会被调用。我们看下方法的实现:
A.首先判断是否有动画要实现:
if (!removalsPending && !movesPending && !additionsPending && !changesPending) {
// nothing to animate
return;
}
B.执行移除动画:
for (ViewHolder holder : mPendingRemovals) {
animateRemoveImpl(holder);
}
mPendingRemovals.clear();
C.执行移动动画:
if (movesPending) {
final ArrayList<MoveInfo> moves = new ArrayList<>();
moves.addAll(mPendingMoves);
mMovesList.add(moves);
mPendingMoves.clear();
Runnable mover = new Runnable() {
@Override
public void run() {
for (MoveInfo moveInfo : moves) {
animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY,
moveInfo.toX, moveInfo.toY);
}
moves.clear();
mMovesList.remove(moves);
}
};
if (removalsPending) {
View view = moves.get(0).holder.itemView;
ViewCompat.postOnAnimationDelayed(view, mover, getRemoveDuration());
} else {
mover.run();
}
}
D.执行变更动画,和移动动画并行处理:
if (changesPending) {
final ArrayList<ChangeInfo> changes = new ArrayList<>();
changes.addAll(mPendingChanges);
mChangesList.add(changes);
mPendingChanges.clear();
Runnable changer = new Runnable() {
@Override
public void run() {
for (ChangeInfo change : changes) {
animateChangeImpl(change);
}
changes.clear();
mChangesList.remove(changes);
}
};
if (removalsPending) {
ViewHolder holder = changes.get(0).oldHolder;
ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration());
} else {
changer.run();
}
}
E.执行添加动画:
if (additionsPending) {
final ArrayList<ViewHolder> additions = new ArrayList<>();
additions.addAll(mPendingAdditions);
mAdditionsList.add(additions);
mPendingAdditions.clear();
Runnable adder = new Runnable() {
@Override
public void run() {
for (ViewHolder holder : additions) {
animateAddImpl(holder);
}
additions.clear();
mAdditionsList.remove(additions);
}
};
if (removalsPending || movesPending || changesPending) {
long removeDuration = removalsPending ? getRemoveDuration() : 0;
long moveDuration = movesPending ? getMoveDuration() : 0;
long changeDuration = changesPending ? getChangeDuration() : 0;
long totalDelay = removeDuration + Math.max(moveDuration, changeDuration);
View view = additions.get(0).itemView;
ViewCompat.postOnAnimationDelayed(view, adder, totalDelay);
} else {
adder.run();
}
}
执行顺序为:
remove动画最先执行,随后move和change并行执行,最后是add动画.
我们需要再看下父类ItemAnimator中的重要方法:
(1).animateDisappearance
当ViewHolder消失在屏幕上时被调用(可能是remove或move)
(2).animateAppearance
当ViewHolder出现在屏幕上时被调用(可能是add或move)
(3).animatePersistence
在没调用notifyItemChanged()和notifyDataSetChanged()的情况下布局发生改变时被调用
(4).animateChange
在显式调用notifyItemChanged()或notifyDataSetChanged()时被调用
综上,如过自定义ItemAnimator,需要继承SimpleItemAnimator,并且实现上述的关键方法。
6.扩展
(1)添加headerView和footerView
这里引入装饰器设计模式(Decorator):这个模式通过组合的方式,在不破坏原类代码的情况下,完成对原类的扩展。
为原有的Adapter(这里命名为NormalAdapter)添加addHeaderView()和addFooterView()接口:
MyAdapter adapter = new MyAdapter(this);
MyAdapterWrapper newAdapter = new MyAdapterWrapper(adapter);
View headerView = LayoutInflater.from(this).inflate(R.layout.item_header, mRecyclerView, false);
View footerView = LayoutInflater.from(this).inflate(R.layout.item_footer, mRecyclerView, false);
newAdapter.addHeaderView(headerView);
newAdapter.addFooterView(footerView);
mRecyclerView.setAdapter(newAdapter);
接着是MyAdapterWrapper的实现:
public class MyAdapterWrapper extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
enum ITEM_TYPE{
HEADER,
FOOTER,
NORMAL
}
private MyAdapter mAdapter;
private View mHeaderView;
private View mFooterView;
public MyAdapterWrapper(MyAdapter myAdapter){
mAdapter = myAdapter;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if(viewType == ITEM_TYPE.HEADER.ordinal()){
return new RecyclerView.ViewHolder(mHeaderView) {};
} else if(viewType == ITEM_TYPE.FOOTER.ordinal()){
return new RecyclerView.ViewHolder(mFooterView) {};
} else{
return mAdapter.onCreateViewHolder(parent,viewType);
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (position == 0){
return;
}else if(position == mAdapter.getItemCount() + 1){
return;
}else {
mAdapter.onBindViewHolder((MyAdapter.VH)holder,position-1);
}
}
@Override
public int getItemCount() {
return mAdapter.getItemCount() + 2;
}
public void addFooterView(View view){
this.mFooterView = view;
}
public void addHeaderView(View view){
this.mHeaderView = view;
}
}
(2)空布局
ListView中有一个方法setEmptyView(),是当Adapter数据为空的时候的View视图,而RecyclerView没有这个API。可以通过监听者模式监听RecyclerView的数据变化,如果adapter为空,那么隐藏RecyclerView,显示EmptyView
(3)RcyclerView拖曳滑动实现
有一个ItemTouchHelper类,可以直接使用,也可以自定义继承ItemTouchHelper.Callback类,并重写一些重要方法,如:
A.getMovementFlags():设置支持的拖拽和滑动的方向,此处我们支持的拖拽方向为上下,滑动方向为从左到右和从右到左,内部通过makeMovementFlags()设置。
B.onMove():拖曳时回调。
C.onSwiped():滑动时回调。
D.isLongPressDragEnabled():是否支持长按推动,默认为true。若要关闭则重写后返回false。
ItemTouchHelper helper = new ItemTouchHelper(new ItemTouchHelper.Callback() {
@Override
public int getMovementFlags(RecyclerView recyclerView, ViewHolder viewHolder) {
return 0;
}
@Override
public boolean onMove(RecyclerView recyclerView, ViewHolder viewHolder, ViewHolder target) {
return false;
}
@Override
public void onSwiped(ViewHolder viewHolder, int direction) {
}
});
helper.attachToRecyclerView(mRecyclerView);
前面拖拽的触发方式只有长按,如果想支持触摸Item中的某个View实现拖拽,则核心方法为helper.startDrag(holder)。首先定义接口:
interface OnStartDragListener{
void startDrag(RecyclerView.ViewHolder holder);
}
然后让Activity实现该接口:
public MainActivity extends Activity implements OnStartDragListener{
...
public void startDrag(RecyclerView.ViewHolder holder) {
mHelper.startDrag(holder);
}
}
如果要对ViewHolder的text对象支持触摸拖拽,则在Adapter中的onBindViewHolder()中添加:
holder.text.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
mListener.startDrag(holder);
}
return false;
}
});
其中mListener是在创建Adapter时将实现OnStartDragListener接口的Activity对象作为参数传进来。
(4).RecyclerView回收机制
这个后续会和ListView的回收机制一起做个对比。
注意:
1.Android Stuido关于在V7包下找不到recyclerview的解决办法
ctrl + alt + shift + s 打开Project
Structure,选择app,点击右上角加号选择第一项Library dependence。搜索recyclerview,将结果添加进去,重新build即可。
2.RecyclerView如果不显示内容的解决方法:
A.Adapter中的getItemCount()需要有返回值>0;
B.setAdapter之前要调用setLayoutManager();