当前位置: 移动技术网 > 移动技术>移动开发>Android > 解决RecyclerView无法onItemClick问题的两种方法

解决RecyclerView无法onItemClick问题的两种方法

2019年07月24日  | 移动技术网移动技术  | 我要评论
对于recyclerview的使用,大家可以查看将替代listview的recyclerview 的使用详解(一)单单从代码结构来说recyclerview确实比listv

对于recyclerview的使用,大家可以查看将替代listview的recyclerview 的使用详解(一)单单从代码结构来说recyclerview确实比listview优化了很多,也简化了我们编写代码量,但是有一个问题会导致开发者不会去用它,更比说替换listview了,我不知道使用过recyclerview的人有没有进一步查看,recyclerview没有提供item的点击事件,我们使用列表不仅仅为了显示数据,同时也可以能会交互,所以recyclerview这个问题导致基本没有人用它,我清楚谷歌是怎么想的,不过recyclerview也并没有把所有的路给堵死,需要我们写代码来实现item的点击事件,我们都知道recyclerview里面新加了viewholder这个静态抽象类,这个类里面有一个方法getposition()可以返回当前viewholder实例的位置,实现onitemclick就是使用它来做的,下面有两种方法来实现:

第一种:不修改源码

这种方法不修改源码,问题是只能在recyclerview.adapter中实现itemclick事件

public static class viewholder extends recyclerview.viewholder { 
public viewholder(view itemview) { 
super(itemview); 
itemview.setonclicklistener(new onclicklistener() { 
@override 
public void onclick(view v) { 
log.e("jwzhangjie", "当前点击的位置:"+getposition()); 
} 
}); 
} 
} 

这种方式直观上看起来不太好,不过也可以实现itemclick事件。

第二种方法:修改recyclerview源码

1、把在recyclerview类里面定义onitemclicklistener接口

/** 
* interface definition for a callback to be invoked when an item in this 
* recyclerview.adapter has been clicked. 
*/ 
public interface onitemclicklistener { 
/** 
* callback method to be invoked when an item in this recyclerview.adapter has 
* been clicked. 
* <p> 
* implementers can call getposition(position) if they need 
* to access the data associated with the selected item. 
* 
* @param view the view within the recyclerview.adapter that was clicked (this 
* will be a view provided by the adapter) 
* @param position the position of the view in the adapter. 
*/ 
void onitemclick(view view, int position); 
} 
public static onitemclicklistener monitemclicklistener = null; 
/** 
* register a callback to be invoked when an item in this adapterview has 
* been clicked. 
* 
* @param listener the callback that will be invoked. 
*/ 
public void setonitemclicklistener(onitemclicklistener listener) { 
monitemclicklistener = listener; 
} 
/** 
* @return the callback to be invoked with an item in this adapterview has 
* been clicked, or null id no callback has been set. 
*/ 
public final onitemclicklistener getonitemclicklistener() { 
return monitemclicklistener; 
} 

2、在recyclerview中的抽象类viewholder中添加view的点击事件

public static abstract class viewholder implements onclicklistener{ 
public final view itemview; 
int mposition = no_position; 
int moldposition = no_position; 
long mitemid = no_id; 
int mitemviewtype = invalid_type; 
/** 
* this viewholder has been bound to a position; mposition, mitemid and mitemviewtype 
* are all valid. 
*/ 
static final int flag_bound = 1 << 0; 
/** 
* the data this viewholder's view reflects is stale and needs to be rebound 
* by the adapter. mposition and mitemid are consistent. 
*/ 
static final int flag_update = 1 << 1; 
/** 
* this viewholder's data is invalid. the identity implied by mposition and mitemid 
* are not to be trusted and may no longer match the item view type. 
* this viewholder must be fully rebound to different data. 
*/ 
static final int flag_invalid = 1 << 2; 
/** 
* this viewholder points at data that represents an item previously removed from the 
* data set. its view may still be used for things like outgoing animations. 
*/ 
static final int flag_removed = 1 << 3; 
/** 
* this viewholder should not be recycled. this flag is set via setisrecyclable() 
* and is intended to keep views around during animations. 
*/ 
static final int flag_not_recyclable = 1 << 4; 
private int mflags; 
private int misrecyclablecount = 0; 
// if non-null, view is currently considered scrap and may be reused for other data by the 
// scrap container. 
private recycler mscrapcontainer = null; 
@override 
public void onclick(view v) { 
if (monitemclicklistener != null) { 
monitemclicklistener.onitemclick(itemview, getposition()); 
} 
} 
public viewholder(view itemview) { 
if (itemview == null) { 
throw new illegalargumentexception("itemview may not be null"); 
} 
this.itemview = itemview; 
this.itemview.setonclicklistener(this); 
} 
void offsetposition(int offset) { 
if (moldposition == no_position) { 
moldposition = mposition; 
} 
mposition += offset; 
} 
void clearoldposition() { 
moldposition = no_position; 
} 
public final int getposition() { 
return moldposition == no_position ? mposition : moldposition; 
} 
public final long getitemid() { 
return mitemid; 
} 
public final int getitemviewtype() { 
return mitemviewtype; 
} 
boolean isscrap() { 
return mscrapcontainer != null; 
} 
void unscrap() { 
mscrapcontainer.unscrapview(this); 
mscrapcontainer = null; 
} 
void setscrapcontainer(recycler recycler) { 
mscrapcontainer = recycler; 
} 
boolean isinvalid() { 
return (mflags & flag_invalid) != 0; 
} 
boolean needsupdate() { 
return (mflags & flag_update) != 0; 
} 
boolean isbound() { 
return (mflags & flag_bound) != 0; 
} 
boolean isremoved() { 
return (mflags & flag_removed) != 0; 
} 
void setflags(int flags, int mask) { 
mflags = (mflags & ~mask) | (flags & mask); 
} 
void addflags(int flags) { 
mflags |= flags; 
} 
void clearflagsforsharedpool() { 
mflags = 0; 
} 
@override 
public string tostring() { 
final stringbuilder sb = new stringbuilder("viewholder{" + 
integer.tohexstring(hashcode()) + " position=" + mposition + " id=" + mitemid); 
if (isscrap()) sb.append(" scrap"); 
if (isinvalid()) sb.append(" invalid"); 
if (!isbound()) sb.append(" unbound"); 
if (needsupdate()) sb.append(" update"); 
if (isremoved()) sb.append(" removed"); 
sb.append("}"); 
return sb.tostring(); 
} 

3、完成上面的步骤,就可以使用recyclerview来完成itemclick事件了

cashaccountlist.setonitemclicklistener(new onitemclicklistener() { 
@override 
public void onitemclick(view view, int position) { 
applog.e("position: "+position); 
} 
}); 

下面是完整的recyclerview源码:

/* 
* copyright (c) 2013 the android open source project 
* 
* licensed under the apache license, version 2.0 (the "license"); 
* you may not use this file except in compliance with the license. 
* you may obtain a copy of the license at 
* 
* http://www.apache.org/licenses/license-2.0 
* 
* unless required by applicable law or agreed to in writing, software 
* distributed under the license is distributed on an "as is" basis, 
* without warranties or conditions of any kind, either express or implied. 
* see the license for the specific language governing permissions and 
* limitations under the license. 
*/ 
package android.support.v7.widget; 
import android.content.context; 
import android.database.observable; 
import android.graphics.canvas; 
import android.graphics.pointf; 
import android.graphics.rect; 
import android.os.build; 
import android.os.parcel; 
import android.os.parcelable; 
import android.support.annotation.nullable; 
import android.support.v4.util.arraymap; 
import android.support.v4.util.pools; 
import android.support.v4.view.motioneventcompat; 
import android.support.v4.view.velocitytrackercompat; 
import android.support.v4.view.viewcompat; 
import android.support.v4.widget.edgeeffectcompat; 
import android.support.v4.widget.scrollercompat; 
import android.util.attributeset; 
import android.util.log; 
import android.util.sparsearray; 
import android.util.sparseintarray; 
import android.view.focusfinder; 
import android.view.motionevent; 
import android.view.velocitytracker; 
import android.view.view; 
import android.view.viewconfiguration; 
import android.view.viewgroup; 
import android.view.viewparent; 
import android.view.animation.interpolator; 
import java.util.arraylist; 
import java.util.collections; 
import java.util.list; 
/** 
* a flexible view for providing a limited window into a large data set. 
* 
* <h3>glossary of terms:</h3> 
* 
* <ul> 
* <li><em>adapter:</em> a subclass of {@link adapter} responsible for providing views 
* that represent items in a data set.</li> 
* <li><em>position:</em> the position of a data item within an <em>adapter</em>.</li> 
* <li><em>index:</em> the index of an attached child view as used in a call to 
* {@link viewgroup#getchildat}. contrast with <em>position.</em></li> 
* <li><em>binding:</em> the process of preparing a child view to display data corresponding 
* to a <em>position</em> within the adapter.</li> 
* <li><em>recycle (view):</em> a view previously used to display data for a specific adapter 
* position may be placed in a cache for later reuse to display the same type of data again 
* later. this can drastically improve performance by skipping initial layout inflation 
* or construction.</li> 
* <li><em>scrap (view):</em> a child view that has entered into a temporarily detached 
* state during layout. scrap views may be reused without becoming fully detached 
* from the parent recyclerview, either unmodified if no rebinding is required or modified 
* by the adapter if the view was considered <em>dirty</em>.</li> 
* <li><em>dirty (view):</em> a child view that must be rebound by the adapter before 
* being displayed.</li> 
* </ul> 
*/ 
public class recyclerview extends viewgroup { 
private static final string tag = "recyclerview"; 
private static final boolean debug = false; 
private static final boolean enable_predictive_animations = false; 
private static final boolean dispatch_temp_detach = false; 
public static final int horizontal = 0; 
public static final int vertical = 1; 
public static final int no_position = -1; 
public static final long no_id = -1; 
public static final int invalid_type = -1; 
private static final int max_scroll_duration = 2000; 
private final recyclerviewdataobserver mobserver = new recyclerviewdataobserver(); 
private final recycler mrecycler = new recycler(); 
private savedstate mpendingsavedstate; 
/** 
* note: this runnable is only ever posted if: 
* 1) we've been through first layout 
* 2) we know we have a fixed size (mhasfixedsize) 
* 3) we're attached 
*/ 
private final runnable mupdatechildviewsrunnable = new runnable() { 
public void run() { 
if (mpendingupdates.isempty()) { 
return; 
} 
eatrequestlayout(); 
updatechildviews(); 
resumerequestlayout(true); 
} 
}; 
private final rect mtemprect = new rect(); 
private final arraylist<updateop> mpendingupdates = new arraylist<updateop>(); 
private final arraylist<updateop> mpendinglayoutupdates = new arraylist<updateop>(); 
private pools.pool<updateop> mupdateoppool = new pools.simplepool<updateop>(updateop.pool_size); 
private adapter madapter; 
private layoutmanager mlayout; 
private recyclerlistener mrecyclerlistener; 
private final arraylist<itemdecoration> mitemdecorations = new arraylist<itemdecoration>(); 
private final arraylist<onitemtouchlistener> monitemtouchlisteners = 
new arraylist<onitemtouchlistener>(); 
private onitemtouchlistener mactiveonitemtouchlistener; 
private boolean misattached; 
private boolean mhasfixedsize; 
private boolean mfirstlayoutcomplete; 
private boolean meatrequestlayout; 
private boolean mlayoutrequesteaten; 
private boolean madapterupdateduringmeasure; 
private final boolean mpostupdatesonanimation; 
private edgeeffectcompat mleftglow, mtopglow, mrightglow, mbottomglow; 
itemanimator mitemanimator = new defaultitemanimator(); 
private static final int invalid_pointer = -1; 
/** 
* the recyclerview is not currently scrolling. 
* @see #getscrollstate() 
*/ 
public static final int scroll_state_idle = 0; 
/** 
* the recyclerview is currently being dragged by outside input such as user touch input. 
* @see #getscrollstate() 
*/ 
public static final int scroll_state_dragging = 1; 
/** 
* the recyclerview is currently animating to a final position while not under 
* outside control. 
* @see #getscrollstate() 
*/ 
public static final int scroll_state_settling = 2; 
// touch/scrolling handling 
private int mscrollstate = scroll_state_idle; 
private int mscrollpointerid = invalid_pointer; 
private velocitytracker mvelocitytracker; 
private int minitialtouchx; 
private int minitialtouchy; 
private int mlasttouchx; 
private int mlasttouchy; 
private final int mtouchslop; 
private final int mminflingvelocity; 
private final int mmaxflingvelocity; 
private final viewflinger mviewflinger = new viewflinger(); 
private final state mstate = new state(); 
private onscrolllistener mscrolllistener; 
// for use in item animations 
boolean mitemsaddedorremoved = false; 
boolean mitemschanged = false; 
int manimatingviewindex = -1; 
int mnumanimatingviews = 0; 
boolean minprelayout = false; 
private itemanimator.itemanimatorlistener mitemanimatorlistener = 
new itemanimatorrestorelistener(); 
private boolean mpostedanimatorrunner = false; 
private runnable mitemanimatorrunner = new runnable() { 
@override 
public void run() { 
if (mitemanimator != null) { 
mitemanimator.runpendinganimations(); 
} 
mpostedanimatorrunner = false; 
} 
}; 
private static final interpolator squinticinterpolator = new interpolator() { 
public float getinterpolation(float t) { 
t -= 1.0f; 
return t * t * t * t * t + 1.0f; 
} 
}; 
public recyclerview(context context) { 
this(context, null); 
} 
public recyclerview(context context, attributeset attrs) { 
this(context, attrs, 0); 
} 
public recyclerview(context context, attributeset attrs, int defstyle) { 
super(context, attrs, defstyle); 
final int version = build.version.sdk_int; 
mpostupdatesonanimation = version >= 16; 
final viewconfiguration vc = viewconfiguration.get(context); 
mtouchslop = vc.getscaledtouchslop(); 
mminflingvelocity = vc.getscaledminimumflingvelocity(); 
mmaxflingvelocity = vc.getscaledmaximumflingvelocity(); 
setwillnotdraw(viewcompat.getoverscrollmode(this) == viewcompat.over_scroll_never); 
mitemanimator.setlistener(mitemanimatorlistener); 
} 
/** 
* recyclerview can perform several optimizations if it can know in advance that changes in 
* adapter content cannot change the size of the recyclerview itself. 
* if your use of recyclerview falls into this category, set this to true. 
* 
* @param hasfixedsize true if adapter changes cannot affect the size of the recyclerview. 
*/ 
public void sethasfixedsize(boolean hasfixedsize) { 
mhasfixedsize = hasfixedsize; 
} 
/** 
* @return true if the app has specified that changes in adapter content cannot change 
* the size of the recyclerview itself. 
*/ 
public boolean hasfixedsize() { 
return mhasfixedsize; 
} 
/** 
* set a new adapter to provide child views on demand. 
* 
* @param adapter the new adapter to set, or null to set no adapter. 
*/ 
public void setadapter(adapter adapter) { 
if (madapter != null) { 
madapter.unregisteradapterdataobserver(mobserver); 
} 
// end all running animations 
if (mitemanimator != null) { 
mitemanimator.endanimations(); 
} 
// since animations are ended, mlayout.children should be equal to recyclerview.children. 
// this may not be true if item animator's end does not work as expected. (e.g. not release 
// children instantly). it is safer to use mlayout's child count. 
if (mlayout != null) { 
mlayout.removeandrecycleallviews(mrecycler); 
mlayout.removeandrecyclescrapint(mrecycler, true); 
} 
final adapter oldadapter = madapter; 
madapter = adapter; 
if (adapter != null) { 
adapter.registeradapterdataobserver(mobserver); 
} 
if (mlayout != null) { 
mlayout.onadapterchanged(oldadapter, madapter); 
} 
mrecycler.onadapterchanged(oldadapter, madapter); 
mstate.mstructurechanged = true; 
markknownviewsinvalid(); 
requestlayout(); 
} 
/** 
* retrieves the previously set adapter or null if no adapter is set. 
* 
* @return the previously set adapter 
* @see #setadapter(adapter) 
*/ 
public adapter getadapter() { 
return madapter; 
} 
/** 
* register a listener that will be notified whenever a child view is recycled. 
* 
* <p>this listener will be called when a layoutmanager or the recyclerview decides 
* that a child view is no longer needed. if an application associates expensive 
* or heavyweight data with item views, this may be a good place to release 
* or free those resources.</p> 
* 
* @param listener listener to register, or null to clear 
*/ 
public void setrecyclerlistener(recyclerlistener listener) { 
mrecyclerlistener = listener; 
} 
/** 
* 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) { 
if (layout == mlayout) { 
return; 
} 
mrecycler.clear(); 
removeallviews(); 
if (mlayout != null) { 
if (misattached) { 
mlayout.ondetachedfromwindow(this); 
} 
mlayout.mrecyclerview = null; 
} 
mlayout = layout; 
if (layout != null) { 
if (layout.mrecyclerview != null) { 
throw new illegalargumentexception("layoutmanager " + layout + 
" is already attached to a recyclerview: " + layout.mrecyclerview); 
} 
layout.mrecyclerview = this; 
if (misattached) { 
mlayout.onattachedtowindow(this); 
} 
} 
requestlayout(); 
} 
@override 
protected parcelable onsaveinstancestate() { 
savedstate state = new savedstate(super.onsaveinstancestate()); 
if (mpendingsavedstate != null) { 
state.copyfrom(mpendingsavedstate); 
} else if (mlayout != null) { 
state.mlayoutstate = mlayout.onsaveinstancestate(); 
} else { 
state.mlayoutstate = null; 
} 
return state; 
} 
@override 
protected void onrestoreinstancestate(parcelable state) { 
mpendingsavedstate = (savedstate) state; 
super.onrestoreinstancestate(mpendingsavedstate.getsuperstate()); 
if (mlayout != null && mpendingsavedstate.mlayoutstate != null) { 
mlayout.onrestoreinstancestate(mpendingsavedstate.mlayoutstate); 
} 
} 
/** 
* adds a view to the animatingviews list. 
* manimatingviews holds the child views that are currently being kept around 
* purely for the purpose of being animated out of view. they are drawn as a regular 
* part of the child list of the recyclerview, but they are invisible to the layoutmanager 
* as they are managed separately from the regular child views. 
* @param view the view to be removed 
*/ 
private void addanimatingview(view view) { 
boolean alreadyadded = false; 
if (mnumanimatingviews > 0) { 
for (int i = manimatingviewindex; i < getchildcount(); ++i) { 
if (getchildat(i) == view) { 
alreadyadded = true; 
break; 
} 
} 
} 
if (!alreadyadded) { 
if (mnumanimatingviews == 0) { 
manimatingviewindex = getchildcount(); 
} 
++mnumanimatingviews; 
addview(view); 
} 
mrecycler.unscrapview(getchildviewholder(view)); 
} 
/** 
* removes a view from the animatingviews list. 
* @param view the view to be removed 
* @see #addanimatingview(view) 
*/ 
private void removeanimatingview(view view) { 
if (mnumanimatingviews > 0) { 
for (int i = manimatingviewindex; i < getchildcount(); ++i) { 
if (getchildat(i) == view) { 
removeviewat(i); 
--mnumanimatingviews; 
if (mnumanimatingviews == 0) { 
manimatingviewindex = -1; 
} 
mrecycler.recycleview(view); 
return; 
} 
} 
} 
} 
private view getanimatingview(int position, int type) { 
if (mnumanimatingviews > 0) { 
for (int i = manimatingviewindex; i < getchildcount(); ++i) { 
final view view = getchildat(i); 
viewholder holder = getchildviewholder(view); 
if (holder.getposition() == position && 
( type == invalid_type || holder.getitemviewtype() == type)) { 
return view; 
} 
} 
} 
return null; 
} 
/** 
* return the {@link layoutmanager} currently responsible for 
* layout policy for this recyclerview. 
* 
* @return the currently bound layoutmanager 
*/ 
public layoutmanager getlayoutmanager() { 
return mlayout; 
} 
/** 
* retrieve this recyclerview's {@link recycledviewpool}. this method will never return null; 
* if no pool is set for this view a new one will be created. see 
* {@link #setrecycledviewpool(recycledviewpool) setrecycledviewpool} for more information. 
* 
* @return the pool used to store recycled item views for reuse. 
* @see #setrecycledviewpool(recycledviewpool) 
*/ 
public recycledviewpool getrecycledviewpool() { 
return mrecycler.getrecycledviewpool(); 
} 
/** 
* recycled view pools allow multiple recyclerviews to share a common pool of scrap views. 
* this can be useful if you have multiple recyclerviews with adapters that use the same 
* view types, for example if you have several data sets with the same kinds of item views 
* displayed by a {@link android.support.v4.view.viewpager viewpager}. 
* 
* @param pool pool to set. if this parameter is null a new pool will be created and used. 
*/ 
public void setrecycledviewpool(recycledviewpool pool) { 
mrecycler.setrecycledviewpool(pool); 
} 
/** 
* set the number of offscreen views to retain before adding them to the potentially shared 
* {@link #getrecycledviewpool() recycled view pool}. 
* 
* <p>the offscreen view cache stays aware of changes in the attached adapter, allowing 
* a layoutmanager to reuse those views unmodified without needing to return to the adapter 
* to rebind them.</p> 
* 
* @param size number of views to cache offscreen before returning them to the general 
* recycled view pool 
*/ 
public void setitemviewcachesize(int size) { 
mrecycler.setviewcachesize(size); 
} 
/** 
* return the current scrolling state of the recyclerview. 
* 
* @return {@link #scroll_state_idle}, {@link #scroll_state_dragging} or 
* {@link #scroll_state_settling} 
*/ 
public int getscrollstate() { 
return mscrollstate; 
} 
private void setscrollstate(int state) { 
if (state == mscrollstate) { 
return; 
} 
mscrollstate = state; 
if (state != scroll_state_settling) { 
stopscroll(); 
} 
if (mscrolllistener != null) { 
mscrolllistener.onscrollstatechanged(state); 
} 
} 
/** 
* 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 (mitemdecorations.isempty()) { 
setwillnotdraw(false); 
} 
if (index < 0) { 
mitemdecorations.add(decor); 
} else { 
mitemdecorations.add(index, decor); 
} 
markitemdecorinsetsdirty(); 
requestlayout(); 
} 
/** 
* 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 
*/ 
public void additemdecoration(itemdecoration decor) { 
additemdecoration(decor, -1); 
} 
/** 
* remove an {@link itemdecoration} from this recyclerview. 
* 
* <p>the given decoration will no longer impact the measurement and drawing of 
* item views.</p> 
* 
* @param decor decoration to remove 
* @see #additemdecoration(itemdecoration) 
*/ 
public void removeitemdecoration(itemdecoration decor) { 
mitemdecorations.remove(decor); 
if (mitemdecorations.isempty()) { 
setwillnotdraw(viewcompat.getoverscrollmode(this) == viewcompat.over_scroll_never); 
} 
markitemdecorinsetsdirty(); 
requestlayout(); 
} 
/** 
* set a listener that will be notified of any changes in scroll state or position. 
* 
* @param listener listener to set or null to clear 
*/ 
public void setonscrolllistener(onscrolllistener listener) { 
mscrolllistener = listener; 
} 
/** 
* convenience method to scroll to a certain position. 
* 
* recyclerview does not implement scrolling logic, rather forwards the call to 
* {@link android.support.v7.widget.recyclerview.layoutmanager#scrolltoposition(int)} 
* @param position scroll to this adapter position 
* @see android.support.v7.widget.recyclerview.layoutmanager#scrolltoposition(int) 
*/ 
public void scrolltoposition(int position) { 
stopscroll(); 
mlayout.scrolltoposition(position); 
awakenscrollbars(); 
} 
/** 
* starts a smooth scroll to an adapter position. 
* <p> 
* to support smooth scrolling, you must override 
* {@link layoutmanager#smoothscrolltoposition(recyclerview, state, int)} and create a 
* {@link smoothscroller}. 
* <p> 
* {@link layoutmanager} is responsible for creating the actual scroll action. if you want to 
* provide a custom smooth scroll logic, override 
* {@link layoutmanager#smoothscrolltoposition(recyclerview, state, int)} in your 
* layoutmanager. 
* 
* @param position the adapter position to scroll to 
* @see layoutmanager#smoothscrolltoposition(recyclerview, state, int) 
*/ 
public void smoothscrolltoposition(int position) { 
mlayout.smoothscrolltoposition(this, mstate, position); 
} 
@override 
public void scrollto(int x, int y) { 
throw new unsupportedoperationexception( 
"recyclerview does not support scrolling to an absolute position."); 
} 
@override 
public void scrollby(int x, int y) { 
if (mlayout == null) { 
throw new illegalstateexception("cannot scroll without a layoutmanager set. " + 
"call setlayoutmanager with a non-null argument."); 
} 
final boolean canscrollhorizontal = mlayout.canscrollhorizontally(); 
final boolean canscrollvertical = mlayout.canscrollvertically(); 
if (canscrollhorizontal || canscrollvertical) { 
scrollbyinternal(canscrollhorizontal ? x : 0, canscrollvertical ? y : 0); 
} 
} 
/** 
* helper method reflect data changes to the state. 
* <p> 
* adapter changes during a scroll may trigger a crash because scroll assumes no data change 
* but data actually changed. 
* <p> 
* this method consumes all deferred changes to avoid that case. 
* <p> 
* this also ends all pending animations. it will be changed once we can support 
* animations during scroll. 
*/ 
private void consumependingupdateoperations() { 
if (mitemanimator != null) { 
mitemanimator.endanimations(); 
} 
if (mpendingupdates.size() > 0) { 
mupdatechildviewsrunnable.run(); 
} 
} 
/** 
* does not perform bounds checking. used by internal methods that have already validated input. 
*/ 
void scrollbyinternal(int x, int y) { 
int overscrollx = 0, overscrolly = 0; 
consumependingupdateoperations(); 
if (madapter != null) { 
eatrequestlayout(); 
if (x != 0) { 
final int hresult = mlayout.scrollhorizontallyby(x, mrecycler, mstate); 
overscrollx = x - hresult; 
} 
if (y != 0) { 
final int vresult = mlayout.scrollverticallyby(y, mrecycler, mstate); 
overscrolly = y - vresult; 
} 
resumerequestlayout(false); 
} 
if (!mitemdecorations.isempty()) { 
invalidate(); 
} 
if (viewcompat.getoverscrollmode(this) != viewcompat.over_scroll_never) { 
pullglows(overscrollx, overscrolly); 
} 
if (mscrolllistener != null && (x != 0 || y != 0)) { 
mscrolllistener.onscrolled(x, y); 
} 
if (!awakenscrollbars()) { 
invalidate(); 
} 
} 
/** 
* <p>compute the horizontal offset of the horizontal scrollbar's thumb within the horizontal 
* range. this value is used to compute the length of the thumb within the scrollbar's track. 
* </p> 
* 
* <p>the range is expressed in arbitrary units that must be the same as the units used by 
* {@link #computehorizontalscrollrange()} and {@link #computehorizontalscrollextent()}.</p> 
* 
* <p>default implementation returns 0.</p> 
* 
* <p>if you want to support scroll bars, override 
* {@link recyclerview.layoutmanager#computehorizontalscrolloffset(recyclerview.state)} in your 
* layoutmanager. </p> 
* 
* @return the horizontal offset of the scrollbar's thumb 
* @see android.support.v7.widget.recyclerview.layoutmanager#computehorizontalscrolloffset 
* (recyclerview.adapter) 
*/ 
@override 
protected int computehorizontalscrolloffset() { 
return mlayout.canscrollhorizontally() ? mlayout.computehorizontalscrolloffset(mstate) 
: 0; 
} 
/** 
* <p>compute the horizontal extent of the horizontal scrollbar's thumb within the 
* horizontal range. this value is used to compute the length of the thumb within the 
* scrollbar's track.</p> 
* 
* <p>the range is expressed in arbitrary units that must be the same as the units used by 
* {@link #computehorizontalscrollrange()} and {@link #computehorizontalscrolloffset()}.</p> 
* 
* <p>default implementation returns 0.</p> 
* 
* <p>if you want to support scroll bars, override 
* {@link recyclerview.layoutmanager#computehorizontalscrollextent(recyclerview.state)} in your 
* layoutmanager.</p> 
* 
* @return the horizontal extent of the scrollbar's thumb 
* @see recyclerview.layoutmanager#computehorizontalscrollextent(recyclerview.state) 
*/ 
@override 
protected int computehorizontalscrollextent() { 
return mlayout.canscrollhorizontally() ? mlayout.computehorizontalscrollextent(mstate) : 0; 
} 
/** 
* <p>compute the horizontal range that the horizontal scrollbar represents.</p> 
* 
* <p>the range is expressed in arbitrary units that must be the same as the units used by 
* {@link #computehorizontalscrollextent()} and {@link #computehorizontalscrolloffset()}.</p> 
* 
* <p>default implementation returns 0.</p> 
* 
* <p>if you want to support scroll bars, override 
* {@link recyclerview.layoutmanager#computehorizontalscrollrange(recyclerview.state)} in your 
* layoutmanager.</p> 
* 
* @return the total horizontal range represented by the vertical scrollbar 
* @see recyclerview.layoutmanager#computehorizontalscrollrange(recyclerview.state) 
*/ 
@override 
protected int computehorizontalscrollrange() { 
return mlayout.canscrollhorizontally() ? mlayout.computehorizontalscrollrange(mstate) : 0; 
} 
/** 
* <p>compute the vertical offset of the vertical scrollbar's thumb within the vertical range. 
* this value is used to compute the length of the thumb within the scrollbar's track. </p> 
* 
* <p>the range is expressed in arbitrary units that must be the same as the units used by 
* {@link #computeverticalscrollrange()} and {@link #computeverticalscrollextent()}.</p> 
* 
* <p>default implementation returns 0.</p> 
* 
* <p>if you want to support scroll bars, override 
* {@link recyclerview.layoutmanager#computeverticalscrolloffset(recyclerview.state)} in your 
* layoutmanager.</p> 
* 
* @return the vertical offset of the scrollbar's thumb 
* @see android.support.v7.widget.recyclerview.layoutmanager#computeverticalscrolloffset 
* (recyclerview.adapter) 
*/ 
@override 
protected int computeverticalscrolloffset() { 
return mlayout.canscrollvertically() ? mlayout.computeverticalscrolloffset(mstate) : 0; 
} 
/** 
* <p>compute the vertical extent of the vertical scrollbar's thumb within the vertical range. 
* this value is used to compute the length of the thumb within the scrollbar's track.</p> 
* 
* <p>the range is expressed in arbitrary units that must be the same as the units used by 
* {@link #computeverticalscrollrange()} and {@link #computeverticalscrolloffset()}.</p> 
* 
* <p>default implementation returns 0.</p> 
* 
* <p>if you want to support scroll bars, override 
* {@link recyclerview.layoutmanager#computeverticalscrollextent(recyclerview.state)} in your 
* layoutmanager.</p> 
* 
* @return the vertical extent of the scrollbar's thumb 
* @see recyclerview.layoutmanager#computeverticalscrollextent(recyclerview.state) 
*/ 
@override 
protected int computeverticalscrollextent() { 
return mlayout.canscrollvertically() ? mlayout.computeverticalscrollextent(mstate) : 0; 
} 
/** 
* <p>compute the vertical range that the vertical scrollbar represents.</p> 
* 
* <p>the range is expressed in arbitrary units that must be the same as the units used by 
* {@link #computeverticalscrollextent()} and {@link #computeverticalscrolloffset()}.</p> 
* 
* <p>default implementation returns 0.</p> 
* 
* <p>if you want to support scroll bars, override 
* {@link recyclerview.layoutmanager#computeverticalscrollrange(recyclerview.state)} in your 
* layoutmanager.</p> 
* 
* @return the total vertical range represented by the vertical scrollbar 
* @see recyclerview.layoutmanager#computeverticalscrollrange(recyclerview.state) 
*/ 
@override 
protected int computeverticalscrollrange() { 
return mlayout.canscrollvertically() ? mlayout.computeverticalscrollrange(mstate) : 0; 
} 
void eatrequestlayout() { 
if (!meatrequestlayout) { 
meatrequestlayout = true; 
mlayoutrequesteaten = false; 
} 
} 
void resumerequestlayout(boolean performlayoutchildren) { 
if (meatrequestlayout) { 
if (performlayoutchildren && mlayoutrequesteaten && 
mlayout != null && madapter != null) { 
dispatchlayout(); 
} 
meatrequestlayout = false; 
mlayoutrequesteaten = false; 
} 
} 
/** 
* animate a scroll by the given amount of pixels along either axis. 
* 
* @param dx pixels to scroll horizontally 
* @param dy pixels to scroll vertically 
*/ 
public void smoothscrollby(int dx, int dy) { 
if (dx != 0 || dy != 0) { 
mviewflinger.smoothscrollby(dx, dy); 
} 
} 
/** 
* begin a standard fling with an initial velocity along each axis in pixels per second. 
* if the velocity given is below the system-defined minimum this method will return false 
* and no fling will occur. 
* 
* @param velocityx initial horizontal velocity in pixels per second 
* @param velocityy initial vertical velocity in pixels per second 
* @return true if the fling was started, false if the velocity was too low to fling 
*/ 
public boolean fling(int velocityx, int velocityy) { 
if (math.abs(velocityx) < mminflingvelocity) { 
velocityx = 0; 
} 
if (math.abs(velocityy) < mminflingvelocity) { 
velocityy = 0; 
} 
velocityx = math.max(-mmaxflingvelocity, math.min(velocityx, mmaxflingvelocity)); 
velocityy = math.max(-mmaxflingvelocity, math.min(velocityy, mmaxflingvelocity)); 
if (velocityx != 0 || velocityy != 0) { 
mviewflinger.fling(velocityx, velocityy); 
return true; 
} 
return false; 
} 
/** 
* stop any current scroll in progress, such as one started by 
* {@link #smoothscrollby(int, int)}, {@link #fling(int, int)} or a touch-initiated fling. 
*/ 
public void stopscroll() { 
mviewflinger.stop(); 
mlayout.stopsmoothscroller(); 
} 
/** 
* apply a pull to relevant overscroll glow effects 
*/ 
private void pullglows(int overscrollx, int overscrolly) { 
if (overscrollx < 0) { 
if (mleftglow == null) { 
mleftglow = new edgeeffectcompat(getcontext()); 
mleftglow.setsize(getmeasuredheight() - getpaddingtop() - getpaddingbottom(), 
getmeasuredwidth() - getpaddingleft() - getpaddingright()); 
} 
mleftglow.onpull(-overscrollx / (float) getwidth()); 
} else if (overscrollx > 0) { 
if (mrightglow == null) { 
mrightglow = new edgeeffectcompat(getcontext()); 
mrightglow.setsize(getmeasuredheight() - getpaddingtop() - getpaddingbottom(), 
getmeasuredwidth() - getpaddingleft() - getpaddingright()); 
} 
mrightglow.onpull(overscrollx / (float) getwidth()); 
} 
if (overscrolly < 0) { 
if (mtopglow == null) { 
mtopglow = new edgeeffectcompat(getcontext()); 
mtopglow.setsize(getmeasuredwidth() - getpaddingleft() - getpaddingright(), 
getmeasuredheight() - getpaddingtop() - getpaddingbottom()); 
} 
mtopglow.onpull(-overscrolly / (float) getheight()); 
} else if (overscrolly > 0) { 
if (mbottomglow == null) { 
mbottomglow = new edgeeffectcompat(getcontext()); 
mbottomglow.setsize(getmeasuredwidth() - getpaddingleft() - getpaddingright(), 
getmeasuredheight() - getpaddingtop() - getpaddingbottom()); 
} 
mbottomglow.onpull(overscrolly / (float) getheight()); 
} 
if (overscrollx != 0 || overscrolly != 0) { 
viewcompat.postinvalidateonanimation(this); 
} 
} 
private void releaseglows() { 
boolean needsinvalidate = false; 
if (mleftglow != null) needsinvalidate = mleftglow.onrelease(); 
if (mtopglow != null) needsinvalidate |= mtopglow.onrelease(); 
if (mrightglow != null) needsinvalidate |= mrightglow.onrelease(); 
if (mbottomglow != null) needsinvalidate |= mbottomglow.onrelease(); 
if (needsinvalidate) { 
viewcompat.postinvalidateonanimation(this); 
} 
} 
void absorbglows(int velocityx, int velocityy) { 
if (velocityx < 0) { 
if (mleftglow == null) { 
mleftglow = new edgeeffectcompat(getcontext()); 
mleftglow.setsize(getmeasuredheight() - getpaddingtop() - getpaddingbottom(), 
getmeasuredwidth() - getpaddingleft() - getpaddingright()); 
} 
mleftglow.onabsorb(-velocityx); 
} else if (velocityx > 0) { 
if (mrightglow == null) { 
mrightglow = new edgeeffectcompat(getcontext()); 
mrightglow.setsize(getmeasuredheight() - getpaddingtop() - getpaddingbottom(), 
getmeasuredwidth() - getpaddingleft() - getpaddingright()); 
} 
mrightglow.onabsorb(velocityx); 
} 
if (velocityy < 0) { 
if (mtopglow == null) { 
mtopglow = new edgeeffectcompat(getcontext()); 
mtopglow.setsize(getmeasuredwidth() - getpaddingleft() - getpaddingright(), 
getmeasuredheight() - getpaddingtop() - getpaddingbottom()); 
} 
mtopglow.onabsorb(-velocityy); 
} else if (velocityy > 0) { 
if (mbottomglow == null) { 
mbottomglow = new edgeeffectcompat(getcontext()); 
mbottomglow.setsize(getmeasuredwidth() - getpaddingleft() - getpaddingright(), 
getmeasuredheight() - getpaddingtop() - getpaddingbottom()); 
} 
mbottomglow.onabsorb(velocityy); 
} 
if (velocityx != 0 || velocityy != 0) { 
viewcompat.postinvalidateonanimation(this); 
} 
} 
// focus handling 
@override 
public view focussearch(view focused, int direction) { 
view result = mlayout.oninterceptfocussearch(focused, direction); 
if (result != null) { 
return result; 
} 
final focusfinder ff = focusfinder.getinstance(); 
result = ff.findnextfocus(this, focused, direction); 
if (result == null && madapter != null) { 
eatrequestlayout(); 
result = mlayout.onfocussearchfailed(focused, direction, mrecycler, mstate); 
resumerequestlayout(false); 
} 
return result != null ? result : super.focussearch(focused, direction); 
} 
@override 
public void requestchildfocus(view child, view focused) { 
if (!mlayout.onrequestchildfocus(this, child, focused)) { 
mtemprect.set(0, 0, focused.getwidth(), focused.getheight()); 
offsetdescendantrecttomycoords(focused, mtemprect); 
offsetrectintodescendantcoords(child, mtemprect); 
requestchildrectangleonscreen(child, mtemprect, !mfirstlayoutcomplete); 
} 
super.requestchildfocus(child, focused); 
} 
@override 
public boolean requestchildrectangleonscreen(view child, rect rect, boolean immediate) { 
return mlayout.requestchildrectangleonscreen(this, child, rect, immediate); 
} 
@override 
public void addfocusables(arraylist<view> views, int direction, int focusablemode) { 
if (!mlayout.onaddfocusables(this, views, direction, focusablemode)) { 
super.addfocusables(views, direction, focusablemode); 
} 
} 
@override 
protected void onattachedtowindow() { 
super.onattachedtowindow(); 
misattached = true; 
mfirstlayoutcomplete = false; 
if (mlayout != null) { 
mlayout.onattachedtowindow(this); 
} 
mpostedanimatorrunner = false; 
} 
@override 
protected void ondetachedfromwindow() { 
super.ondetachedfromwindow(); 
mfirstlayoutcomplete = false; 
stopscroll(); 
// todo mark what our target position was if relevant, then we can jump there 
// on reattach. 
misattached = false; 
if (mlayout != null) { 
mlayout.ondetachedfromwindow(this); 
} 
removecallbacks(mitemanimatorrunner); 
} 
/** 
* add an {@link onitemtouchlistener} to intercept touch events before they are dispatched 
* to child views or this view's standard scrolling behavior. 
* 
* <p>client code may use listeners to implement item manipulation behavior. once a listener 
* returns true from 
* {@link onitemtouchlistener#onintercepttouchevent(recyclerview, motionevent)} its 
* {@link onitemtouchlistener#ontouchevent(recyclerview, motionevent)} method will be called 
* for each incoming motionevent until the end of the gesture.</p> 
* 
* @param listener listener to add 
*/ 
public void addonitemtouchlistener(onitemtouchlistener listener) { 
monitemtouchlisteners.add(listener); 
} 
/** 
* remove an {@link onitemtouchlistener}. it will no longer be able to intercept touch events. 
* 
* @param listener listener to remove 
*/ 
public void removeonitemtouchlistener(onitemtouchlistener listener) { 
monitemtouchlisteners.remove(listener); 
if (mactiveonitemtouchlistener == listener) { 
mactiveonitemtouchlistener = null; 
} 
} 
private boolean dispatchonitemtouchintercept(motionevent e) { 
final int action = e.getaction(); 
if (action == motionevent.action_cancel || action == motionevent.action_down) { 
mactiveonitemtouchlistener = null; 
} 
final int listenercount = monitemtouchlisteners.size(); 
for (int i = 0; i < listenercount; i++) { 
final onitemtouchlistener listener = monitemtouchlisteners.get(i); 
if (listener.onintercepttouchevent(this, e) && action != motionevent.action_cancel) { 
mactiveonitemtouchlistener = listener; 
return true; 
} 
} 
return false; 
} 
private boolean dispatchonitemtouch(motionevent e) { 
final int action = e.getaction(); 
if (mactiveonitemtouchlistener != null) { 
if (action == motionevent.action_down) { 
// stale state from a previous gesture, we're starting a new one. clear it. 
mactiveonitemtouchlistener = null; 
} else { 
mactiveonitemtouchlistener.ontouchevent(this, e); 
if (action == motionevent.action_cancel || action == motionevent.action_up) { 
// clean up for the next gesture. 
mactiveonitemtouchlistener = null; 
} 
return true; 
} 
} 
// listeners will have already received the action_down via dispatchonitemtouchintercept 
// as called from onintercepttouchevent; skip it. 
if (action != motionevent.action_down) { 
final int listenercount = monitemtouchlisteners.size(); 
for (int i = 0; i < listenercount; i++) { 
final onitemtouchlistener listener = monitemtouchlisteners.get(i); 
if (listener.onintercepttouchevent(this, e)) { 
mactiveonitemtouchlistener = listener; 
return true; 
} 
} 
} 
return false; 
} 
@override 
public boolean onintercepttouchevent(motionevent e) { 
if (dispatchonitemtouchintercept(e)) { 
canceltouch(); 
return true; 
} 
final boolean canscrollhorizontally = mlayout.canscrollhorizontally(); 
final boolean canscrollvertically = mlayout.canscrollvertically(); 
if (mvelocitytracker == null) { 
mvelocitytracker = velocitytracker.obtain(); 
} 
mvelocitytracker.addmovement(e); 
final int action = motioneventcompat.getactionmasked(e); 
final int actionindex = motioneventcompat.getactionindex(e); 
switch (action) { 
case motionevent.action_down: 
mscrollpointerid = motioneventcompat.getpointerid(e, 0); 
minitialtouchx = mlasttouchx = (int) (e.getx() + 0.5f); 
minitialtouchy = mlasttouchy = (int) (e.gety() + 0.5f); 
if (mscrollstate == scroll_state_settling) { 
getparent().requestdisallowintercepttouchevent(true); 
setscrollstate(scroll_state_dragging); 
} 
break; 
case motioneventcompat.action_pointer_down: 
mscrollpointerid = motioneventcompat.getpointerid(e, actionindex); 
minitialtouchx = mlasttouchx = (int) (motioneventcompat.getx(e, actionindex) + 0.5f); 
minitialtouchy = mlasttouchy = (int) (motioneventcompat.gety(e, actionindex) + 0.5f); 
break; 
case motionevent.action_move: { 
final int index = motioneventcompat.findpointerindex(e, mscrollpointerid); 
if (index < 0) { 
log.e(tag, "error processing scroll; pointer index for id " + 
mscrollpointerid + " not found. did any motionevents get skipped?"); 
return false; 
} 
final int x = (int) (motioneventcompat.getx(e, index) + 0.5f); 
final int y = (int) (motioneventcompat.gety(e, index) + 0.5f); 
if (mscrollstate != scroll_state_dragging) { 
final int dx = x - minitialtouchx; 
final int dy = y - minitialtouchy; 
boolean startscroll = false; 
if (canscrollhorizontally && math.abs(dx) > mtouchslop) { 
mlasttouchx = minitialtouchx + mtouchslop * (dx < 0 ? -1 : 1); 
startscroll = true; 
} 
if (canscrollvertically && math.abs(dy) > mtouchslop) { 
mlasttouchy = minitialtouchy + mtouchslop * (dy < 0 ? -1 : 1); 
startscroll = true; 
} 
if (startscroll) { 
getparent().requestdisallowintercepttouchevent(true); 
setscrollstate(scroll_state_dragging); 
} 
} 
} break; 
case motioneventcompat.action_pointer_up: { 
onpointerup(e); 
} break; 
case motionevent.action_up: { 
mvelocitytracker.clear(); 
} break; 
case motionevent.action_cancel: { 
canceltouch(); 
} 
} 
return mscrollstate == scroll_state_dragging; 
} 
@override 
public boolean ontouchevent(motionevent e) { 
if (dispatchonitemtouch(e)) { 
canceltouch(); 
return true; 
} 
final boolean canscrollhorizontally = mlayout.canscrollhorizontally(); 
final boolean canscrollvertically = mlayout.canscrollvertically(); 
if (mvelocitytracker == null) { 
mvelocitytracker = velocitytracker.obtain(); 
} 
mvelocitytracker.addmovement(e); 
final int action = motioneventcompat.getactionmasked(e); 
final int actionindex = motioneventcompat.getactionindex(e); 
switch (action) { 
case motionevent.action_down: { 
mscrollpointerid = motioneventcompat.getpointerid(e, 0); 
minitialtouchx = mlasttouchx = (int) (e.getx() + 0.5f); 
minitialtouchy = mlasttouchy = (int) (e.gety() + 0.5f); 
} break; 
case motioneventcompat.action_pointer_down: { 
mscrollpointerid = motioneventcompat.getpointerid(e, actionindex); 
minitialtouchx = mlasttouchx = (int) (motioneventcompat.getx(e, actionindex) + 0.5f); 
minitialtouchy = mlasttouchy = (int) (motioneventcompat.gety(e, actionindex) + 0.5f); 
} break; 
case motionevent.action_move: { 
final int index = motioneventcompat.findpointerindex(e, mscrollpointerid); 
if (index < 0) { 
log.e(tag, "error processing scroll; pointer index for id " + 
mscrollpointerid + " not found. did any motionevents get skipped?"); 
return false; 
} 
final int x = (int) (motioneventcompat.getx(e, index) + 0.5f); 
final int y = (int) (motioneventcompat.gety(e, index) + 0.5f); 
if (mscrollstate != scroll_state_dragging) { 
final int dx = x - minitialtouchx; 
final int dy = y - minitialtouchy; 
boolean startscroll = false; 
if (canscrollhorizontally && math.abs(dx) > mtouchslop) { 
mlasttouchx = minitialtouchx + mtouchslop * (dx < 0 ? -1 : 1); 
startscroll = true; 
} 
if (canscrollvertically && math.abs(dy) > mtouchslop) { 
mlasttouchy = minitialtouchy + mtouchslop * (dy < 0 ? -1 : 1); 
startscroll = true; 
} 
if (startscroll) { 
getparent().requestdisallowintercepttouchevent(true); 
setscrollstate(scroll_state_dragging); 
} 
} 
if (mscrollstate == scroll_state_dragging) { 
final int dx = x - mlasttouchx; 
final int dy = y - mlasttouchy; 
scrollbyinternal(canscrollhorizontally ? -dx : 0, 
canscrollvertically ? -dy : 0); 
} 
mlasttouchx = x; 
mlasttouchy = y; 
} break; 
case motioneventcompat.action_pointer_up: { 
onpointerup(e); 
} break; 
case motionevent.action_up: { 
mvelocitytracker.computecurrentvelocity(1000, mmaxflingvelocity); 
final float xvel = canscrollhorizontally ? 
-velocitytrackercompat.getxvelocity(mvelocitytracker, mscrollpointerid) : 0; 
final float yvel = canscrollvertically ? 
-velocitytrackercompat.getyvelocity(mvelocitytracker, mscrollpointerid) : 0; 
if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) { 
setscrollstate(scroll_state_idle); 
} 
mvelocitytracker.clear(); 
releaseglows(); 
} break; 
case motionevent.action_cancel: { 
canceltouch(); 
} break; 
} 
return true; 
} 
private void canceltouch() { 
mvelocitytracker.clear(); 
releaseglows(); 
setscrollstate(scroll_state_idle); 
} 
private void onpointerup(motionevent e) { 
final int actionindex = motioneventcompat.getactionindex(e); 
if (motioneventcompat.getpointerid(e, actionindex) == mscrollpointerid) { 
// pick a new pointer to pick up the slack. 
final int newindex = actionindex == 0 ? 1 : 0; 
mscrollpointerid = motioneventcompat.getpointerid(e, newindex); 
minitialtouchx = mlasttouchx = (int) (motioneventcompat.getx(e, newindex) + 0.5f); 
minitialtouchy = mlasttouchy = (int) (motioneventcompat.gety(e, newindex) + 0.5f); 
} 
} 
@override 
protected void onmeasure(int widthspec, int heightspec) { 
if (madapterupdateduringmeasure) { 
eatrequestlayout(); 
updatechildviews(); 
madapterupdateduringmeasure = false; 
resumerequestlayout(false); 
} 
if (madapter != null) { 
mstate.mitemcount = madapter.getitemcount(); 
} 
mlayout.onmeasure(mrecycler, mstate, widthspec, heightspec); 
final int widthsize = getmeasuredwidth(); 
final int heightsize = getmeasuredheight(); 
if (mleftglow != null) mleftglow.setsize(heightsize, widthsize); 
if (mtopglow != null) mtopglow.setsize(widthsize, heightsize); 
if (mrightglow != null) mrightglow.setsize(heightsize, widthsize); 
if (mbottomglow != null) mbottomglow.setsize(widthsize, heightsize); 
} 
/** 
* 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.setlistener(null); 
} 
mitemanimator = animator; 
if (mitemanimator != null) { 
mitemanimator.setlistener(mitemanimatorlistener); 
} 
} 
/** 
* gets the current itemanimator for this recyclerview. a null return value 
* indicates that there is no animator and that item changes will happen without 
* any animations. by default, recyclerview instantiates and 
* uses an instance of {@link defaultitemanimator}. 
* 
* @return itemanimator the current itemanimator. if null, no animations will occur 
* when changes occur to the items in this recyclerview. 
*/ 
public itemanimator getitemanimator() { 
return mitemanimator; 
} 
/** 
* post a runnable to the next frame to run pending item animations. only the first such 
* request will be posted, governed by the mpostedanimatorrunner flag. 
*/ 
private void postanimationrunner() { 
if (!mpostedanimatorrunner && misattached) { 
viewcompat.postonanimation(this, mitemanimatorrunner); 
mpostedanimatorrunner = true; 
} 
} 
private boolean predictiveitemanimationsenabled() { 
return (mitemanimator != null && mlayout.supportspredictiveitemanimations()); 
} 
/** 
* wrapper around layoutchildren() that handles animating changes caused by layout. 
* animations work on the assumption that there are five different kinds of items 
* in play: 
* persistent: items are visible before and after layout 
* removed: items were visible before layout and were removed by the app 
* added: items did not exist before layout and were added by the app 
* disappearing: items exist in the data set before/after, but changed from 
* visible to non-visible in the process of layout (they were moved off 
* screen as a side-effect of other changes) 
* appearing: items exist in the data set before/after, but changed from 
* non-visible to visible in the process of layout (they were moved on 
* screen as a side-effect of other changes) 
* the overall approach figures out what items exist before/after layout and 
* infers one of the five above states for each of the items. then the animations 
* are set up accordingly: 
* persistent views are moved ({@link itemanimator#animatemove(viewholder, int, int, int, int)}) 
* removed views are removed ({@link itemanimator#animateremove(viewholder)}) 
* added views are added ({@link itemanimator#animateadd(viewholder)}) 
* disappearing views are moved off screen 
* appearing views are moved on screen 
*/ 
void dispatchlayout() { 
if (madapter == null) { 
log.e(tag, "no adapter attached; skipping layout"); 
return; 
} 
eatrequestlayout(); 
// simple animations are a subset of advanced animations (which will cause a 
// prelayout step) 
boolean animatechangessimple = mitemanimator != null && mitemsaddedorremoved 
&& !mitemschanged; 
final boolean animatechangesadvanced = enable_predictive_animations && 
animatechangessimple && predictiveitemanimationsenabled(); 
mitemsaddedorremoved = mitemschanged = false; 
arraymap<view, rect> appearingviewinitialbounds = null; 
mstate.minprelayout = animatechangesadvanced; 
mstate.mitemcount = madapter.getitemcount(); 
if (animatechangessimple) { 
// step 0: find out where all non-removed items are, pre-layout 
mstate.mprelayoutholdermap.clear(); 
mstate.mpostlayoutholdermap.clear(); 
int count = getchildcount(); 
for (int i = 0; i < count; ++i) { 
final viewholder holder = getchildviewholderint(getchildat(i)); 
final view view = holder.itemview; 
mstate.mprelayoutholdermap.put(holder, new itemholderinfo(holder, 
view.getleft(), view.gettop(), view.getright(), view.getbottom(), 
holder.mposition)); 
} 
} 
if (animatechangesadvanced) { 
// step 1: run prelayout: this will use the old positions of items. the layout manager 
// is expected to layout everything, even removed items (though not to add removed 
// items back to the container). this gives the pre-layout position of appearing views 
// which come into existence as part of the real layout. 
minprelayout = true; 
final boolean didstructurechange = mstate.mstructurechanged; 
mstate.mstructurechanged = false; 
// temporarily disable flag because we are asking for previous layout 
mlayout.onlayoutchildren(mrecycler, mstate); 
mstate.mstructurechanged = didstructurechange; 
minprelayout = false; 
appearingviewinitialbounds = new arraymap<view, rect>(); 
for (int i = 0; i < getchildcount(); ++i) { 
boolean found = false; 
view child = getchildat(i); 
for (int j = 0; j < mstate.mprelayoutholdermap.size(); ++j) { 
viewholder holder = mstate.mprelayoutholdermap.keyat(j); 
if (holder.itemview == child) { 
found = true; 
continue; 
} 
} 
if (!found) { 
appearingviewinitialbounds.put(child, new rect(child.getleft(), child.gettop(), 
child.getright(), child.getbottom())); 
} 
} 
} 
clearoldpositions(); 
dispatchlayoutupdates(); 
mstate.mitemcount = madapter.getitemcount(); 
// step 2: run layout 
mstate.minprelayout = false; 
mlayout.onlayoutchildren(mrecycler, mstate); 
mstate.mstructurechanged = false; 
mpendingsavedstate = null; 
// onlayoutchildren may have caused client code to disable item animations; re-check 
animatechangessimple = animatechangessimple && mitemanimator != null; 
if (animatechangessimple) { 
// step 3: find out where things are now, post-layout 
int count = getchildcount(); 
for (int i = 0; i < count; ++i) { 
viewholder holder = getchildviewholderint(getchildat(i)); 
final view view = holder.itemview; 
mstate.mpostlayoutholdermap.put(holder, new itemholderinfo(holder, 
view.getleft(), view.gettop(), view.getright(), view.getbottom(), 
holder.mposition)); 
} 
// step 4: animate disappearing and removed items 
int prelayoutcount = mstate.mprelayoutholdermap.size(); 
for (int i = prelayoutcount - 1; i >= 0; i--) { 
viewholder itemholder = mstate.mprelayoutholdermap.keyat(i); 
if (!mstate.mpostlayoutholdermap.containskey(itemholder)) { 
itemholderinfo disappearingitem = mstate.mprelayoutholdermap.valueat(i); 
mstate.mprelayoutholdermap.removeat(i); 
view disappearingitemview = disappearingitem.holder.itemview; 
removedetachedview(disappearingitemview, false); 
mrecycler.unscrapview(disappearingitem.holder); 
animatedisappearance(disappearingitem); 
} 
} 
// step 5: animate appearing and added items 
int postlayoutcount = mstate.mpostlayoutholdermap.size(); 
if (postlayoutcount > 0) { 
for (int i = postlayoutcount - 1; i >= 0; i--) { 
viewholder itemholder = mstate.mpostlayoutholdermap.keyat(i); 
itemholderinfo info = mstate.mpostlayoutholdermap.valueat(i); 
if ((mstate.mprelayoutholdermap.isempty() || 
!mstate.mprelayoutholdermap.containskey(itemholder))) { 
mstate.mpostlayoutholdermap.removeat(i); 
rect initialbounds = (appearingviewinitialbounds != null) ? 
appearingviewinitialbounds.get(itemholder.itemview) : null; 
animateappearance(itemholder, initialbounds, 
info.left, info.top); 
} 
} 
} 
// step 6: animate persistent items 
count = mstate.mpostlayoutholdermap.size(); 
for (int i = 0; i < count; ++i) { 
viewholder postholder = mstate.mpostlayoutholdermap.keyat(i); 
itemholderinfo postinfo = mstate.mpostlayoutholdermap.valueat(i); 
itemholderinfo preinfo = mstate.mprelayoutholdermap.get(postholder); 
if (preinfo != null && postinfo != null) { 
if (preinfo.left != postinfo.left || preinfo.top != postinfo.top) { 
postholder.setisrecyclable(false); 
if (debug) { 
log.d(tag, "persistent: " + postholder + 
" with view " + postholder.itemview); 
} 
if (mitemanimator.animatemove(postholder, 
preinfo.left, preinfo.top, postinfo.left, postinfo.top)) { 
postanimationrunner(); 
} 
} 
} 
} 
} 
resumerequestlayout(false); 
mlayout.removeandrecyclescrapint(mrecycler, !animatechangesadvanced); 
mstate.mpreviouslayoutitemcount = mstate.mitemcount; 
mstate.mdeletedinvisibleitemcountsincepreviouslayout = 0; 
} 
private void animateappearance(viewholder itemholder, rect beforebounds, int afterleft, 
int aftertop) { 
view newitemview = itemholder.itemview; 
if (beforebounds != null && 
(beforebounds.left != afterleft || beforebounds.top != aftertop)) { 
// slide items in if before/after locations differ 
itemholder.setisrecyclable(false); 
if (debug) { 
log.d(tag, "appearing: " + itemholder + " with view " + newitemview); 
} 
if (mitemanimator.animatemove(itemholder, 
beforebounds.left, beforebounds.top, 
afterleft, aftertop)) { 
postanimationrunner(); 
} 
} else { 
if (debug) { 
log.d(tag, "added: " + itemholder + " with view " + newitemview); 
} 
itemholder.setisrecyclable(false); 
if (mitemanimator.animateadd(itemholder)) { 
postanimationrunner(); 
} 
} 
} 
private void animatedisappearance(itemholderinfo disappearingitem) { 
view disappearingitemview = disappearingitem.holder.itemview; 
addanimatingview(disappearingitemview); 
int oldleft = disappearingitem.left; 
int oldtop = disappearingitem.top; 
int newleft = disappearingitemview.getleft(); 
int newtop = disappearingitemview.gettop(); 
if (oldleft != newleft || oldtop != newtop) { 
disappearingitem.holder.setisrecyclable(false); 
disappearingitemview.layout(newleft, newtop, 
newleft + disappearingitemview.getwidth(), 
newtop + disappearingitemview.getheight()); 
if (debug) { 
log.d(tag, "disappearing: " + disappearingitem.holder + 
" with view " + disappearingitemview); 
} 
if (mitemanimator.animatemove(disappearingitem.holder, oldleft, oldtop, 
newleft, newtop)) { 
postanimationrunner(); 
} 
} else { 
if (debug) { 
log.d(tag, "removed: " + disappearingitem.holder + 
" with view " + disappearingitemview); 
} 
disappearingitem.holder.setisrecyclable(false); 
if (mitemanimator.animateremove(disappearingitem.holder)) { 
postanimationrunner(); 
} 
} 
} 
@override 
protected void onlayout(boolean changed, int l, int t, int r, int b) { 
eatrequestlayout(); 
dispatchlayout(); 
resumerequestlayout(false); 
mfirstlayoutcomplete = true; 
} 
@override 
public void requestlayout() { 
if (!meatrequestlayout) { 
super.requestlayout(); 
} else { 
mlayoutrequesteaten = true; 
} 
} 
void markitemdecorinsetsdirty() { 
final int childcount = getchildcount(); 
for (int i = 0; i < childcount; i++) { 
final view child = getchildat(i); 
((layoutparams) child.getlayoutparams()).minsetsdirty = true; 
} 
} 
@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); 
} 
boolean needsinvalidate = false; 
if (mleftglow != null && !mleftglow.isfinished()) { 
final int restore = c.save(); 
c.rotate(270); 
c.translate(-getheight() + getpaddingtop(), 0); 
needsinvalidate = mleftglow != null && mleftglow.draw(c); 
c.restoretocount(restore); 
} 
if (mtopglow != null && !mtopglow.isfinished()) { 
c.translate(getpaddingleft(), getpaddingtop()); 
needsinvalidate |= mtopglow != null && mtopglow.draw(c); 
} 
if (mrightglow != null && !mrightglow.isfinished()) { 
final int restore = c.save(); 
final int width = getwidth(); 
c.rotate(90); 
c.translate(-getpaddingtop(), -width); 
needsinvalidate |= mrightglow != null && mrightglow.draw(c); 
c.restoretocount(restore); 
} 
if (mbottomglow != null && !mbottomglow.isfinished()) { 
final int restore = c.save(); 
c.rotate(180); 
c.translate(-getwidth() + getpaddingleft(), -getheight() + getpaddingtop()); 
needsinvalidate |= mbottomglow != null && mbottomglow.draw(c); 
c.restoretocount(restore); 
} 
if (needsinvalidate) { 
viewcompat.postinvalidateonanimation(this); 
} 
} 
@override 
public void ondraw(canvas c) { 
super.ondraw(c); 
final int count = mitemdecorations.size(); 
for (int i = 0; i < count; i++) { 
mitemdecorations.get(i).ondraw(c, this); 
} 
} 
@override 
protected boolean checklayoutparams(viewgroup.layoutparams p) { 
return p instanceof layoutparams && mlayout.checklayoutparams((layoutparams) p); 
} 
@override 
protected viewgroup.layoutparams generatedefaultlayoutparams() { 
if (mlayout == null) { 
throw new illegalstateexception("recyclerview has no layoutmanager"); 
} 
return mlayout.generatedefaultlayoutparams(); 
} 
@override 
public viewgroup.layoutparams generatelayoutparams(attributeset attrs) { 
if (mlayout == null) { 
throw new illegalstateexception("recyclerview has no layoutmanager"); 
} 
return mlayout.generatelayoutparams(getcontext(), attrs); 
} 
@override 
protected viewgroup.layoutparams generatelayoutparams(viewgroup.layoutparams p) { 
if (mlayout == null) { 
throw new illegalstateexception("recyclerview has no layoutmanager"); 
} 
return mlayout.generatelayoutparams(p); 
} 
private int findpositionoffset(int position) { 
int offset = 0; 
int count = mpendinglayoutupdates.size(); 
for (int i = 0; i < count; ++i) { 
updateop op = mpendinglayoutupdates.get(i); 
if (op.positionstart <= position) { 
if (op.cmd == updateop.remove) { 
offset -= op.itemcount; 
} else if (op.cmd == updateop.add) { 
offset += op.itemcount; 
} 
} 
} 
return position + offset; 
} 
void dispatchlayoutupdates() { 
final int opcount = mpendinglayoutupdates.size(); 
for (int i = 0; i < opcount; i++) { 
final updateop op = mpendinglayoutupdates.get(i); 
switch (op.cmd) { 
case updateop.add: 
mlayout.onitemsadded(this, op.positionstart, op.itemcount); 
break; 
case updateop.remove: 
mlayout.onitemsremoved(this, op.positionstart, op.itemcount); 
break; 
case updateop.update: 
// todo: tell the layout manager 
break; 
} 
recycleupdateop(op); 
} 
mpendinglayoutupdates.clear(); 
} 
void updatechildviews() { 
final int opcount = mpendingupdates.size(); 
for (int i = 0; i < opcount; i++) { 
final updateop op = mpendingupdates.get(i); 
switch (op.cmd) { 
case updateop.add: 
if (debug) { 
log.d(tag, "updateop.add start=" + op.positionstart + " count=" + 
op.itemcount); 
} 
offsetpositionrecordsforinsert(op.positionstart, op.itemcount); 
mitemsaddedorremoved = true; 
break; 
case updateop.remove: 
if (debug) { 
log.d(tag, "updateop.remove start=" + op.positionstart + " count=" + 
op.itemcount); 
} 
for (int j = 0; j < op.itemcount; ++j) { 
viewholder holder = findviewholderforposition(op.positionstart + j, true); 
if (holder != null) { 
holder.setisrecyclable(false); 
} else { 
mstate.mdeletedinvisibleitemcountsincepreviouslayout ++; 
} 
} 
offsetpositionrecordsforremove(op.positionstart, op.itemcount); 
mitemsaddedorremoved = true; 
break; 
case updateop.update: 
if (debug) { 
log.d(tag, "updateop.update start=" + op.positionstart + " count=" + 
op.itemcount); 
} 
viewrangeupdate(op.positionstart, op.itemcount); 
mitemschanged = true; 
break; 
} 
mpendinglayoutupdates.add(op); 
// todo: recycle the op if no animator (also don't bother stashing in pending layout updates?) 
} 
mpendingupdates.clear(); 
} 
void clearoldpositions() { 
final int childcount = getchildcount(); 
for (int i = 0; i < childcount; i++) { 
final viewholder holder = getchildviewholderint(getchildat(i)); 
holder.clearoldposition(); 
} 
mrecycler.clearoldpositions(); 
} 
void offsetpositionrecordsforinsert(int positionstart, int itemcount) { 
final int childcount = getchildcount(); 
for (int i = 0; i < childcount; i++) { 
final viewholder holder = getchildviewholderint(getchildat(i)); 
if (holder != null && holder.mposition >= positionstart) { 
if (debug) { 
log.d(tag, "offsetpositionrecordsforinsert attached child " + i + " holder " + 
holder + " now at position " + (holder.mposition + itemcount)); 
} 
holder.offsetposition(itemcount); 
mstate.mstructurechanged = true; 
} 
} 
mrecycler.offsetpositionrecordsforinsert(positionstart, itemcount); 
requestlayout(); 
} 
void offsetpositionrecordsforremove(int positionstart, int itemcount) { 
final int positionend = positionstart + itemcount; 
final int childcount = getchildcount(); 
for (int i = 0; i < childcount; i++) { 
final viewholder holder = getchildviewholderint(getchildat(i)); 
if (holder != null) { 
if (holder.mposition >= positionend) { 
if (debug) { 
log.d(tag, "offsetpositionrecordsforremove attached child " + i + 
" holder " + holder + " now at position " + 
(holder.mposition - itemcount)); 
} 
holder.offsetposition(-itemcount); 
mstate.mstructurechanged = true; 
} else if (holder.mposition >= positionstart) { 
if (debug) { 
log.d(tag, "offsetpositionrecordsforremove attached child " + i + 
" holder " + holder + " now removed"); 
} 
holder.addflags(viewholder.flag_removed); 
mstate.mstructurechanged = true; 
} 
} 
} 
mrecycler.offsetpositionrecordsforremove(positionstart, itemcount); 
requestlayout(); 
} 
/** 
* rebind existing views for the given range, or create as needed. 
* 
* @param positionstart adapter position to start at 
* @param itemcount number of views that must explicitly be rebound 
*/ 
void viewrangeupdate(int positionstart, int itemcount) { 
final int childcount = getchildcount(); 
final int positionend = positionstart + itemcount; 
for (int i = 0; i < childcount; i++) { 
final viewholder holder = getchildviewholderint(getchildat(i)); 
if (holder == null) { 
continue; 
} 
final int position = holder.getposition(); 
if (position >= positionstart && position < positionend) { 
holder.addflags(viewholder.flag_update); 
// binding an attached view will request a layout if needed. 
madapter.bindviewholder(holder, holder.getposition()); 
} 
} 
mrecycler.viewrangeupdate(positionstart, itemcount); 
} 
/** 
* mark all known views as invalid. used in response to a, "the whole world might have changed" 
* data change event. 
*/ 
void markknownviewsinvalid() { 
final int childcount = getchildcount(); 
for (int i = 0; i < childcount; i++) { 
final viewholder holder = getchildviewholderint(getchildat(i)); 
if (holder != null) { 
holder.addflags(viewholder.flag_update | viewholder.flag_invalid); 
} 
} 
mrecycler.markknownviewsinvalid(); 
} 
/** 
* schedule an update of data from the adapter to occur on the next frame. 
* on newer platform versions this happens via the postonanimation mechanism and recyclerview 
* attempts to avoid relayouts if possible. 
* on older platform versions the recyclerview requests a layout the same way listview does. 
*/ 
void postadapterupdate(updateop op) { 
mpendingupdates.add(op); 
if (mpendingupdates.size() == 1) { 
if (mpostupdatesonanimation && mhasfixedsize && misattached) { 
viewcompat.postonanimation(this, mupdatechildviewsrunnable); 
} else { 
madapterupdateduringmeasure = true; 
requestlayout(); 
} 
} 
} 
/** 
* retrieve the {@link viewholder} for the given child view. 
* 
* @param child child of this recyclerview to query for its viewholder 
* @return the child view's viewholder 
*/ 
public viewholder getchildviewholder(view child) { 
final viewparent parent = child.getparent(); 
if (parent != null && parent != this) { 
throw new illegalargumentexception("view " + child + " is not a direct child of " + 
this); 
} 
return getchildviewholderint(child); 
} 
static viewholder getchildviewholderint(view child) { 
if (child == null) { 
return null; 
} 
return ((layoutparams) child.getlayoutparams()).mviewholder; 
} 
/** 
* return the adapter position that the given child view corresponds to. 
* 
* @param child child view to query 
* @return adapter position corresponding to the given view or {@link #no_position} 
*/ 
public int getchildposition(view child) { 
final viewholder holder = getchildviewholderint(child); 
return holder != null ? holder.getposition() : no_position; 
} 
/** 
* return the stable item id that the given child view corresponds to. 
* 
* @param child child view to query 
* @return item id corresponding to the given view or {@link #no_id} 
*/ 
public long getchilditemid(view child) { 
if (madapter == null || !madapter.hasstableids()) { 
return no_id; 
} 
final viewholder holder = getchildviewholderint(child); 
return holder != null ? holder.getitemid() : no_id; 
} 
/** 
* return the viewholder for the item in the given position of the data set. 
* 
* @param position the position of the item in the data set of the adapter 
* @return the viewholder at <code>position</code> 
*/ 
public viewholder findviewholderforposition(int position) { 
return findviewholderforposition(position, false); 
} 
viewholder findviewholderforposition(int position, boolean checknewposition) { 
final int childcount = getchildcount(); 
for (int i = 0; i < childcount; i++) { 
final viewholder holder = getchildviewholderint(getchildat(i)); 
if (holder != null) { 
if (checknewposition) { 
if (holder.mposition == position) { 
return holder; 
} 
} else if(holder.getposition() == position) { 
return holder; 
} 
} 
} 
return mrecycler.findviewholderforposition(position); 
} 
/** 
* return the viewholder for the item with the given id. the recyclerview must 
* use an adapter with {@link adapter#sethasstableids(boolean) stableids} to 
* return a non-null value. 
* 
* @param id the id for the requested item 
* @return the viewholder with the given <code>id</code>, of null if there 
* is no such item. 
*/ 
public viewholder findviewholderforitemid(long id) { 
final int childcount = getchildcount(); 
for (int i = 0; i < childcount; i++) { 
final viewholder holder = getchildviewholderint(getchildat(i)); 
if (holder != null && holder.getitemid() == id) { 
return holder; 
} 
} 
return mrecycler.findviewholderforitemid(id); 
} 
/** 
* find the topmost view under the given point. 
* 
* @param x horizontal position in pixels to search 
* @param y vertical position in pixels to search 
* @return the child view under (x, y) or null if no matching child is found 
*/ 
public view findchildviewunder(float x, float y) { 
final int count = getchildcount(); 
for (int i = count - 1; i >= 0; i--) { 
final view child = getchildat(i); 
final float translationx = viewcompat.gettranslationx(child); 
final float translationy = viewcompat.gettranslationy(child); 
if (x >= child.getleft() + translationx && 
x <= child.getright() + translationx && 
y >= child.gettop() + translationy && 
y <= child.getbottom() + translationy) { 
return child; 
} 
} 
return null; 
} 
/** 
* offset the bounds of all child views by <code>dy</code> pixels. 
* useful for implementing simple scrolling in {@link layoutmanager layoutmanagers}. 
* 
* @param dy vertical pixel offset to apply to the bounds of all child views 
*/ 
public void offsetchildrenvertical(int dy) { 
final int childcount = getchildcount(); 
for (int i = 0; i < childcount; i++) { 
getchildat(i).offsettopandbottom(dy); 
} 
} 
/** 
* called when an item view is attached to this recyclerview. 
* 
* <p>subclasses of recyclerview may want to perform extra bookkeeping or modifications 
* of child views as they become attached. this will be called before a 
* {@link layoutmanager} measures or lays out the view and is a good time to perform these 
* changes.</p> 
* 
* @param child child view that is now attached to this recyclerview and its associated window 
*/ 
public void onchildattachedtowindow(view child) { 
} 
/** 
* called when an item view is detached from this recyclerview. 
* 
* <p>subclasses of recyclerview may want to perform extra bookkeeping or modifications 
* of child views as they become detached. this will be called as a 
* {@link layoutmanager} fully detaches the child view from the parent and its window.</p> 
* 
* @param child child view that is now detached from this recyclerview and its associated window 
*/ 
public void onchilddetachedfromwindow(view child) { 
} 
/** 
* offset the bounds of all child views by <code>dx</code> pixels. 
* useful for implementing simple scrolling in {@link layoutmanager layoutmanagers}. 
* 
* @param dx horizontal pixel offset to apply to the bounds of all child views 
*/ 
public void offsetchildrenhorizontal(int dx) { 
final int childcount = getchildcount(); 
for (int i = 0; i < childcount; i++) { 
getchildat(i).offsetleftandright(dx); 
} 
} 
rect getitemdecorinsetsforchild(view child) { 
final layoutparams lp = (layoutparams) child.getlayoutparams(); 
if (!lp.minsetsdirty) { 
return lp.mdecorinsets; 
} 
final rect insets = lp.mdecorinsets; 
insets.set(0, 0, 0, 0); 
final int decorcount = mitemdecorations.size(); 
for (int i = 0; i < decorcount; i++) { 
mtemprect.set(0, 0, 0, 0); 
mitemdecorations.get(i).getitemoffsets(mtemprect, lp.getviewposition(), this); 
insets.left += mtemprect.left; 
insets.top += mtemprect.top; 
insets.right += mtemprect.right; 
insets.bottom += mtemprect.bottom; 
} 
lp.minsetsdirty = false; 
return insets; 
} 
private class viewflinger implements runnable { 
private int mlastflingx; 
private int mlastflingy; 
private scrollercompat mscroller; 
private interpolator minterpolator = squinticinterpolator; 
// when set to true, postonanimation callbacks are delayed until the run method completes 
private boolean meatrunonanimationrequest = false; 
// tracks if postanimationcallback should be re-attached when it is done 
private boolean mreschedulepostanimationcallback = false; 
public viewflinger() { 
mscroller = scrollercompat.create(getcontext(), squinticinterpolator); 
} 
@override 
public void run() { 
disablerunonanimationrequests(); 
consumependingupdateoperations(); 
// keep a local reference so that if it is changed during onanimation method, it wont cause 
// unexpected behaviors 
final scrollercompat scroller = mscroller; 
final smoothscroller smoothscroller = mlayout.msmoothscroller; 
if (scroller.computescrolloffset()) { 
final int x = scroller.getcurrx(); 
final int y = scroller.getcurry(); 
final int dx = x - mlastflingx; 
final int dy = y - mlastflingy; 
mlastflingx = x; 
mlastflingy = y; 
int overscrollx = 0, overscrolly = 0; 
if (madapter != null) { 
eatrequestlayout(); 
if (dx != 0) { 
final int hresult = mlayout.scrollhorizontallyby(dx, mrecycler, mstate); 
overscrollx = dx - hresult; 
} 
if (dy != 0) { 
final int vresult = mlayout.scrollverticallyby(dy, mrecycler, mstate); 
overscrolly = dy - vresult; 
} 
if (smoothscroller != null && !smoothscroller.ispendinginitialrun() && 
smoothscroller.isrunning()) { 
smoothscroller.onanimation(dx - overscrollx, dy - overscrolly); 
} 
resumerequestlayout(false); 
} 
if (!mitemdecorations.isempty()) { 
invalidate(); 
} 
if (overscrollx != 0 || overscrolly != 0) { 
final int vel = (int) scroller.getcurrvelocity(); 
int velx = 0; 
if (overscrollx != x) { 
velx = overscrollx < 0 ? -vel : overscrollx > 0 ? vel : 0; 
} 
int vely = 0; 
if (overscrolly != y) { 
vely = overscrolly < 0 ? -vel : overscrolly > 0 ? vel : 0; 
} 
if (viewcompat.getoverscrollmode(recyclerview.this) != 
viewcompat.over_scroll_never) { 
absorbglows(velx, vely); 
} 
if ((velx != 0 || overscrollx == x || scroller.getfinalx() == 0) && 
(vely != 0 || overscrolly == y || scroller.getfinaly() == 0)) { 
scroller.abortanimation(); 
} 
} 
if (mscrolllistener != null && (x != 0 || y != 0)) { 
mscrolllistener.onscrolled(dx, dy); 
} 
if (!awakenscrollbars()) { 
invalidate(); 
} 
if (scroller.isfinished()) { 
setscrollstate(scroll_state_idle); 
} else { 
postonanimation(); 
} 
} 
// call this after the onanimation is complete not to have inconsistent callbacks etc. 
if (smoothscroller != null && smoothscroller.ispendinginitialrun()) { 
smoothscroller.onanimation(0, 0); 
} 
enablerunonanimationrequests(); 
} 
private void disablerunonanimationrequests() { 
mreschedulepostanimationcallback = false; 
meatrunonanimationrequest = true; 
} 
private void enablerunonanimationrequests() { 
meatrunonanimationrequest = false; 
if (mreschedulepostanimationcallback) { 
postonanimation(); 
} 
} 
void postonanimation() { 
if (meatrunonanimationrequest) { 
mreschedulepostanimationcallback = true; 
} else { 
viewcompat.postonanimation(recyclerview.this, this); 
} 
} 
public void fling(int velocityx, int velocityy) { 
setscrollstate(scroll_state_settling); 
mlastflingx = mlastflingy = 0; 
mscroller.fling(0, 0, velocityx, velocityy, 
integer.min_value, integer.max_value, integer.min_value, integer.max_value); 
postonanimation(); 
} 
public void smoothscrollby(int dx, int dy) { 
smoothscrollby(dx, dy, 0, 0); 
} 
public void smoothscrollby(int dx, int dy, int vx, int vy) { 
smoothscrollby(dx, dy, computescrollduration(dx, dy, vx, vy)); 
} 
private float distanceinfluenceforsnapduration(float f) { 
f -= 0.5f; // center the values about 0. 
f *= 0.3f * math.pi / 2.0f; 
return (float) math.sin(f); 
} 
private int computescrollduration(int dx, int dy, int vx, int vy) { 
final int absdx = math.abs(dx); 
final int absdy = math.abs(dy); 
final boolean horizontal = absdx > absdy; 
final int velocity = (int) math.sqrt(vx * vx + vy * vy); 
final int delta = (int) math.sqrt(dx * dx + dy * dy); 
final int containersize = horizontal ? getwidth() : getheight(); 
final int halfcontainersize = containersize / 2; 
final float distanceratio = math.min(1.f, 1.f * delta / containersize); 
final float distance = halfcontainersize + halfcontainersize * 
distanceinfluenceforsnapduration(distanceratio); 
final int duration; 
if (velocity > 0) { 
duration = 4 * math.round(1000 * math.abs(distance / velocity)); 
} else { 
float absdelta = (float) (horizontal ? absdx : absdy); 
duration = (int) (((absdelta / containersize) + 1) * 300); 
} 
return math.min(duration, max_scroll_duration); 
} 
public void smoothscrollby(int dx, int dy, int duration) { 
smoothscrollby(dx, dy, duration, squinticinterpolator); 
} 
public void smoothscrollby(int dx, int dy, int duration, interpolator interpolator) { 
if (minterpolator != interpolator) { 
minterpolator = interpolator; 
mscroller = scrollercompat.create(getcontext(), interpolator); 
} 
setscrollstate(scroll_state_settling); 
mlastflingx = mlastflingy = 0; 
mscroller.startscroll(0, 0, dx, dy, duration); 
postonanimation(); 
} 
public void stop() { 
removecallbacks(this); 
mscroller.abortanimation(); 
} 
} 
private class recyclerviewdataobserver extends adapterdataobserver { 
@override 
public void onchanged() { 
if (madapter.hasstableids()) { 
// todo determine what actually changed 
markknownviewsinvalid(); 
mstate.mstructurechanged = true; 
requestlayout(); 
} else { 
markknownviewsinvalid(); 
mstate.mstructurechanged = true; 
requestlayout(); 
} 
} 
@override 
public void onitemrangechanged(int positionstart, int itemcount) { 
postadapterupdate(obtainupdateop(updateop.update, positionstart, itemcount)); 
} 
@override 
public void onitemrangeinserted(int positionstart, int itemcount) { 
postadapterupdate(obtainupdateop(updateop.add, positionstart, itemcount)); 
} 
@override 
public void onitemrangeremoved(int positionstart, int itemcount) { 
postadapterupdate(obtainupdateop(updateop.remove, positionstart, itemcount)); 
} 
} 
public static class recycledviewpool { 
private sparsearray<arraylist<viewholder>> mscrap = 
new sparsearray<arraylist<viewholder>>(); 
private sparseintarray mmaxscrap = new sparseintarray(); 
private int mattachcount = 0; 
private static final int default_max_scrap = 5; 
public void clear() { 
mscrap.clear(); 
} 
public void setmaxrecycledviews(int viewtype, int max) { 
mmaxscrap.put(viewtype, max); 
final arraylist<viewholder> scrapheap = mscrap.get(viewtype); 
if (scrapheap != null) { 
while (scrapheap.size() > max) { 
scrapheap.remove(scrapheap.size() - 1); 
} 
} 
} 
public viewholder getrecycledview(int viewtype) { 
final arraylist<viewholder> scrapheap = mscrap.get(viewtype); 
if (scrapheap != null && !scrapheap.isempty()) { 
final int index = scrapheap.size() - 1; 
final viewholder scrap = scrapheap.get(index); 
scrapheap.remove(index); 
return scrap; 
} 
return null; 
} 
public void putrecycledview(viewholder scrap) { 
final int viewtype = scrap.getitemviewtype(); 
final arraylist scrapheap = getscrapheapfortype(viewtype); 
if (mmaxscrap.get(viewtype) <= scrapheap.size()) { 
return; 
} 
scrap.mposition = no_position; 
scrap.moldposition = no_position; 
scrap.mitemid = no_id; 
scrap.clearflagsforsharedpool(); 
scrapheap.add(scrap); 
} 
void attach(adapter adapter) { 
mattachcount++; 
} 
void detach() { 
mattachcount--; 
} 
void onadapterchanged(adapter oldadapter, adapter newadapter) { 
if (mattachcount == 1) { 
clear(); 
} 
} 
private arraylist<viewholder> getscrapheapfortype(int viewtype) { 
arraylist<viewholder> scrap = mscrap.get(viewtype); 
if (scrap == null) { 
scrap = new arraylist<viewholder>(); 
mscrap.put(viewtype, scrap); 
if (mmaxscrap.indexofkey(viewtype) < 0) { 
mmaxscrap.put(viewtype, default_max_scrap); 
} 
} 
return scrap; 
} 
} 
/** 
* a recycler is responsible for managing scrapped or detached item views for reuse. 
* 
* <p>a "scrapped" view is a view that is still attached to its parent recyclerview but 
* that has been marked for removal or reuse.</p> 
* 
* <p>typical use of a recycler by a {@link layoutmanager} will be to obtain views for 
* an adapter's data set representing the data at a given position or item id. 
* if the view to be reused is considered "dirty" the adapter will be asked to rebind it. 
* if not, the view can be quickly reused by the layoutmanager with no further work. 
* clean views that have not {@link android.view.view#islayoutrequested() requested layout} 
* may be repositioned by a layoutmanager without remeasurement.</p> 
*/ 
public final class recycler { 
private final arraylist<viewholder> mattachedscrap = new arraylist<viewholder>(); 
private final arraylist<viewholder> mcachedviews = new arraylist<viewholder>(); 
private final list<viewholder> 
munmodifiableattachedscrap = collections.unmodifiablelist(mattachedscrap); 
private int mviewcachemax = default_cache_size; 
private recycledviewpool mrecyclerpool; 
private static final int default_cache_size = 2; 
/** 
* clear scrap views out of this recycler. detached views contained within a 
* recycled view pool will remain. 
*/ 
public void clear() { 
mattachedscrap.clear(); 
recyclecachedviews(); 
} 
/** 
* set the maximum number of detached, valid views we should retain for later use. 
* 
* @param viewcount number of views to keep before sending views to the shared pool 
*/ 
public void setviewcachesize(int viewcount) { 
mviewcachemax = viewcount; 
while (mcachedviews.size() > viewcount) { 
mcachedviews.remove(mcachedviews.size() - 1); 
} 
} 
/** 
* returns an unmodifiable list of viewholders that are currently in the scrap list. 
* 
* @return list of viewholders in the scrap list. 
*/ 
public list<viewholder> getscraplist() { 
return munmodifiableattachedscrap; 
} 
/** 
* helper method for getviewforposition. 
* <p> 
* checks whether a given view holder can be used for the provided position. 
* 
* @param holder viewholder 
* @param offsetposition the position which is updated by update_op changes on the adapter 
* @return true if viewholder matches the provided position, false otherwise 
*/ 
boolean validateviewholderforoffsetposition(viewholder holder, int offsetposition) { 
// if it is a removed holder, nothing to verify since we cannot ask adapter anymore 
// if it is not removed, verify the type and id. 
if (holder.isremoved()) { 
return true; 
} 
if (offsetposition < 0 || offsetposition >= madapter.getitemcount()) { 
if (debug) { 
log.d(tag, "validateviewholderforoffsetposition: invalid position, returning " 
+ "false"); 
} 
return false; 
} 
final int type = madapter.getitemviewtype(offsetposition); 
if (type != holder.getitemviewtype()) { 
return false; 
} 
if (madapter.hasstableids()) { 
return holder.getitemid() == madapter.getitemid(offsetposition); 
} 
return true; 
} 
/** 
* obtain a view initialized for the given position. 
* 
* <p>this method should be used by {@link layoutmanager} implementations to obtain 
* views to represent data from an {@link adapter}.</p> 
* 
* <p>the recycler may reuse a scrap or detached view from a shared pool if one is 
* available for the correct view type. if the adapter has not indicated that the 
* data at the given position has changed, the recycler will attempt to hand back 
* a scrap view that was previously initialized for that data without rebinding.</p> 
* 
* @param position position to obtain a view for 
* @return a view representing the data at <code>position</code> from <code>adapter</code> 
*/ 
public view getviewforposition(int position) { 
viewholder holder; 
holder = getscrapviewforposition(position, invalid_type); 
final int offsetposition = findpositionoffset(position); 
if (holder != null) { 
if (!validateviewholderforoffsetposition(holder, offsetposition)) { 
// recycle this scrap 
removedetachedview(holder.itemview, false); 
quickrecyclescrapview(holder.itemview); 
// if validate fails, we can query scrap again w/ type. that may return a 
// different view holder from cache. 
final int type = madapter.getitemviewtype(offsetposition); 
if (madapter.hasstableids()) { 
final long id = madapter.getitemid(offsetposition); 
holder = getscrapviewforid(id, type); 
} else { 
holder = getscrapviewforposition(offsetposition, type); 
} 
} 
} else { 
// try recycler. 
holder = getrecycledviewpool() 
.getrecycledview(madapter.getitemviewtype(offsetposition)); 
} 
if (holder == null) { 
if (offsetposition < 0 || offsetposition >= madapter.getitemcount()) { 
throw new indexoutofboundsexception("invalid item position " + position 
+ "(" + offsetposition + ")"); 
} else { 
holder = madapter.createviewholder(recyclerview.this, 
madapter.getitemviewtype(offsetposition)); 
if (debug) { 
log.d(tag, "getviewforposition created new viewholder"); 
} 
} 
} 
if (!holder.isremoved() && (!holder.isbound() || holder.needsupdate())) { 
if (debug) { 
log.d(tag, "getviewforposition unbound holder or needs update; updating..."); 
} 
// todo: think through when getoffsetposition() is called. i use it here because 
// existing views have already been offset appropriately through the moldoffset 
// mechanism, but new views do not have this mechanism. 
madapter.bindviewholder(holder, offsetposition); 
} 
viewgroup.layoutparams lp = holder.itemview.getlayoutparams(); 
if (lp == null) { 
lp = generatedefaultlayoutparams(); 
holder.itemview.setlayoutparams(lp); 
} else if (!checklayoutparams(lp)) { 
lp = generatelayoutparams(lp); 
holder.itemview.setlayoutparams(lp); 
} 
((layoutparams) lp).mviewholder = holder; 
return holder.itemview; 
} 
/** 
* recycle a detached view. the specified view will be added to a pool of views 
* for later rebinding and reuse. 
* 
* <p>a view must be fully detached before it may be recycled.</p> 
* 
* @param view removed view for recycling 
*/ 
public void recycleview(view view) { 
recycleviewholder(getchildviewholderint(view)); 
} 
void recyclecachedviews() { 
final int count = mcachedviews.size(); 
for (int i = count - 1; i >= 0; i--) { 
final viewholder cachedview = mcachedviews.get(i); 
if (cachedview.isrecyclable()) { 
getrecycledviewpool().putrecycledview(cachedview); 
dispatchviewrecycled(cachedview); 
} 
mcachedviews.remove(i); 
} 
} 
void recycleviewholder(viewholder holder) { 
if (holder.isscrap() || holder.itemview.getparent() != null) { 
throw new illegalargumentexception( 
"scrapped or attached views may not be recycled."); 
} 
boolean cached = false; 
if (!holder.isinvalid() && (minprelayout || !holder.isremoved())) { 
// retire oldest cached views first 
if (mcachedviews.size() == mviewcachemax && !mcachedviews.isempty()) { 
for (int i = 0; i < mcachedviews.size(); i++) { 
final viewholder cachedview = mcachedviews.get(i); 
if (cachedview.isrecyclable()) { 
mcachedviews.remove(i); 
getrecycledviewpool().putrecycledview(cachedview); 
dispatchviewrecycled(cachedview); 
break; 
} 
} 
} 
if (mcachedviews.size() < mviewcachemax) { 
mcachedviews.add(holder); 
cached = true; 
} 
} 
if (!cached && holder.isrecyclable()) { 
getrecycledviewpool().putrecycledview(holder); 
dispatchviewrecycled(holder); 
} 
// remove from pre/post maps that are used to animate items; a recycled holder 
// should not be animated 
mstate.mprelayoutholdermap.remove(holder); 
mstate.mpostlayoutholdermap.remove(holder); 
} 
/** 
* used as a fast path for unscrapping and recycling a view during a bulk operation. 
* the caller must call {@link #clearscrap()} when it's done to update the recycler's 
* internal bookkeeping. 
*/ 
void quickrecyclescrapview(view view) { 
final viewholder holder = getchildviewholderint(view); 
holder.mscrapcontainer = null; 
recycleviewholder(holder); 
} 
/** 
* mark an attached view as scrap. 
* 
* <p>"scrap" views are still attached to their parent recyclerview but are eligible 
* for rebinding and reuse. requests for a view for a given position may return a 
* reused or rebound scrap view instance.</p> 
* 
* @param view view to scrap 
*/ 
void scrapview(view view) { 
final viewholder holder = getchildviewholderint(view); 
holder.setscrapcontainer(this); 
mattachedscrap.add(holder); 
} 
/** 
* remove a previously scrapped view from the pool of eligible scrap. 
* 
* <p>this view will no longer be eligible for reuse until re-scrapped or 
* until it is explicitly removed and recycled.</p> 
*/ 
void unscrapview(viewholder holder) { 
mattachedscrap.remove(holder); 
holder.mscrapcontainer = null; 
} 
int getscrapcount() { 
return mattachedscrap.size(); 
} 
view getscrapviewat(int index) { 
return mattachedscrap.get(index).itemview; 
} 
void clearscrap() { 
mattachedscrap.clear(); 
} 
/** 
* returns a scrap view for the position. if type is not invalid_type, it also checks if 
* viewholder's type matches the provided type. 
* 
* @param position item position 
* @param type view type 
* @return a viewholder that can be re-used for this position. 
*/ 
viewholder getscrapviewforposition(int position, int type) { 
final int scrapcount = mattachedscrap.size(); 
// try first for an exact, non-invalid match from scrap. 
for (int i = 0; i < scrapcount; i++) { 
final viewholder holder = mattachedscrap.get(i); 
if (holder.getposition() == position && !holder.isinvalid() && 
(minprelayout || !holder.isremoved())) { 
if (type != invalid_type && holder.getitemviewtype() != type) { 
log.e(tag, "scrap view for position " + position + " isn't dirty but has" + 
" wrong view type! (found " + holder.getitemviewtype() + 
" but expected " + type + ")"); 
break; 
} 
mattachedscrap.remove(i); 
holder.setscrapcontainer(null); 
if (debug) { 
log.d(tag, "getscrapviewforposition(" + position + ", " + type + 
") found exact match in scrap: " + holder); 
} 
return holder; 
} 
} 
if (mnumanimatingviews != 0) { 
view view = getanimatingview(position, type); 
if (view != null) { 
// ending the animation should cause it to get recycled before we reuse it 
mitemanimator.endanimation(getchildviewholder(view)); 
} 
} 
// search in our first-level recycled view cache. 
final int cachesize = mcachedviews.size(); 
for (int i = 0; i < cachesize; i++) { 
final viewholder holder = mcachedviews.get(i); 
if (holder.getposition() == position) { 
mcachedviews.remove(i); 
if (holder.isinvalid() && 
(type != invalid_type && holder.getitemviewtype() != type)) { 
// can't use it. we don't know where it's been. 
if (debug) { 
log.d(tag, "getscrapviewforposition(" + position + ", " + type + 
") found position match, but holder is invalid with type " + 
holder.getitemviewtype()); 
} 
if (holder.isrecyclable()) { 
getrecycledviewpool().putrecycledview(holder); 
} 
// even if the holder wasn't officially recycleable, dispatch that it 
// was recycled anyway in case there are resources to unbind. 
dispatchviewrecycled(holder); 
// drop out of the cache search and try something else instead, 
// we won't find another match here. 
break; 
} 
if (debug) { 
log.d(tag, "getscrapviewforposition(" + position + ", " + type + 
") found match in cache: " + holder); 
} 
return holder; 
} 
} 
// give up. head to the shared pool. 
if (debug) { 
log.d(tag, "getscrapviewforposition(" + position + ", " + type + 
") fetching from shared pool"); 
} 
return type == invalid_type ? null : getrecycledviewpool().getrecycledview(type); 
} 
viewholder getscrapviewforid(long id, int type) { 
// look in our attached views first 
final int count = mattachedscrap.size(); 
for (int i = 0; i < count; i++) { 
final viewholder holder = mattachedscrap.get(i); 
if (holder.getitemid() == id) { 
if (type == holder.getitemviewtype()) { 
mattachedscrap.remove(i); 
holder.setscrapcontainer(null); 
return holder; 
} else { 
break; 
} 
} 
} 
// search the first-level cache 
final int cachesize = mcachedviews.size(); 
for (int i = 0; i < cachesize; i++) { 
final viewholder holder = mcachedviews.get(i); 
if (holder.getitemid() == id) { 
mcachedviews.remove(i); 
return holder; 
} 
} 
// that didn't work, look for an unordered view of the right type instead. 
// the holder's position won't match so the calling code will need to have 
// the adapter rebind it. 
return getrecycledviewpool().getrecycledview(type); 
} 
void dispatchviewrecycled(viewholder holder) { 
if (mrecyclerlistener != null) { 
mrecyclerlistener.onviewrecycled(holder); 
} 
if (madapter != null) { 
madapter.onviewrecycled(holder); 
} 
if (debug) log.d(tag, "dispatchviewrecycled: " + holder); 
} 
void onadapterchanged(adapter oldadapter, adapter newadapter) { 
clear(); 
getrecycledviewpool().onadapterchanged(oldadapter, newadapter); 
} 
void offsetpositionrecordsforinsert(int insertedat, int count) { 
final int cachedcount = mcachedviews.size(); 
for (int i = 0; i < cachedcount; i++) { 
final viewholder holder = mcachedviews.get(i); 
if (holder != null && holder.getposition() >= insertedat) { 
if (debug) { 
log.d(tag, "offsetpositionrecordsforinsert cached " + i + " holder " + 
holder + " now at position " + (holder.mposition + count)); 
} 
holder.offsetposition(count); 
} 
} 
} 
void offsetpositionrecordsforremove(int removedfrom, int count) { 
final int removedend = removedfrom + count; 
final int cachedcount = mcachedviews.size(); 
for (int i = cachedcount - 1; i >= 0; i--) { 
final viewholder holder = mcachedviews.get(i); 
if (holder != null) { 
if (holder.getposition() >= removedend) { 
if (debug) { 
log.d(tag, "offsetpositionrecordsforremove cached " + i + 
" holder " + holder + " now at position " + 
(holder.mposition - count)); 
} 
holder.offsetposition(-count); 
} else if (holder.getposition() >= removedfrom) { 
// item for this view was removed. dump it from the cache. 
if (debug) { 
log.d(tag, "offsetpositionrecordsforremove cached " + i + 
" holder " + holder + " now placed in pool"); 
} 
mcachedviews.remove(i); 
getrecycledviewpool().putrecycledview(holder); 
dispatchviewrecycled(holder); 
} 
} 
} 
} 
void setrecycledviewpool(recycledviewpool pool) { 
if (mrecyclerpool != null) { 
mrecyclerpool.detach(); 
} 
mrecyclerpool = pool; 
if (pool != null) { 
mrecyclerpool.attach(getadapter()); 
} 
} 
recycledviewpool getrecycledviewpool() { 
if (mrecyclerpool == null) { 
mrecyclerpool = new recycledviewpool(); 
} 
return mrecyclerpool; 
} 
viewholder findviewholderforposition(int position) { 
final int cachedcount = mcachedviews.size(); 
for (int i = 0; i < cachedcount; i++) { 
final viewholder holder = mcachedviews.get(i); 
if (holder != null && holder.getposition() == position) { 
mcachedviews.remove(i); 
return holder; 
} 
} 
return null; 
} 
viewholder findviewholderforitemid(long id) { 
if (!madapter.hasstableids()) { 
return null; 
} 
final int cachedcount = mcachedviews.size(); 
for (int i = 0; i < cachedcount; i++) { 
final viewholder holder = mcachedviews.get(i); 
if (holder != null && holder.getitemid() == id) { 
mcachedviews.remove(i); 
return holder; 
} 
} 
return null; 
} 
void viewrangeupdate(int positionstart, int itemcount) { 
final int positionend = positionstart + itemcount; 
final int cachedcount = mcachedviews.size(); 
for (int i = 0; i < cachedcount; i++) { 
final viewholder holder = mcachedviews.get(i); 
if (holder == null) { 
continue; 
} 
final int pos = holder.getposition(); 
if (pos >= positionstart && pos < positionend) { 
holder.addflags(viewholder.flag_update); 
} 
} 
} 
void markknownviewsinvalid() { 
final int cachedcount = mcachedviews.size(); 
for (int i = 0; i < cachedcount; i++) { 
final viewholder holder = mcachedviews.get(i); 
if (holder != null) { 
holder.addflags(viewholder.flag_update | viewholder.flag_invalid); 
} 
} 
} 
void clearoldpositions() { 
final int cachedcount = mcachedviews.size(); 
for (int i = 0; i < cachedcount; i++) { 
final viewholder holder = mcachedviews.get(i); 
holder.clearoldposition(); 
} 
} 
} 
/** 
* 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; 
public abstract vh oncreateviewholder(viewgroup parent, int viewtype); 
public abstract void onbindviewholder(vh holder, int position); 
public final vh createviewholder(viewgroup parent, int viewtype) { 
final vh holder = oncreateviewholder(parent, viewtype); 
holder.mitemviewtype = viewtype; 
return holder; 
} 
public final void bindviewholder(vh holder, int position) { 
holder.mposition = position; 
if (hasstableids()) { 
holder.mitemid = getitemid(position); 
} 
onbindviewholder(holder, position); 
holder.setflags(viewholder.flag_bound, 
viewholder.flag_bound | viewholder.flag_update | viewholder.flag_invalid); 
} 
/** 
* return the view type of the item at <code>position</code> for the purposes 
* of view recycling. 
* 
* <p>the default implementation of this method returns 0, making the assumption of 
* a single view type for the adapter. unlike listview adapters, types need not 
* be contiguous. consider using id resources to uniquely identify item view types. 
* 
* @param position position to query 
* @return integer value identifying the type of the view needed to represent the item at 
* <code>position</code>. type codes need not be contiguous. 
*/ 
public int getitemviewtype(int position) { 
return 0; 
} 
public void sethasstableids(boolean hasstableids) { 
if (hasobservers()) { 
throw new illegalstateexception("cannot change whether this adapter has " + 
"stable ids while the adapter has registered observers."); 
} 
mhasstableids = hasstableids; 
} 
/** 
* return the stable id for the item at <code>position</code>. if {@link #hasstableids()} 
* would return false this method should return {@link #no_id}. the default implementation 
* of this method returns {@link #no_id}. 
* 
* @param position adapter position to query 
* @return the stable id of the item at position 
*/ 
public long getitemid(int position) { 
return no_id; 
} 
public abstract int getitemcount(); 
/** 
* returns true if this adapter publishes a unique <code>long</code> value that can 
* act as a key for the item at a given position in the data set. if that item is relocated 
* in the data set, the id returned for that item should be the same. 
* 
* @return true if this adapter's items have stable ids 
*/ 
public final boolean hasstableids() { 
return mhasstableids; 
} 
/** 
* called when a view created by this adapter has been recycled. 
* 
* <p>a view is recycled when a {@link layoutmanager} decides that it no longer 
* needs to be attached to its parent {@link recyclerview}. this can be because it has 
* fallen out of visibility or a set of cached views represented by views still 
* attached to the parent recyclerview. if an item view has large or expensive data 
* bound to it such as large bitmaps, this may be a good place to release those 
* resources.</p> 
* 
* @param holder the viewholder for the view being recycled 
*/ 
public void onviewrecycled(vh holder) { 
} 
/** 
* called when a view created by this adapter has been attached to a window. 
* 
* <p>this can be used as a reasonable signal that the view is about to be seen 
* by the user. if the adapter previously freed any resources in 
* {@link #onviewdetachedfromwindow(recyclerview.viewholder) onviewdetachedfromwindow} 
* those resources should be restored here.</p> 
* 
* @param holder holder of the view being attached 
*/ 
public void onviewattachedtowindow(vh holder) { 
} 
/** 
* called when a view created by this adapter has been detached from its window. 
* 
* <p>becoming detached from the window is not necessarily a permanent condition; 
* the consumer of an adapter's views may choose to cache views offscreen while they 
* are not visible, attaching an detaching them as appropriate.</p> 
* 
* @param holder holder of the view being detached 
*/ 
public void onviewdetachedfromwindow(vh holder) { 
} 
/** 
* returns true if one or more observers are attached to this adapter. 
* @return true if this adapter has observers 
*/ 
public final boolean hasobservers() { 
return mobservable.hasobservers(); 
} 
/** 
* register a new observer to listen for data changes. 
* 
* <p>the adapter may publish a variety of events describing specific changes. 
* not all adapters may support all change types and some may fall back to a generic 
* {@link android.support.v7.widget.recyclerview.adapterdataobserver#onchanged() 
* "something changed"} event if more specific data is not available.</p> 
* 
* <p>components registering observers with an adapter are responsible for 
* {@link #unregisteradapterdataobserver(android.support.v7.widget.recyclerview.adapterdataobserver) 
* unregistering} those observers when finished.</p> 
* 
* @param observer observer to register 
* 
* @see #unregisteradapterdataobserver(android.support.v7.widget.recyclerview.adapterdataobserver) 
*/ 
public void registeradapterdataobserver(adapterdataobserver observer) { 
mobservable.registerobserver(observer); 
} 
/** 
* unregister an observer currently listening for data changes. 
* 
* <p>the unregistered observer will no longer receive events about changes 
* to the adapter.</p> 
* 
* @param observer observer to unregister 
* 
* @see #registeradapterdataobserver(android.support.v7.widget.recyclerview.adapterdataobserver) 
*/ 
public void unregisteradapterdataobserver(adapterdataobserver observer) { 
mobservable.unregisterobserver(observer); 
} 
/** 
* notify any registered observers that the data set has changed. 
* 
* <p>there are two different classes of data change events, item changes and structural 
* changes. item changes are when a single item has its data updated but no positional 
* changes have occurred. structural changes are when items are inserted, removed or moved 
* within the data set.</p> 
* 
* <p>this event does not specify what about the data set has changed, forcing 
* any observers to assume that all existing items and structure may no longer be valid. 
* layoutmanagers will be forced to fully rebind and relayout all visible views.</p> 
* 
* <p><code>recyclerview</code> will attempt to synthesize visible structural change events 
* for adapters that report that they have {@link #hasstableids() stable ids} when 
* this method is used. this can help for the purposes of animation and visual 
* object persistence but individual item views will still need to be rebound 
* and relaid out.</p> 
* 
* <p>if you are writing an adapter it will always be more efficient to use the more 
* specific change events if you can. rely on <code>notifydatasetchanged()</code> 
* as a last resort.</p> 
* 
* @see #notifyitemchanged(int) 
* @see #notifyiteminserted(int) 
* @see #notifyitemremoved(int) 
* @see #notifyitemrangechanged(int, int) 
* @see #notifyitemrangeinserted(int, int) 
* @see #notifyitemrangeremoved(int, int) 
*/ 
public final void notifydatasetchanged() { 
mobservable.notifychanged(); 
} 
/** 
* notify any registered observers that the item at <code>position</code> has changed. 
* 
* <p>this is an item change event, not a structural change event. it indicates that any 
* reflection of the data at <code>position</code> is out of date and should be updated. 
* the item at <code>position</code> retains the same identity.</p> 
* 
* @param position position of the item that has changed 
* 
* @see #notifyitemrangechanged(int, int) 
*/ 
public final void notifyitemchanged(int position) { 
mobservable.notifyitemrangechanged(position, 1); 
} 
/** 
* notify any registered observers that the <code>itemcount</code> items starting at 
* position <code>positionstart</code> have changed. 
* 
* <p>this is an item change event, not a structural change event. it indicates that 
* any reflection of the data in the given position range is out of date and should 
* be updated. the items in the given range retain the same identity.</p> 
* 
* @param positionstart position of the first item that has changed 
* @param itemcount number of items that have changed 
* 
* @see #notifyitemchanged(int) 
*/ 
public final void notifyitemrangechanged(int positionstart, int itemcount) { 
mobservable.notifyitemrangechanged(positionstart, itemcount); 
} 
/** 
* notify any registered observers that the item reflected at <code>position</code> 
* has been newly inserted. the item previously at <code>position</code> is now at 
* position <code>position + 1</code>. 
* 
* <p>this is a structural change event. representations of other existing items in the 
* data set are still considered up to date and will not be rebound, though their 
* positions may be altered.</p> 
* 
* @param position position of the newly inserted item in the data set 
* 
* @see #notifyitemrangeinserted(int, int) 
*/ 
public final void notifyiteminserted(int position) { 
mobservable.notifyitemrangeinserted(position, 1); 
} 
/** 
* notify any registered observers that the currently reflected <code>itemcount</code> 
* items starting at <code>positionstart</code> have been newly inserted. the items 
* previously located at <code>positionstart</code> and beyond can now be found starting 
* at position <code>positionstart + itemcount</code>. 
* 
* <p>this is a structural change event. representations of other existing items in the 
* data set are still considered up to date and will not be rebound, though their positions 
* may be altered.</p> 
* 
* @param positionstart position of the first item that was inserted 
* @param itemcount number of items inserted 
* 
* @see #notifyiteminserted(int) 
*/ 
public final void notifyitemrangeinserted(int positionstart, int itemcount) { 
mobservable.notifyitemrangeinserted(positionstart, itemcount); 
} 
/** 
* notify any registered observers that the item previously located at <code>position</code> 
* has been removed from the data set. the items previously located at and after 
* <code>position</code> may now be found at <code>oldposition - 1</code>. 
* 
* <p>this is a structural change event. representations of other existing items in the 
* data set are still considered up to date and will not be rebound, though their positions 
* may be altered.</p> 
* 
* @param position position of the item that has now been removed 
* 
* @see #notifyitemrangeremoved(int, int) 
*/ 
public final void notifyitemremoved(int position) { 
mobservable.notifyitemrangeremoved(position, 1); 
} 
/** 
* notify any registered observers that the <code>itemcount</code> items previously 
* located at <code>positionstart</code> have been removed from the data set. the items 
* previously located at and after <code>positionstart + itemcount</code> may now be found 
* at <code>oldposition - itemcount</code>. 
* 
* <p>this is a structural change event. representations of other existing items in the data 
* set are still considered up to date and will not be rebound, though their positions 
* may be altered.</p> 
* 
* @param positionstart previous position of the first item that was removed 
* @param itemcount number of items removed from the data set 
*/ 
public final void notifyitemrangeremoved(int positionstart, int itemcount) { 
mobservable.notifyitemrangeremoved(positionstart, itemcount); 
} 
} 
/** 
* a <code>layoutmanager</code> is responsible for measuring and positioning item views 
* within a <code>recyclerview</code> as well as determining the policy for when to recycle 
* item views that are no longer visible to the user. by changing the <code>layoutmanager</code> 
* a <code>recyclerview</code> can be used to implement a standard vertically scrolling list, 
* a uniform grid, staggered grids, horizontally scrolling collections and more. several stock 
* layout managers are provided for general use. 
*/ 
public static abstract class layoutmanager { 
recyclerview mrecyclerview; 
@nullable 
smoothscroller msmoothscroller; 
/** 
* calls {@code recyclerview#requestlayout} on the underlying recyclerview 
*/ 
public void requestlayout() { 
if(mrecyclerview != null) { 
mrecyclerview.requestlayout(); 
} 
} 
/** 
* returns whether this layoutmanager supports automatic item animations. 
* a layoutmanager wishing to support item animations should obey certain 
* rules as outlined in {@link #onlayoutchildren(recycler, state)}. 
* the default return value is <code>false</code>, so subclasses of layoutmanager 
* will not get predictive item animations by default. 
* 
* <p>whether item animations are enabled in a recyclerview is determined both 
* by the return value from this method and the 
* {@link recyclerview#setitemanimator(itemanimator) itemanimator} set on the 
* recyclerview itself. if the recyclerview has a non-null itemanimator but this 
* method returns false, then simple item animations will be enabled, in which 
* views that are moving onto or off of the screen are simply faded in/out. if 
* the recyclerview has a non-null itemanimator and this method returns true, 
* then there will be two calls to {@link #onlayoutchildren(recycler, state)} to 
* setup up the information needed to more intelligently predict where appearing 
* and disappearing views should be animated from/to.</p> 
* 
* @return true if predictive item animations should be enabled, false otherwise 
*/ 
public boolean supportspredictiveitemanimations() { 
return false; 
} 
/** 
* called when this layoutmanager is both attached to a recyclerview and that recyclerview 
* is attached to a window. 
* 
* <p>subclass implementations should always call through to the superclass implementation. 
* </p> 
* 
* @param view the recyclerview this layoutmanager is bound to 
*/ 
public void onattachedtowindow(recyclerview view) { 
} 
/** 
* called when this layoutmanager is detached from its parent recyclerview or when 
* its parent recyclerview is detached from its window. 
* 
* <p>subclass implementations should always call through to the superclass implementation. 
* </p> 
* 
* @param view the recyclerview this layoutmanager is bound to 
*/ 
public void ondetachedfromwindow(recyclerview view) { 
} 
/** 
* lay out all relevant child views from the given adapter. 
* 
* the layoutmanager is in charge of the behavior of item animations. by default, 
* recyclerview has a non-null {@link #getitemanimator() itemanimator}, and simple 
* item animations are enabled. this means that add/remove operations on the 
* adapter will result in animations to add new or appearing items, removed or 
* disappearing items, and moved items. if a layoutmanager returns false from 
* {@link #supportspredictiveitemanimations()}, which is the default, and runs a 
* normal layout operation during {@link #onlayoutchildren(recycler, state)}, the 
* recyclerview will have enough information to run those animations in a simple 
* way. for example, the default itemanimator, {@link defaultitemanimator}, will 
* simple fade views in and out, whether they are actuall added/removed or whether 
* they are moved on or off the screen due to other add/remove operations. 
* 
* <p>a layoutmanager wanting a better item animation experience, where items can be 
* animated onto and off of the screen according to where the items exist when they 
* are not on screen, then the layoutmanager should return true from 
* {@link #supportspredictiveitemanimations()} and add additional logic to 
* {@link #onlayoutchildren(recycler, state)}. supporting predictive animations 
* means that {@link #onlayoutchildren(recycler, state)} will be called twice; 
* once as a "pre" layout step to determine where items would have been prior to 
* a real layout, and again to do the "real" layout. in the pre-layout phase, 
* items will remember their pre-layout positions to allow them to be laid out 
* appropriately. also, {@link layoutparams#isitemremoved() removed} items will 
* be returned from the scrap to help determine correct placement of other items. 
* these removed items should not be added to the child list, but should be used 
* to help calculate correct positioning of other views, including views that 
* were not previously onscreen (referred to as appearing views), but whose 
* pre-layout offscreen position can be determined given the extra 
* information about the pre-layout removed views.</p> 
* 
* <p>the second layout pass is the real layout in which only non-removed views 
* will be used. the only additional requirement during this pass is, if 
* {@link #supportspredictiveitemanimations()} returns true, to note which 
* views exist in the child list prior to layout and which are not there after 
* layout (referred to as disappearing views), and to position/layout those views 
* appropriately, without regard to the actual bounds of the recyclerview. this allows 
* the animation system to know the location to which to animate these disappearing 
* views.</p> 
* 
* <p>the default layoutmanager implementations for recyclerview handle all of these 
* requirements for animations already. clients of recyclerview can either use one 
* of these layout managers directly or look at their implementations of 
* onlayoutchildren() to see how they account for the appearing and 
* disappearing views.</p> 
* 
* @param recycler recycler to use for fetching potentially cached views for a 
* position 
* @param state transient state of recyclerview 
*/ 
public void onlayoutchildren(recycler recycler, state state) { 
log.e(tag, "you must override onlayoutchildren(recycler recycler, state state) "); 
} 
/** 
* create a default <code>layoutparams</code> object for a child of the recyclerview. 
* 
* <p>layoutmanagers will often want to use a custom <code>layoutparams</code> type 
* to store extra information specific to the layout. client code should subclass 
* {@link recyclerview.layoutparams} for this purpose.</p> 
* 
* <p><em>important:</em> if you use your own custom <code>layoutparams</code> type 
* you must also override 
* {@link #checklayoutparams(layoutparams)}, 
* {@link #generatelayoutparams(android.view.viewgroup.layoutparams)} and 
* {@link #generatelayoutparams(android.content.context, android.util.attributeset)}.</p> 
* 
* @return a new layoutparams for a child view 
*/ 
public abstract layoutparams generatedefaultlayoutparams(); 
/** 
* determines the validity of the supplied layoutparams object. 
* 
* <p>this should check to make sure that the object is of the correct type 
* and all values are within acceptable ranges. the default implementation 
* returns <code>true</code> for non-null params.</p> 
* 
* @param lp layoutparams object to check 
* @return true if this layoutparams object is valid, false otherwise 
*/ 
public boolean checklayoutparams(layoutparams lp) { 
return lp != null; 
} 
/** 
* create a layoutparams object suitable for this layoutmanager, copying relevant 
* values from the supplied layoutparams object if possible. 
* 
* <p><em>important:</em> if you use your own custom <code>layoutparams</code> type 
* you must also override 
* {@link #checklayoutparams(layoutparams)}, 
* {@link #generatelayoutparams(android.view.viewgroup.layoutparams)} and 
* {@link #generatelayoutparams(android.content.context, android.util.attributeset)}.</p> 
* 
* @param lp source layoutparams object to copy values from 
* @return a new layoutparams object 
*/ 
public layoutparams generatelayoutparams(viewgroup.layoutparams lp) { 
if (lp instanceof layoutparams) { 
return new layoutparams((layoutparams) lp); 
} else if (lp instanceof marginlayoutparams) { 
return new layoutparams((marginlayoutparams) lp); 
} else { 
return new layoutparams(lp); 
} 
} 
/** 
* create a layoutparams object suitable for this layoutmanager from 
* an inflated layout resource. 
* 
* <p><em>important:</em> if you use your own custom <code>layoutparams</code> type 
* you must also override 
* {@link #checklayoutparams(layoutparams)}, 
* {@link #generatelayoutparams(android.view.viewgroup.layoutparams)} and 
* {@link #generatelayoutparams(android.content.context, android.util.attributeset)}.</p> 
* 
* @param c context for obtaining styled attributes 
* @param attrs attributeset describing the supplied arguments 
* @return a new layoutparams object 
*/ 
public layoutparams generatelayoutparams(context c, attributeset attrs) { 
return new layoutparams(c, attrs); 
} 
/** 
* scroll horizontally by dx pixels in screen coordinates and return the distance traveled. 
* the default implementation does nothing and returns 0. 
* 
* @param dx distance to scroll by in pixels. x increases as scroll position 
* approaches the right. 
* @param recycler recycler to use for fetching potentially cached views for a 
* position 
* @param state transient state of recyclerview 
* @return the actual distance scrolled. the return value will be negative if dx was 
* negative and scrolling proceeeded in that direction. 
* <code>math.abs(result)</code> may be less than dx if a boundary was reached. 
*/ 
public int scrollhorizontallyby(int dx, recycler recycler, state state) { 
return 0; 
} 
/** 
* scroll vertically by dy pixels in screen coordinates and return the distance traveled. 
* the default implementation does nothing and returns 0. 
* 
* @param dy distance to scroll in pixels. y increases as scroll position 
* approaches the bottom. 
* @param recycler recycler to use for fetching potentially cached views for a 
* position 
* @param state transient state of recyclerview 
* @return the actual distance scrolled. the return value will be negative if dy was 
* negative and scrolling proceeeded in that direction. 
* <code>math.abs(result)</code> may be less than dy if a boundary was reached. 
*/ 
public int scrollverticallyby(int dy, recycler recycler, state state) { 
return 0; 
} 
/** 
* query if horizontal scrolling is currently supported. the default implementation 
* returns false. 
* 
* @return true if this layoutmanager can scroll the current contents horizontally 
*/ 
public boolean canscrollhorizontally() { 
return false; 
} 
/** 
* query if vertical scrolling is currently supported. the default implementation 
* returns false. 
* 
* @return true if this layoutmanager can scroll the current contents vertically 
*/ 
public boolean canscrollvertically() { 
return false; 
} 
/** 
* scroll to the specified adapter position. 
* 
* actual position of the item on the screen depends on the layoutmanager implementation. 
* @param position scroll to this adapter position. 
*/ 
public void scrolltoposition(int position) { 
if (debug) { 
log.e(tag, "you must implement scrolltoposition. it will soon become abstract"); 
} 
} 
/** 
* <p>smooth scroll to the specified adapter position.</p> 
* <p>to support smooth scrolling, override this method, create your {@link smoothscroller} 
* instance and call {@link #startsmoothscroll(smoothscroller)}. 
* </p> 
* @param recyclerview the recyclerview to which this layout manager is attached 
* @param state current state of recyclerview 
* @param position scroll to this adapter position. 
*/ 
public void smoothscrolltoposition(recyclerview recyclerview, state state, 
int position) { 
log.e(tag, "you must override smoothscrolltoposition to support smooth scrolling"); 
} 
/** 
* <p>starts a smooth scroll using the provided smoothscroller.</p> 
* <p>calling this method will cancel any previous smooth scroll request.</p> 
* @param smoothscroller unstance which defines how smooth scroll should be animated 
*/ 
public void startsmoothscroll(smoothscroller smoothscroller) { 
if (msmoothscroller != null && smoothscroller != msmoothscroller 
&& msmoothscroller.isrunning()) { 
msmoothscroller.stop(); 
} 
msmoothscroller = smoothscroller; 
msmoothscroller.start(mrecyclerview, this); 
} 
/** 
* @return true if recycylerview is currently in the state of smooth scrolling. 
*/ 
public boolean issmoothscrolling() { 
return msmoothscroller != null && msmoothscroller.isrunning(); 
} 
/** 
* returns the resolved layout direction for this recyclerview. 
* 
* @return {@link android.support.v4.view.viewcompat#layout_direction_rtl} if the layout 
* direction is rtl or returns 
* {@link android.support.v4.view.viewcompat#layout_direction_ltr} if the layout direction 
* is not rtl. 
*/ 
public int getlayoutdirection() { 
return viewcompat.getlayoutdirection(mrecyclerview); 
} 
/** 
* add a view to the currently attached recyclerview if needed. layoutmanagers should 
* use this method to add views obtained from a {@link recycler} using 
* {@link recycler#getviewforposition(int)}. 
* 
* @param child view to add 
* @param index index to add child at 
*/ 
public void addview(view child, int index) { 
if (mrecyclerview.manimatingviewindex >= 0) { 
if (index > mrecyclerview.manimatingviewindex) { 
throw new indexoutofboundsexception("index=" + index + " count=" 
+ mrecyclerview.manimatingviewindex); 
} 
mrecyclerview.manimatingviewindex++; 
} 
final viewholder holder = getchildviewholderint(child); 
if (holder.isscrap()) { 
holder.unscrap(); 
mrecyclerview.attachviewtoparent(child, index, child.getlayoutparams()); 
if (dispatch_temp_detach) { 
viewcompat.dispatchfinishtemporarydetach(child); 
} 
} else { 
mrecyclerview.addview(child, index); 
final layoutparams lp = (layoutparams) child.getlayoutparams(); 
lp.minsetsdirty = true; 
final adapter adapter = mrecyclerview.getadapter(); 
if (adapter != null) { 
adapter.onviewattachedtowindow(getchildviewholderint(child)); 
} 
mrecyclerview.onchildattachedtowindow(child); 
if (msmoothscroller != null && msmoothscroller.isrunning()) { 
msmoothscroller.onchildattachedtowindow(child); 
} 
} 
} 
/** 
* add a view to the currently attached recyclerview if needed. layoutmanagers should 
* use this method to add views obtained from a {@link recycler} using 
* {@link recycler#getviewforposition(int)}. 
* 
* @param child view to add 
*/ 
public void addview(view child) { 
if (mrecyclerview.manimatingviewindex >= 0) { 
addview(child, mrecyclerview.manimatingviewindex); 
} else { 
addview(child, -1); 
} 
} 
/** 
* remove a view from the currently attached recyclerview if needed. layoutmanagers should 
* use this method to completely remove a child view that is no longer needed. 
* layoutmanagers should strongly consider recycling removed views using 
* {@link recycler#recycleview(android.view.view)}. 
* 
* @param child view to remove 
*/ 
public void removeview(view child) { 
final adapter adapter = mrecyclerview.getadapter(); 
if (adapter != null) { 
adapter.onviewdetachedfromwindow(getchildviewholderint(child)); 
} 
mrecyclerview.onchilddetachedfromwindow(child); 
mrecyclerview.removeview(child); 
if (mrecyclerview.manimatingviewindex >= 0) { 
mrecyclerview.manimatingviewindex--; 
} 
} 
/** 
* remove a view from the currently attached recyclerview if needed. layoutmanagers should 
* use this method to completely remove a child view that is no longer needed. 
* layoutmanagers should strongly consider recycling removed views using 
* {@link recycler#recycleview(android.view.view)}. 
* 
* @param index index of the child view to remove 
*/ 
public void removeviewat(int index) { 
final view child = mrecyclerview.getchildat(index); 
if (child != null) { 
final adapter adapter = mrecyclerview.getadapter(); 
if (adapter != null) { 
adapter.onviewdetachedfromwindow(getchildviewholderint(child)); 
} 
mrecyclerview.onchilddetachedfromwindow(child); 
mrecyclerview.removeviewat(index); 
if (mrecyclerview.manimatingviewindex >= 0) { 
mrecyclerview.manimatingviewindex--; 
} 
} 
} 
/** 
* remove all views from the currently attached recyclerview. this will not recycle 
* any of the affected views; the layoutmanager is responsible for doing so if desired. 
*/ 
public void removeallviews() { 
final adapter adapter = mrecyclerview.getadapter(); 
// only remove non-animating views 
final int childcount = mrecyclerview.getchildcount() - mrecyclerview.mnumanimatingviews; 
for (int i = 0; i < childcount; i++) { 
final view child = mrecyclerview.getchildat(i); 
if (adapter != null) { 
adapter.onviewdetachedfromwindow(getchildviewholderint(child)); 
} 
mrecyclerview.onchilddetachedfromwindow(child); 
} 
for (int i = childcount - 1; i >= 0; i--) { 
mrecyclerview.removeviewat(i); 
if (mrecyclerview.manimatingviewindex >= 0) { 
mrecyclerview.manimatingviewindex--; 
} 
} 
} 
/** 
* returns the adapter position of the item represented by the given view. 
* 
* @param view the view to query 
* @return the adapter position of the item which is rendered by this view. 
*/ 
public int getposition(view view) { 
return ((recyclerview.layoutparams) view.getlayoutparams()).getviewposition(); 
} 
/** 
* <p>finds the view which represents the given adapter position.</p> 
* <p>this method traverses each child since it has no information about child order. 
* override this method to improve performance if your layoutmanager keeps data about 
* child views.</p> 
* 
* @param position position of the item in adapter 
* @return the child view that represents the given position or null if the position is not 
* visible 
*/ 
public view findviewbyposition(int position) { 
final int childcount = getchildcount(); 
for (int i = 0; i < childcount; i++) { 
view child = getchildat(i); 
if (getposition(child) == position) { 
return child; 
} 
} 
return null; 
} 
/** 
* temporarily detach a child view. 
* 
* <p>layoutmanagers may want to perform a lightweight detach operation to rearrange 
* views currently attached to the recyclerview. generally layoutmanager implementations 
* will want to use {@link #detachandscrapview(android.view.view, recyclerview.recycler)} 
* so that the detached view may be rebound and reused.</p> 
* 
* <p>if a layoutmanager uses this method to detach a view, it <em>must</em> 
* {@link #attachview(android.view.view, int, recyclerview.layoutparams) reattach} 
* or {@link #removedetachedview(android.view.view) fully remove} the detached view 
* before the layoutmanager entry point method called by recyclerview returns.</p> 
* 
* @param child child to detach 
*/ 
public void detachview(view child) { 
if (dispatch_temp_detach) { 
viewcompat.dispatchstarttemporarydetach(child); 
} 
mrecyclerview.detachviewfromparent(child); 
} 
/** 
* temporarily detach a child view. 
* 
* <p>layoutmanagers may want to perform a lightweight detach operation to rearrange 
* views currently attached to the recyclerview. generally layoutmanager implementations 
* will want to use {@link #detachandscrapview(android.view.view, recyclerview.recycler)} 
* so that the detached view may be rebound and reused.</p> 
* 
* <p>if a layoutmanager uses this method to detach a view, it <em>must</em> 
* {@link #attachview(android.view.view, int, recyclerview.layoutparams) reattach} 
* or {@link #removedetachedview(android.view.view) fully remove} the detached view 
* before the layoutmanager entry point method called by recyclerview returns.</p> 
* 
* @param index index of the child to detach 
*/ 
public void detachviewat(int index) { 
if (dispatch_temp_detach) { 
viewcompat.dispatchstarttemporarydetach(mrecyclerview.getchildat(index)); 
} 
mrecyclerview.detachviewfromparent(index); 
if (mrecyclerview.manimatingviewindex >= 0) { 
--mrecyclerview.manimatingviewindex; 
} 
} 
/** 
* reattach a previously {@link #detachview(android.view.view) detached} view. 
* this method should not be used to reattach views that were previously 
* {@link #detachandscrapview(android.view.view, recyclerview.recycler)} scrapped}. 
* 
* @param child child to reattach 
* @param index intended child index for child 
* @param lp layoutparams for child 
*/ 
public void attachview(view child, int index, layoutparams lp) { 
mrecyclerview.attachviewtoparent(child, index, lp); 
if (mrecyclerview.manimatingviewindex >= 0) { 
++mrecyclerview.manimatingviewindex; 
} 
if (dispatch_temp_detach) { 
viewcompat.dispatchfinishtemporarydetach(child); 
} 
} 
/** 
* reattach a previously {@link #detachview(android.view.view) detached} view. 
* this method should not be used to reattach views that were previously 
* {@link #detachandscrapview(android.view.view, recyclerview.recycler)} scrapped}. 
* 
* @param child child to reattach 
* @param index intended child index for child 
*/ 
public void attachview(view child, int index) { 
attachview(child, index, (layoutparams) child.getlayoutparams()); 
} 
/** 
* reattach a previously {@link #detachview(android.view.view) detached} view. 
* this method should not be used to reattach views that were previously 
* {@link #detachandscrapview(android.view.view, recyclerview.recycler)} scrapped}. 
* 
* @param child child to reattach 
*/ 
public void attachview(view child) { 
attachview(child, -1); 
} 
/** 
* finish removing a view that was previously temporarily 
* {@link #detachview(android.view.view) detached}. 
* 
* @param child detached child to remove 
*/ 
public void removedetachedview(view child) { 
mrecyclerview.removedetachedview(child, false); 
} 
/** 
* detach a child view and add it to a {@link recycler recycler's} scrap heap. 
* 
* <p>scrapping a view allows it to be rebound and reused to show updated or 
* different data.</p> 
* 
* @param child child to detach and scrap 
* @param recycler recycler to deposit the new scrap view into 
*/ 
public void detachandscrapview(view child, recycler recycler) { 
detachview(child); 
recycler.scrapview(child); 
} 
/** 
* detach a child view and add it to a {@link recycler recycler's} scrap heap. 
* 
* <p>scrapping a view allows it to be rebound and reused to show updated or 
* different data.</p> 
* 
* @param index index of child to detach and scrap 
* @param recycler recycler to deposit the new scrap view into 
*/ 
public void detachandscrapviewat(int index, recycler recycler) { 
final view child = getchildat(index); 
detachviewat(index); 
recycler.scrapview(child); 
} 
/** 
* remove a child view and recycle it using the given recycler. 
* 
* @param child child to remove and recycle 
* @param recycler recycler to use to recycle child 
*/ 
public void removeandrecycleview(view child, recycler recycler) { 
removeview(child); 
recycler.recycleview(child); 
} 
/** 
* remove a child view and recycle it using the given recycler. 
* 
* @param index index of child to remove and recycle 
* @param recycler recycler to use to recycle child 
*/ 
public void removeandrecycleviewat(int index, recycler recycler) { 
final view view = getchildat(index); 
removeviewat(index); 
recycler.recycleview(view); 
} 
/** 
* return the current number of child views attached to the parent recyclerview. 
* this does not include child views that were temporarily detached and/or scrapped. 
* 
* @return number of attached children 
*/ 
public int getchildcount() { 
return mrecyclerview != null ? 
mrecyclerview.getchildcount() - mrecyclerview.mnumanimatingviews : 0; 
} 
/** 
* return the child view at the given index 
* @param index index of child to return 
* @return child view at index 
*/ 
public view getchildat(int index) { 
return mrecyclerview != null ? mrecyclerview.getchildat(index) : null; 
} 
/** 
* return the width of the parent recyclerview 
* 
* @return width in pixels 
*/ 
public int getwidth() { 
return mrecyclerview != null ? mrecyclerview.getwidth() : 0; 
} 
/** 
* return the height of the parent recyclerview 
* 
* @return height in pixels 
*/ 
public int getheight() { 
return mrecyclerview != null ? mrecyclerview.getheight() : 0; 
} 
/** 
* return the left padding of the parent recyclerview 
* 
* @return padding in pixels 
*/ 
public int getpaddingleft() { 
return mrecyclerview != null ? mrecyclerview.getpaddingleft() : 0; 
} 
/** 
* return the top padding of the parent recyclerview 
* 
* @return padding in pixels 
*/ 
public int getpaddingtop() { 
return mrecyclerview != null ? mrecyclerview.getpaddingtop() : 0; 
} 
/** 
* return the right padding of the parent recyclerview 
* 
* @return padding in pixels 
*/ 
public int getpaddingright() { 
return mrecyclerview != null ? mrecyclerview.getpaddingright() : 0; 
} 
/** 
* return the bottom padding of the parent recyclerview 
* 
* @return padding in pixels 
*/ 
public int getpaddingbottom() { 
return mrecyclerview != null ? mrecyclerview.getpaddingbottom() : 0; 
} 
/** 
* return the start padding of the parent recyclerview 
* 
* @return padding in pixels 
*/ 
public int getpaddingstart() { 
return mrecyclerview != null ? viewcompat.getpaddingstart(mrecyclerview) : 0; 
} 
/** 
* return the end padding of the parent recyclerview 
* 
* @return padding in pixels 
*/ 
public int getpaddingend() { 
return mrecyclerview != null ? viewcompat.getpaddingend(mrecyclerview) : 0; 
} 
/** 
* returns true if the recyclerview this layoutmanager is bound to has focus. 
* 
* @return true if the recyclerview has focus, false otherwise. 
* @see view#isfocused() 
*/ 
public boolean isfocused() { 
return mrecyclerview != null && mrecyclerview.isfocused(); 
} 
/** 
* returns true if the recyclerview this layoutmanager is bound to has or contains focus. 
* 
* @return true if the recyclerview has or contains focus 
* @see view#hasfocus() 
*/ 
public boolean hasfocus() { 
return mrecyclerview != null && mrecyclerview.hasfocus(); 
} 
/** 
* return the number of items in the adapter bound to the parent recyclerview 
* 
* @return items in the bound adapter 
*/ 
public int getitemcount() { 
final adapter a = mrecyclerview != null ? mrecyclerview.getadapter() : null; 
return a != null ? a.getitemcount() : 0; 
} 
/** 
* offset all child views attached to the parent recyclerview by dx pixels along 
* the horizontal axis. 
* 
* @param dx pixels to offset by 
*/ 
public void offsetchildrenhorizontal(int dx) { 
if (mrecyclerview != null) { 
mrecyclerview.offsetchildrenhorizontal(dx); 
} 
} 
/** 
* offset all child views attached to the parent recyclerview by dy pixels along 
* the vertical axis. 
* 
* @param dy pixels to offset by 
*/ 
public void offsetchildrenvertical(int dy) { 
if (mrecyclerview != null) { 
mrecyclerview.offsetchildrenvertical(dy); 
} 
} 
/** 
* temporarily detach and scrap all currently attached child views. views will be scrapped 
* into the given recycler. the recycler may prefer to reuse scrap views before 
* other views that were previously recycled. 
* 
* @param recycler recycler to scrap views into 
*/ 
public void detachandscrapattachedviews(recycler recycler) { 
final int childcount = getchildcount(); 
for (int i = childcount - 1; i >= 0; i--) { 
final view v = getchildat(i); 
detachviewat(i); 
recycler.scrapview(v); 
} 
} 
/** 
* recycles the scrapped views. 
* <p> 
* when a view is detached and removed, it does not trigger a viewgroup invalidate. this is 
* the expected behavior if scrapped views are used for animations. otherwise, we need to 
* call remove and invalidate recyclerview to ensure ui update. 
* 
* @param recycler recycler 
* @param remove whether scrapped views should be removed from viewgroup or not. this 
* method will invalidate recyclerview if it removes any scrapped child. 
*/ 
void removeandrecyclescrapint(recycler recycler, boolean remove) { 
final int scrapcount = recycler.getscrapcount(); 
for (int i = 0; i < scrapcount; i++) { 
final view scrap = recycler.getscrapviewat(i); 
if (remove) { 
mrecyclerview.removedetachedview(scrap, false); 
} 
recycler.quickrecyclescrapview(scrap); 
} 
recycler.clearscrap(); 
if (remove && scrapcount > 0) { 
mrecyclerview.invalidate(); 
} 
} 
/** 
* measure a child view using standard measurement policy, taking the padding 
* of the parent recyclerview and any added item decorations into account. 
* 
* <p>if the recyclerview can be scrolled in either dimension the caller may 
* pass 0 as the widthused or heightused parameters as they will be irrelevant.</p> 
* 
* @param child child view to measure 
* @param widthused width in pixels currently consumed by other views, if relevant 
* @param heightused height in pixels currently consumed by other views, if relevant 
*/ 
public void measurechild(view child, int widthused, int heightused) { 
final layoutparams lp = (layoutparams) child.getlayoutparams(); 
final rect insets = mrecyclerview.getitemdecorinsetsforchild(child); 
widthused += insets.left + insets.right; 
heightused += insets.top + insets.bottom; 
final int widthspec = getchildmeasurespec(getwidth(), 
getpaddingleft() + getpaddingright() + widthused, lp.width, 
canscrollhorizontally()); 
final int heightspec = getchildmeasurespec(getheight(), 
getpaddingtop() + getpaddingbottom() + heightused, lp.height, 
canscrollvertically()); 
child.measure(widthspec, heightspec); 
} 
/** 
* measure a child view using standard measurement policy, taking the padding 
* of the parent recyclerview, any added item decorations and the child margins 
* into account. 
* 
* <p>if the recyclerview can be scrolled in either dimension the caller may 
* pass 0 as the widthused or heightused parameters as they will be irrelevant.</p> 
* 
* @param child child view to measure 
* @param widthused width in pixels currently consumed by other views, if relevant 
* @param heightused height in pixels currently consumed by other views, if relevant 
*/ 
public void measurechildwithmargins(view child, int widthused, int heightused) { 
final layoutparams lp = (layoutparams) child.getlayoutparams(); 
final rect insets = mrecyclerview.getitemdecorinsetsforchild(child); 
widthused += insets.left + insets.right; 
heightused += insets.top + insets.bottom; 
final int widthspec = getchildmeasurespec(getwidth(), 
getpaddingleft() + getpaddingright() + 
lp.leftmargin + lp.rightmargin + widthused, lp.width, 
canscrollhorizontally()); 
final int heightspec = getchildmeasurespec(getheight(), 
getpaddingtop() + getpaddingbottom() + 
lp.topmargin + lp.bottommargin + heightused, lp.height, 
canscrollvertically()); 
child.measure(widthspec, heightspec); 
} 
/** 
* calculate a measurespec value for measuring a child view in one dimension. 
* 
* @param parentsize size of the parent view where the child will be placed 
* @param padding total space currently consumed by other elements of parent 
* @param childdimension desired size of the child view, or match_parent/wrap_content. 
* generally obtained from the child view's layoutparams 
* @param canscroll true if the parent recyclerview can scroll in this dimension 
* 
* @return a measurespec value for the child view 
*/ 
public static int getchildmeasurespec(int parentsize, int padding, int childdimension, 
boolean canscroll) { 
int size = math.max(0, parentsize - padding); 
int resultsize = 0; 
int resultmode = 0; 
if (canscroll) { 
if (childdimension >= 0) { 
resultsize = childdimension; 
resultmode = measurespec.exactly; 
} else { 
// match_parent can't be applied since we can scroll in this dimension, wrap 
// instead using unspecified. 
resultsize = 0; 
resultmode = measurespec.unspecified; 
} 
} else { 
if (childdimension >= 0) { 
resultsize = childdimension; 
resultmode = measurespec.exactly; 
} else if (childdimension == layoutparams.fill_parent) { 
resultsize = size; 
resultmode = measurespec.exactly; 
} else if (childdimension == layoutparams.wrap_content) { 
resultsize = size; 
resultmode = measurespec.at_most; 
} 
} 
return measurespec.makemeasurespec(resultsize, resultmode); 
} 
/** 
* returns the measured width of the given child, plus the additional size of 
* any insets applied by {@link itemdecoration itemdecorations}. 
* 
* @param child child view to query 
* @return child's measured width plus <code>itemdecoration</code> insets 
* 
* @see view#getmeasuredwidth() 
*/ 
public int getdecoratedmeasuredwidth(view child) { 
final rect insets = ((layoutparams) child.getlayoutparams()).mdecorinsets; 
return child.getmeasuredwidth() + insets.left + insets.right; 
} 
/** 
* returns the measured height of the given child, plus the additional size of 
* any insets applied by {@link itemdecoration itemdecorations}. 
* 
* @param child child view to query 
* @return child's measured height plus <code>itemdecoration</code> insets 
* 
* @see view#getmeasuredheight() 
*/ 
public int getdecoratedmeasuredheight(view child) { 
final rect insets = ((layoutparams) child.getlayoutparams()).mdecorinsets; 
return child.getmeasuredheight() + insets.top + insets.bottom; 
} 
/** 
* lay out the given child view within the recyclerview using coordinates that 
* include any current {@link itemdecoration itemdecorations}. 
* 
* <p>layoutmanagers should prefer working in sizes and coordinates that include 
* item decoration insets whenever possible. this allows the layoutmanager to effectively 
* ignore decoration insets within measurement and layout code. see the following 
* methods:</p> 
* <ul> 
* <li>{@link #measurechild(view, int, int)}</li> 
* <li>{@link #measurechildwithmargins(view, int, int)}</li> 
* <li>{@link #getdecoratedleft(view)}</li> 
* <li>{@link #getdecoratedtop(view)}</li> 
* <li>{@link #getdecoratedright(view)}</li> 
* <li>{@link #getdecoratedbottom(view)}</li> 
* <li>{@link #getdecoratedmeasuredwidth(view)}</li> 
* <li>{@link #getdecoratedmeasuredheight(view)}</li> 
* </ul> 
* 
* @param child child to lay out 
* @param left left edge, with item decoration insets included 
* @param top top edge, with item decoration insets included 
* @param right right edge, with item decoration insets included 
* @param bottom bottom edge, with item decoration insets included 
* 
* @see view#layout(int, int, int, int) 
*/ 
public void layoutdecorated(view child, int left, int top, int right, int bottom) { 
final rect insets = ((layoutparams) child.getlayoutparams()).mdecorinsets; 
child.layout(left + insets.left, top + insets.top, right - insets.right, 
bottom - insets.bottom); 
} 
/** 
* returns the left edge of the given child view within its parent, offset by any applied 
* {@link itemdecoration itemdecorations}. 
* 
* @param child child to query 
* @return child left edge with offsets applied 
*/ 
public int getdecoratedleft(view child) { 
final rect insets = ((layoutparams) child.getlayoutparams()).mdecorinsets; 
return child.getleft() - insets.left; 
} 
/** 
* returns the top edge of the given child view within its parent, offset by any applied 
* {@link itemdecoration itemdecorations}. 
* 
* @param child child to query 
* @return child top edge with offsets applied 
*/ 
public int getdecoratedtop(view child) { 
final rect insets = ((layoutparams) child.getlayoutparams()).mdecorinsets; 
return child.gettop() - insets.top; 
} 
/** 
* returns the right edge of the given child view within its parent, offset by any applied 
* {@link itemdecoration itemdecorations}. 
* 
* @param child child to query 
* @return child right edge with offsets applied 
*/ 
public int getdecoratedright(view child) { 
final rect insets = ((layoutparams) child.getlayoutparams()).mdecorinsets; 
return child.getright() + insets.right; 
} 
/** 
* returns the bottom edge of the given child view within its parent, offset by any applied 
* {@link itemdecoration itemdecorations}. 
* 
* @param child child to query 
* @return child bottom edge with offsets applied 
*/ 
public int getdecoratedbottom(view child) { 
final rect insets = ((layoutparams) child.getlayoutparams()).mdecorinsets; 
return child.getbottom() + insets.bottom; 
} 
/** 
* called when searching for a focusable view in the given direction has failed 
* for the current content of the recyclerview. 
* 
* <p>this is the layoutmanager's opportunity to populate views in the given direction 
* to fulfill the request if it can. the layoutmanager should attach and return 
* the view to be focused. the default implementation returns null.</p> 
* 
* @param focused the currently focused view 
* @param direction one of {@link view#focus_up}, {@link view#focus_down}, 
* {@link view#focus_left}, {@link view#focus_right}, 
* {@link view#focus_backward}, {@link view#focus_forward} 
* or 0 for not applicable 
* @param recycler the recycler to use for obtaining views for currently offscreen items 
* @param state transient state of recyclerview 
* @return the chosen view to be focused 
*/ 
public view onfocussearchfailed(view focused, int direction, recycler recycler, 
state state) { 
return null; 
} 
/** 
* this method gives a layoutmanager an opportunity to intercept the initial focus search 
* before the default behavior of {@link focusfinder} is used. if this method returns 
* null focusfinder will attempt to find a focusable child view. if it fails 
* then {@link #onfocussearchfailed(view, int, recyclerview.recycler, recyclerview.state)} 
* will be called to give the layoutmanager an opportunity to add new views for items 
* that did not have attached views representing them. the layoutmanager should not add 
* or remove views from this method. 
* 
* @param focused the currently focused view 
* @param direction one of {@link view#focus_up}, {@link view#focus_down}, 
* {@link view#focus_left}, {@link view#focus_right}, 
* {@link view#focus_backward}, {@link view#focus_forward} 
* @return a descendant view to focus or null to fall back to default behavior. 
* the default implementation returns null. 
*/ 
public view oninterceptfocussearch(view focused, int direction) { 
return null; 
} 
/** 
* called when a child of the recyclerview wants a particular rectangle to be positioned 
* onto the screen. see {@link viewparent#requestchildrectangleonscreen(android.view.view, 
* android.graphics.rect, boolean)} for more details. 
* 
* <p>the base implementation will attempt to perform a standard programmatic scroll 
* to bring the given rect into view, within the padded area of the recyclerview.</p> 
* 
* @param child the direct child making the request. 
* @param rect the rectangle in the child's coordinates the child 
* wishes to be on the screen. 
* @param immediate true to forbid animated or delayed scrolling, 
* false otherwise 
* @return whether the group scrolled to handle the operation 
*/ 
public boolean requestchildrectangleonscreen(recyclerview parent, view child, rect rect, 
boolean immediate) { 
final int parentleft = getpaddingleft(); 
final int parenttop = getpaddingtop(); 
final int parentright = getwidth() - getpaddingright(); 
final int parentbottom = getheight() - getpaddingbottom(); 
final int childleft = child.getleft() + rect.left; 
final int childtop = child.gettop() + rect.top; 
final int childright = childleft + rect.right; 
final int childbottom = childtop + rect.bottom; 
final int offscreenleft = math.min(0, childleft - parentleft); 
final int offscreentop = math.min(0, childtop - parenttop); 
final int offscreenright = math.max(0, childright - parentright); 
final int offscreenbottom = math.max(0, childbottom - parentbottom); 
// favor the "start" layout direction over the end when bringing one side or the other 
// of a large rect into view. 
final int dx; 
if (viewcompat.getlayoutdirection(parent) == viewcompat.layout_direction_rtl) { 
dx = offscreenright != 0 ? offscreenright : offscreenleft; 
} else { 
dx = offscreenleft != 0 ? offscreenleft : offscreenright; 
} 
// favor bringing the top into view over the bottom 
final int dy = offscreentop != 0 ? offscreentop : offscreenbottom; 
if (dx != 0 || dy != 0) { 
if (immediate) { 
parent.scrollby(dx, dy); 
} else { 
parent.smoothscrollby(dx, dy); 
} 
return true; 
} 
return false; 
} 
/** 
* called when a descendant view of the recyclerview requests focus. 
* 
* <p>a layoutmanager wishing to keep focused views aligned in a specific 
* portion of the view may implement that behavior in an override of this method.</p> 
* 
* <p>if the layoutmanager executes different behavior that should override the default 
* behavior of scrolling the focused child on screen instead of running alongside it, 
* this method should return true.</p> 
* 
* @param parent the recyclerview hosting this layoutmanager 
* @param child direct child of the recyclerview containing the newly focused view 
* @param focused the newly focused view. this may be the same view as child 
* @return true if the default scroll behavior should be suppressed 
*/ 
public boolean onrequestchildfocus(recyclerview parent, view child, view focused) { 
return false; 
} 
/** 
* called if the recyclerview this layoutmanager is bound to has a different adapter set. 
* the layoutmanager may use this opportunity to clear caches and configure state such 
* that it can relayout appropriately with the new data and potentially new view types. 
* 
* <p>the default implementation removes all currently attached views.</p> 
* 
* @param oldadapter the previous adapter instance. will be null if there was previously no 
* adapter. 
* @param newadapter the new adapter instance. might be null if 
* {@link #setadapter(recyclerview.adapter)} is called with {@code null}. 
*/ 
public void onadapterchanged(adapter oldadapter, adapter newadapter) { 
} 
/** 
* called to populate focusable views within the recyclerview. 
* 
* <p>the layoutmanager implementation should return <code>true</code> if the default 
* behavior of {@link viewgroup#addfocusables(java.util.arraylist, int)} should be 
* suppressed.</p> 
* 
* <p>the default implementation returns <code>false</code> to trigger recyclerview 
* to fall back to the default viewgroup behavior.</p> 
* 
* @param recyclerview the recyclerview hosting this layoutmanager 
* @param views list of output views. this method should add valid focusable views 
* to this list. 
* @param direction one of {@link view#focus_up}, {@link view#focus_down}, 
* {@link view#focus_left}, {@link view#focus_right}, 
* {@link view#focus_backward}, {@link view#focus_forward} 
* @param focusablemode the type of focusables to be added. 
* 
* @return true to suppress the default behavior, false to add default focusables after 
* this method returns. 
* 
* @see #focusables_all 
* @see #focusables_touch_mode 
*/ 
public boolean onaddfocusables(recyclerview recyclerview, arraylist<view> views, 
int direction, int focusablemode) { 
return false; 
} 
/** 
* called when items have been added to the adapter. the layoutmanager may choose to 
* requestlayout if the inserted items would require refreshing the currently visible set 
* of child views. (e.g. currently empty space would be filled by appended items, etc.) 
* 
* @param recyclerview 
* @param positionstart 
* @param itemcount 
*/ 
public void onitemsadded(recyclerview recyclerview, int positionstart, int itemcount) { 
} 
/** 
* called when items have been removed from the adapter. 
* 
* @param recyclerview 
* @param positionstart 
* @param itemcount 
*/ 
public void onitemsremoved(recyclerview recyclerview, int positionstart, int itemcount) { 
} 
/** 
* <p>override this method if you want to support scroll bars.</p> 
* 
* <p>read {@link recyclerview#computehorizontalscrollextent()} for details.</p> 
* 
* <p>default implementation returns 0.</p> 
* 
* @param state current state of recyclerview 
* @return the horizontal extent of the scrollbar's thumb 
* @see recyclerview#computehorizontalscrollextent() 
*/ 
public int computehorizontalscrollextent(state state) { 
return 0; 
} 
/** 
* <p>override this method if you want to support scroll bars.</p> 
* 
* <p>read {@link recyclerview#computehorizontalscrolloffset()} for details.</p> 
* 
* <p>default implementation returns 0.</p> 
* 
* @param state current state of recyclerview where you can find total item count 
* @return the horizontal offset of the scrollbar's thumb 
* @see recyclerview#computehorizontalscrolloffset() 
*/ 
public int computehorizontalscrolloffset(state state) { 
return 0; 
} 
/** 
* <p>override this method if you want to support scroll bars.</p> 
* 
* <p>read {@link recyclerview#computehorizontalscrollrange()} for details.</p> 
* 
* <p>default implementation returns 0.</p> 
* 
* @param state current state of recyclerview where you can find total item count 
* @return the total horizontal range represented by the vertical scrollbar 
* @see recyclerview#computehorizontalscrollrange() 
*/ 
public int computehorizontalscrollrange(state state) { 
return 0; 
} 
/** 
* <p>override this method if you want to support scroll bars.</p> 
* 
* <p>read {@link recyclerview#computeverticalscrollextent()} for details.</p> 
* 
* <p>default implementation returns 0.</p> 
* 
* @param state current state of recyclerview 
* @return the vertical extent of the scrollbar's thumb 
* @see recyclerview#computeverticalscrollextent() 
*/ 
public int computeverticalscrollextent(state state) { 
return 0; 
} 
/** 
* <p>override this method if you want to support scroll bars.</p> 
* 
* <p>read {@link recyclerview#computeverticalscrolloffset()} for details.</p> 
* 
* <p>default implementation returns 0.</p> 
* 
* @param state current state of recyclerview where you can find total item count 
* @return the vertical offset of the scrollbar's thumb 
* @see recyclerview#computeverticalscrolloffset() 
*/ 
public int computeverticalscrolloffset(state state) { 
return 0; 
} 
/** 
* <p>override this method if you want to support scroll bars.</p> 
* 
* <p>read {@link recyclerview#computeverticalscrollrange()} for details.</p> 
* 
* <p>default implementation returns 0.</p> 
* 
* @param state current state of recyclerview where you can find total item count 
* @return the total vertical range represented by the vertical scrollbar 
* @see recyclerview#computeverticalscrollrange() 
*/ 
public int computeverticalscrollrange(state state) { 
return 0; 
} 
/** 
* measure the attached recyclerview. implementations must call 
* {@link #setmeasureddimension(int, int)} before returning. 
* 
* <p>the default implementation will handle exactly measurements and respect 
* the minimum width and height properties of the host recyclerview if measured 
* as unspecified. at_most measurements will be treated as exactly and the recyclerview 
* will consume all available space.</p> 
* 
* @param recycler recycler 
* @param state transient state of recyclerview 
* @param widthspec width {@link android.view.view.measurespec} 
* @param heightspec height {@link android.view.view.measurespec} 
*/ 
public void onmeasure(recycler recycler, state state, int widthspec, int heightspec) { 
final int widthmode = measurespec.getmode(widthspec); 
final int heightmode = measurespec.getmode(heightspec); 
final int widthsize = measurespec.getsize(widthspec); 
final int heightsize = measurespec.getsize(heightspec); 
int width = 0; 
int height = 0; 
switch (widthmode) { 
case measurespec.exactly: 
case measurespec.at_most: 
width = widthsize; 
break; 
case measurespec.unspecified: 
default: 
width = getminimumwidth(); 
break; 
} 
switch (heightmode) { 
case measurespec.exactly: 
case measurespec.at_most: 
height = heightsize; 
break; 
case measurespec.unspecified: 
default: 
height = getminimumheight(); 
break; 
} 
setmeasureddimension(width, height); 
} 
/** 
* {@link view#setmeasureddimension(int, int) set the measured dimensions} of the 
* host recyclerview. 
* 
* @param widthsize measured width 
* @param heightsize measured height 
*/ 
public void setmeasureddimension(int widthsize, int heightsize) { 
mrecyclerview.setmeasureddimension(widthsize, heightsize); 
} 
/** 
* @return the host recyclerview's {@link view#getminimumwidth()} 
*/ 
public int getminimumwidth() { 
return viewcompat.getminimumwidth(mrecyclerview); 
} 
/** 
* @return the host recyclerview's {@link view#getminimumheight()} 
*/ 
public int getminimumheight() { 
return viewcompat.getminimumheight(mrecyclerview); 
} 
/** 
* <p>called when the layoutmanager should save its state. this is a good time to save your 
* scroll position, configuration and anything else that may be required to restore the same 
* layout state if the layoutmanager is recreated.</p> 
* <p>recyclerview does not verify if the layoutmanager has changed between state save and 
* restore. this will let you share information between your layoutmanagers but it is also 
* your responsibility to make sure they use the same parcelable class.</p> 
* 
* @return necessary information for layoutmanager to be able to restore its state 
*/ 
public parcelable onsaveinstancestate() { 
return null; 
} 
public void onrestoreinstancestate(parcelable state) { 
} 
void stopsmoothscroller() { 
if (msmoothscroller != null) { 
msmoothscroller.stop(); 
} 
} 
private void onsmoothscrollerstopped(smoothscroller smoothscroller) { 
if (msmoothscroller == smoothscroller) { 
msmoothscroller = null; 
} 
} 
void removeandrecycleallviews(recycler recycler) { 
for (int i = getchildcount() - 1; i >= 0; i--) { 
removeandrecycleviewat(i, recycler); 
} 
} 
} 
/** 
* an itemdecoration allows the application to add a special drawing and layout offset 
* to specific item views from the adapter's data set. this can be useful for drawing dividers 
* between items, highlights, visual grouping boundaries and more. 
* 
* <p>all itemdecorations are drawn in the order they were added, before the item 
* views (in {@link itemdecoration#ondraw(canvas, recyclerview) ondraw()} and after the items 
* (in {@link itemdecoration#ondrawover(canvas, recyclerview)}.</p> 
*/ 
public static abstract class itemdecoration { 
/** 
* draw any appropriate decorations into the canvas supplied to the recyclerview. 
* any content drawn by this method will be drawn before the item views are drawn, 
* and will thus appear underneath the views. 
* 
* @param c canvas to draw into 
* @param parent recyclerview this itemdecoration is drawing into 
*/ 
public void ondraw(canvas c, recyclerview parent) { 
} 
/** 
* draw any appropriate decorations into the canvas supplied to the recyclerview. 
* any content drawn by this method will be drawn after the item views are drawn 
* and will thus appear over the views. 
* 
* @param c canvas to draw into 
* @param parent recyclerview this itemdecoration is drawing into 
*/ 
public void ondrawover(canvas c, recyclerview parent) { 
} 
/** 
* retrieve any offsets for the given item. each field of <code>outrect</code> specifies 
* the number of pixels that the item view should be inset by, similar to padding or margin. 
* the default implementation sets the bounds of outrect to 0 and returns. 
* 
* <p>if this itemdecoration does not affect the positioning of item views it should set 
* all four fields of <code>outrect</code> (left, top, right, bottom) to zero 
* before returning.</p> 
* 
* @param outrect rect to receive the output. 
* @param itemposition adapter position of the item to offset 
* @param parent recyclerview this itemdecoration is decorating 
*/ 
public void getitemoffsets(rect outrect, int itemposition, recyclerview parent) { 
outrect.set(0, 0, 0, 0); 
} 
} 
/** 
* an onitemtouchlistener allows the application to intercept touch events in progress at the 
* view hierarchy level of the recyclerview before those touch events are considered for 
* recyclerview's own scrolling behavior. 
* 
* <p>this can be useful for applications that wish to implement various forms of gestural 
* manipulation of item views within the recyclerview. onitemtouchlisteners may intercept 
* a touch interaction already in progress even if the recyclerview is already handling that 
* gesture stream itself for the purposes of scrolling.</p> 
*/ 
public interface onitemtouchlistener { 
/** 
* silently observe and/or take over touch events sent to the recyclerview 
* before they are handled by either the recyclerview itself or its child views. 
* 
* <p>the onintercepttouchevent methods of each attached onitemtouchlistener will be run 
* in the order in which each listener was added, before any other touch processing 
* by the recyclerview itself or child views occurs.</p> 
* 
* @param e motionevent describing the touch event. all coordinates are in 
* the recyclerview's coordinate system. 
* @return true if this onitemtouchlistener wishes to begin intercepting touch events, false 
* to continue with the current behavior and continue observing future events in 
* the gesture. 
*/ 
public boolean onintercepttouchevent(recyclerview rv, motionevent e); 
/** 
* process a touch event as part of a gesture that was claimed by returning true from 
* a previous call to {@link #onintercepttouchevent}. 
* 
* @param e motionevent describing the touch event. all coordinates are in 
* the recyclerview's coordinate system. 
*/ 
public void ontouchevent(recyclerview rv, motionevent e); 
} 
/** 
* an onscrolllistener can be set on a recyclerview to receive messages 
* when a scrolling event has occurred on that recyclerview. 
* 
* @see recyclerview#setonscrolllistener(onscrolllistener) 
*/ 
public interface onscrolllistener { 
public void onscrollstatechanged(int newstate); 
public void onscrolled(int dx, int dy); 
} 
/** 
* a recyclerlistener can be set on a recyclerview to receive messages whenever 
* a view is recycled. 
* 
* @see recyclerview#setrecyclerlistener(recyclerlistener) 
*/ 
public interface recyclerlistener { 
/** 
* this method is called whenever the view in the viewholder is recycled. 
* 
* @param holder the viewholder containing the view that was recycled 
*/ 
public void onviewrecycled(viewholder holder); 
} 
/** 
* interface definition for a callback to be invoked when an item in this 
* recyclerview.adapter has been clicked. 
*/ 
public interface onitemclicklistener { 
/** 
* callback method to be invoked when an item in this recyclerview.adapter has 
* been clicked. 
* <p> 
* implementers can call getposition(position) if they need 
* to access the data associated with the selected item. 
* 
* @param view the view within the recyclerview.adapter that was clicked (this 
* will be a view provided by the adapter) 
* @param position the position of the view in the adapter. 
*/ 
void onitemclick(view view, int position); 
} 
public static onitemclicklistener monitemclicklistener = null; 
/** 
* register a callback to be invoked when an item in this adapterview has 
* been clicked. 
* 
* @param listener the callback that will be invoked. 
*/ 
public void setonitemclicklistener(onitemclicklistener listener) { 
monitemclicklistener = listener; 
} 
/** 
* @return the callback to be invoked with an item in this adapterview has 
* been clicked, or null id no callback has been set. 
*/ 
public final onitemclicklistener getonitemclicklistener() { 
return monitemclicklistener; 
} 
/** 
* 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 implements onclicklistener{ 
public final view itemview; 
int mposition = no_position; 
int moldposition = no_position; 
long mitemid = no_id; 
int mitemviewtype = invalid_type; 
/** 
* this viewholder has been bound to a position; mposition, mitemid and mitemviewtype 
* are all valid. 
*/ 
static final int flag_bound = 1 << 0; 
/** 
* the data this viewholder's view reflects is stale and needs to be rebound 
* by the adapter. mposition and mitemid are consistent. 
*/ 
static final int flag_update = 1 << 1; 
/** 
* this viewholder's data is invalid. the identity implied by mposition and mitemid 
* are not to be trusted and may no longer match the item view type. 
* this viewholder must be fully rebound to different data. 
*/ 
static final int flag_invalid = 1 << 2; 
/** 
* this viewholder points at data that represents an item previously removed from the 
* data set. its view may still be used for things like outgoing animations. 
*/ 
static final int flag_removed = 1 << 3; 
/** 
* this viewholder should not be recycled. this flag is set via setisrecyclable() 
* and is intended to keep views around during animations. 
*/ 
static final int flag_not_recyclable = 1 << 4; 
private int mflags; 
private int misrecyclablecount = 0; 
// if non-null, view is currently considered scrap and may be reused for other data by the 
// scrap container. 
private recycler mscrapcontainer = null; 
@override 
public void onclick(view v) { 
if (monitemclicklistener != null) { 
monitemclicklistener.onitemclick(itemview, getposition()); 
} 
} 
public viewholder(view itemview) { 
if (itemview == null) { 
throw new illegalargumentexception("itemview may not be null"); 
} 
this.itemview = itemview; 
this.itemview.setonclicklistener(this); 
} 
void offsetposition(int offset) { 
if (moldposition == no_position) { 
moldposition = mposition; 
} 
mposition += offset; 
} 
void clearoldposition() { 
moldposition = no_position; 
} 
public final int getposition() { 
return moldposition == no_position ? mposition : moldposition; 
} 
public final long getitemid() { 
return mitemid; 
} 
public final int getitemviewtype() { 
return mitemviewtype; 
} 
boolean isscrap() { 
return mscrapcontainer != null; 
} 
void unscrap() { 
mscrapcontainer.unscrapview(this); 
mscrapcontainer = null; 
} 
void setscrapcontainer(recycler recycler) { 
mscrapcontainer = recycler; 
} 
boolean isinvalid() { 
return (mflags & flag_invalid) != 0; 
} 
boolean needsupdate() { 
return (mflags & flag_update) != 0; 
} 
boolean isbound() { 
return (mflags & flag_bound) != 0; 
} 
boolean isremoved() { 
return (mflags & flag_removed) != 0; 
} 
void setflags(int flags, int mask) { 
mflags = (mflags & ~mask) | (flags & mask); 
} 
void addflags(int flags) { 
mflags |= flags; 
} 
void clearflagsforsharedpool() { 
mflags = 0; 
} 
@override 
public string tostring() { 
final stringbuilder sb = new stringbuilder("viewholder{" + 
integer.tohexstring(hashcode()) + " position=" + mposition + " id=" + mitemid); 
if (isscrap()) sb.append(" scrap"); 
if (isinvalid()) sb.append(" invalid"); 
if (!isbound()) sb.append(" unbound"); 
if (needsupdate()) sb.append(" update"); 
if (isremoved()) sb.append(" removed"); 
sb.append("}"); 
return sb.tostring(); 
} 
/** 
* informs the recycler whether this item can be recycled. views which are not 
* recyclable will not be reused for other items until setisrecyclable() is 
* later set to true. calls to setisrecyclable() should always be paired (one 
* call to setisrecyclabe(false) should always be matched with a later call to 
* setisrecyclable(true)). pairs of calls may be nested, as the state is internally 
* reference-counted. 
* 
* @param recyclable whether this item is available to be recycled. default value 
* is true. 
*/ 
public final void setisrecyclable(boolean recyclable) { 
misrecyclablecount = recyclable ? misrecyclablecount - 1 : misrecyclablecount + 1; 
if (misrecyclablecount < 0) { 
misrecyclablecount = 0; 
log.e(view_log_tag, "isrecyclable decremented below 0: " + 
"unmatched pair of setisrecyable() calls"); 
} else if (!recyclable && misrecyclablecount == 1) { 
mflags |= flag_not_recyclable; 
} else if (recyclable && misrecyclablecount == 0) { 
mflags &= ~flag_not_recyclable; 
} 
} 
/** 
* @see {@link #setisrecyclable(boolean)} 
* 
* @return true if this item is available to be recycled, false otherwise. 
*/ 
public final boolean isrecyclable() { 
return (mflags & flag_not_recyclable) == 0 && 
!viewcompat.hastransientstate(itemview); 
} 
} 
/** 
* queued operation to happen when child views are updated. 
*/ 
private static class updateop { 
public static final int add = 0; 
public static final int remove = 1; 
public static final int update = 2; 
static final int pool_size = 30; 
public int cmd; 
public int positionstart; 
public int itemcount; 
public updateop(int cmd, int positionstart, int itemcount) { 
this.cmd = cmd; 
this.positionstart = positionstart; 
this.itemcount = itemcount; 
} 
} 
updateop obtainupdateop(int cmd, int positionstart, int itemcount) { 
updateop op = mupdateoppool.acquire(); 
if (op == null) { 
op = new updateop(cmd, positionstart, itemcount); 
} else { 
op.cmd = cmd; 
op.positionstart = positionstart; 
op.itemcount = itemcount; 
} 
return op; 
} 
void recycleupdateop(updateop op) { 
mupdateoppool.release(op); 
} 
/** 
* {@link android.view.viewgroup.marginlayoutparams layoutparams} subclass for children of 
* {@link recyclerview}. custom {@link layoutmanager layout managers} are encouraged 
* to create their own subclass of this <code>layoutparams</code> class 
* to store any additional required per-child view metadata about the layout. 
*/ 
public static class layoutparams extends marginlayoutparams { 
viewholder mviewholder; 
final rect mdecorinsets = new rect(); 
boolean minsetsdirty = true; 
public layoutparams(context c, attributeset attrs) { 
super(c, attrs); 
} 
public layoutparams(int width, int height) { 
super(width, height); 
} 
public layoutparams(marginlayoutparams source) { 
super(source); 
} 
public layoutparams(viewgroup.layoutparams source) { 
super(source); 
} 
public layoutparams(layoutparams source) { 
super((viewgroup.layoutparams) source); 
} 
/** 
* returns true if the view this layoutparams is attached to needs to have its content 
* updated from the corresponding adapter. 
* 
* @return true if the view should have its content updated 
*/ 
public boolean viewneedsupdate() { 
return mviewholder.needsupdate(); 
} 
/** 
* returns true if the view this layoutparams is attached to is now representing 
* potentially invalid data. a layoutmanager should scrap/recycle it. 
* 
* @return true if the view is invalid 
*/ 
public boolean isviewinvalid() { 
return mviewholder.isinvalid(); 
} 
/** 
* returns true if the adapter data item corresponding to the view this layoutparams 
* is attached to has been removed from the data set. a layoutmanager may choose to 
* treat it differently in order to animate its outgoing or disappearing state. 
* 
* @return true if the item the view corresponds to was removed from the data set 
*/ 
public boolean isitemremoved() { 
return mviewholder.isremoved(); 
} 
/** 
* returns the position that the view this layoutparams is attached to corresponds to. 
* 
* @return the adapter position this view was bound from 
*/ 
public int getviewposition() { 
return mviewholder.getposition(); 
} 
} 
/** 
* observer base class for watching changes to an {@link adapter}. 
* see {@link adapter#registeradapterdataobserver(adapterdataobserver)}. 
*/ 
public static abstract class adapterdataobserver { 
public void onchanged() { 
// do nothing 
} 
public void onitemrangechanged(int positionstart, int itemcount) { 
// do nothing 
} 
public void onitemrangeinserted(int positionstart, int itemcount) { 
// do nothing 
} 
public void onitemrangeremoved(int positionstart, int itemcount) { 
// do nothing 
} 
} 
/** 
* <p>base class for smooth scrolling. handles basic tracking of the target view position and 
* provides methods to trigger a programmatic scroll.</p> 
* 
* @see linearsmoothscroller 
*/ 
public static abstract class smoothscroller { 
private int mtargetposition = recyclerview.no_position; 
private recyclerview mrecyclerview; 
private layoutmanager mlayoutmanager; 
private boolean mpendinginitialrun; 
private boolean mrunning; 
private view mtargetview; 
private final action mrecyclingaction; 
public smoothscroller() { 
mrecyclingaction = new action(0, 0); 
} 
/** 
* starts a smooth scroll for the given target position. 
* <p>in each animation step, {@link recyclerview} will check 
* for the target view and call either 
* {@link #ontargetfound(android.view.view, recyclerview.state, smoothscroller.action)} or 
* {@link #onseektargetstep(int, int, recyclerview.state, smoothscroller.action)} until 
* smoothscroller is stopped.</p> 
* 
* <p>note that if recyclerview finds the target view, it will automatically stop the 
* smoothscroller. this <b>does not</b> mean that scroll will stop, it only means it will 
* stop calling smoothscroller in each animation step.</p> 
*/ 
void start(recyclerview recyclerview, layoutmanager layoutmanager) { 
mrecyclerview = recyclerview; 
mlayoutmanager = layoutmanager; 
if (mtargetposition == recyclerview.no_position) { 
throw new illegalargumentexception("invalid target position"); 
} 
mrecyclerview.mstate.mtargetposition = mtargetposition; 
mrunning = true; 
mpendinginitialrun = true; 
mtargetview = findviewbyposition(gettargetposition()); 
onstart(); 
mrecyclerview.mviewflinger.postonanimation(); 
} 
public void settargetposition(int targetposition) { 
mtargetposition = targetposition; 
} 
/** 
* @return the layoutmanager to which this smoothscroller is attached 
*/ 
public layoutmanager getlayoutmanager() { 
return mlayoutmanager; 
} 
/** 
* stops running the smoothscroller in each animation callback. note that this does not 
* cancel any existing {@link action} updated by 
* {@link #ontargetfound(android.view.view, recyclerview.state, smoothscroller.action)} or 
* {@link #onseektargetstep(int, int, recyclerview.state, smoothscroller.action)}. 
*/ 
final protected void stop() { 
if (!mrunning) { 
return; 
} 
onstop(); 
mrecyclerview.mstate.mtargetposition = recyclerview.no_position; 
mtargetview = null; 
mtargetposition = recyclerview.no_position; 
mpendinginitialrun = false; 
mrunning = false; 
// trigger a cleanup 
mlayoutmanager.onsmoothscrollerstopped(this); 
// clear references to avoid any potential leak by a custom smooth scroller 
mlayoutmanager = null; 
mrecyclerview = null; 
} 
/** 
* returns true if smoothscroller has beens started but has not received the first 
* animation 
* callback yet. 
* 
* @return true if this smoothscroller is waiting to start 
*/ 
public boolean ispendinginitialrun() { 
return mpendinginitialrun; 
} 
/** 
* @return true if smoothscroller is currently active 
*/ 
public boolean isrunning() { 
return mrunning; 
} 
/** 
* returns the adapter position of the target item 
* 
* @return adapter position of the target item or 
* {@link recyclerview#no_position} if no target view is set. 
*/ 
public int gettargetposition() { 
return mtargetposition; 
} 
private void onanimation(int dx, int dy) { 
if (!mrunning || mtargetposition == recyclerview.no_position) { 
stop(); 
} 
mpendinginitialrun = false; 
if (mtargetview != null) { 
// verify target position 
if (getchildposition(mtargetview) == mtargetposition) { 
ontargetfound(mtargetview, mrecyclerview.mstate, mrecyclingaction); 
mrecyclingaction.runinnecessary(mrecyclerview); 
stop(); 
} else { 
log.e(tag, "passed over target position while smooth scrolling."); 
mtargetview = null; 
} 
} 
if (mrunning) { 
onseektargetstep(dx, dy, mrecyclerview.mstate, mrecyclingaction); 
mrecyclingaction.runinnecessary(mrecyclerview); 
} 
} 
/** 
* @see recyclerview#getchildposition(android.view.view) 
*/ 
public int getchildposition(view view) { 
return mrecyclerview.getchildposition(view); 
} 
/** 
* @see recyclerview#getchildcount() 
*/ 
public int getchildcount() { 
return mrecyclerview.getchildcount(); 
} 
/** 
* @see recyclerview.layoutmanager#findviewbyposition(int) 
*/ 
public view findviewbyposition(int position) { 
return mrecyclerview.mlayout.findviewbyposition(position); 
} 
/** 
* @see recyclerview#scrolltoposition(int) 
*/ 
public void instantscrolltoposition(int position) { 
mrecyclerview.scrolltoposition(position); 
} 
protected void onchildattachedtowindow(view child) { 
if (getchildposition(child) == gettargetposition()) { 
mtargetview = child; 
if (debug) { 
log.d(tag, "smooth scroll target view has been attached"); 
} 
} 
} 
/** 
* normalizes the vector. 
* @param scrollvector the vector that points to the target scroll position 
*/ 
protected void normalize(pointf scrollvector) { 
final double magnitute = math.sqrt(scrollvector.x * scrollvector.x + scrollvector.y * 
scrollvector.y); 
scrollvector.x /= magnitute; 
scrollvector.y /= magnitute; 
} 
/** 
* called when smooth scroll is started. this might be a good time to do setup. 
*/ 
abstract protected void onstart(); 
/** 
* called when smooth scroller is stopped. this is a good place to cleanup your state etc. 
* @see #stop() 
*/ 
abstract protected void onstop(); 
/** 
* <p>recyclerview will call this method each time it scrolls until it can find the target 
* position in the layout.</p> 
* <p>smoothscroller should check dx, dy and if scroll should be changed, update the 
* provided {@link action} to define the next scroll.</p> 
* 
* @param dx last scroll amount horizontally 
* @param dy last scroll amount verticaully 
* @param state transient state of recyclerview 
* @param action if you want to trigger a new smooth scroll and cancel the previous one, 
* update this object. 
*/ 
abstract protected void onseektargetstep(int dx, int dy, state state, action action); 
/** 
* called when the target position is laid out. this is the last callback smoothscroller 
* will receive and it should update the provided {@link action} to define the scroll 
* details towards the target view. 
* @param targetview the view element which render the target position. 
* @param state transient state of recyclerview 
* @param action action instance that you should update to define final scroll action 
* towards the targetview 
* @return an {@link action} to finalize the smooth scrolling 
*/ 
abstract protected void ontargetfound(view targetview, state state, action action); 
/** 
* holds information about a smooth scroll request by a {@link smoothscroller}. 
*/ 
public static class action { 
public static final int undefined_duration = integer.min_value; 
private int mdx; 
private int mdy; 
private int mduration; 
private interpolator minterpolator; 
private boolean changed = false; 
// we track this variable to inform custom implementer if they are updating the action 
// in every animation callback 
private int consecutiveupdates = 0; 
/** 
* @param dx pixels to scroll horizontally 
* @param dy pixels to scroll vertically 
*/ 
public action(int dx, int dy) { 
this(dx, dy, undefined_duration, null); 
} 
/** 
* @param dx pixels to scroll horizontally 
* @param dy pixels to scroll vertically 
* @param duration duration of the animation in milliseconds 
*/ 
public action(int dx, int dy, int duration) { 
this(dx, dy, duration, null); 
} 
/** 
* @param dx pixels to scroll horizontally 
* @param dy pixels to scroll vertically 
* @param duration duration of the animation in milliseconds 
* @param interpolator interpolator to be used when calculating scroll position in each 
* animation step 
*/ 
public action(int dx, int dy, int duration, interpolator interpolator) { 
mdx = dx; 
mdy = dy; 
mduration = duration; 
minterpolator = interpolator; 
} 
private void runinnecessary(recyclerview recyclerview) { 
if (changed) { 
validate(); 
if (minterpolator == null) { 
if (mduration == undefined_duration) { 
recyclerview.mviewflinger.smoothscrollby(mdx, mdy); 
} else { 
recyclerview.mviewflinger.smoothscrollby(mdx, mdy, mduration); 
} 
} else { 
recyclerview.mviewflinger.smoothscrollby(mdx, mdy, mduration, minterpolator); 
} 
consecutiveupdates ++; 
if (consecutiveupdates > 10) { 
// a new action is being set in every animation step. this looks like a bad 
// implementation. inform developer. 
log.e(tag, "smooth scroll action is being updated too frequently. make sure" 
+ " you are not changing it unless necessary"); 
} 
changed = false; 
} else { 
consecutiveupdates = 0; 
} 
} 
private void validate() { 
if (minterpolator != null && mduration < 1) { 
throw new illegalstateexception("if you provide an interpolator, you must" 
+ " set a positive duration"); 
} else if (mduration < 1) { 
throw new illegalstateexception("scroll duration must be a positive number"); 
} 
} 
public int getdx() { 
return mdx; 
} 
public void setdx(int dx) { 
changed = true; 
mdx = dx; 
} 
public int getdy() { 
return mdy; 
} 
public void setdy(int dy) { 
changed = true; 
mdy = dy; 
} 
public int getduration() { 
return mduration; 
} 
public void setduration(int duration) { 
changed = true; 
mduration = duration; 
} 
public interpolator getinterpolator() { 
return minterpolator; 
} 
/** 
* sets the interpolator to calculate scroll steps 
* @param interpolator the interpolator to use. if you specify an interpolator, you must 
* also set the duration. 
* @see #setduration(int) 
*/ 
public void setinterpolator(interpolator interpolator) { 
changed = true; 
minterpolator = interpolator; 
} 
/** 
* updates the action with given parameters. 
* @param dx pixels to scroll horizontally 
* @param dy pixels to scroll vertically 
* @param duration duration of the animation in milliseconds 
* @param interpolator interpolator to be used when calculating scroll position in each 
* animation step 
*/ 
public void update(int dx, int dy, int duration, interpolator interpolator) { 
mdx = dx; 
mdy = dy; 
mduration = duration; 
minterpolator = interpolator; 
changed = true; 
} 
} 
} 
static class adapterdataobservable extends observable<adapterdataobserver> { 
public boolean hasobservers() { 
return !mobservers.isempty(); 
} 
public void notifychanged() { 
// since onchanged() is implemented by the app, it could do anything, including 
// removing itself from {@link mobservers} - and that could cause problems if 
// an iterator is used on the arraylist {@link mobservers}. 
// to avoid such problems, just march thru the list in the reverse order. 
for (int i = mobservers.size() - 1; i >= 0; i--) { 
mobservers.get(i).onchanged(); 
} 
} 
public void notifyitemrangechanged(int positionstart, int itemcount) { 
// since onitemrangechanged() is implemented by the app, it could do anything, including 
// removing itself from {@link mobservers} - and that could cause problems if 
// an iterator is used on the arraylist {@link mobservers}. 
// to avoid such problems, just march thru the list in the reverse order. 
for (int i = mobservers.size() - 1; i >= 0; i--) { 
mobservers.get(i).onitemrangechanged(positionstart, itemcount); 
} 
} 
public void notifyitemrangeinserted(int positionstart, int itemcount) { 
// since onitemrangeinserted() is implemented by the app, it could do anything, 
// including removing itself from {@link mobservers} - and that could cause problems if 
// an iterator is used on the arraylist {@link mobservers}. 
// to avoid such problems, just march thru the list in the reverse order. 
for (int i = mobservers.size() - 1; i >= 0; i--) { 
mobservers.get(i).onitemrangeinserted(positionstart, itemcount); 
} 
} 
public void notifyitemrangeremoved(int positionstart, int itemcount) { 
// since onitemrangeremoved() is implemented by the app, it could do anything, including 
// removing itself from {@link mobservers} - and that could cause problems if 
// an iterator is used on the arraylist {@link mobservers}. 
// to avoid such problems, just march thru the list in the reverse order. 
for (int i = mobservers.size() - 1; i >= 0; i--) { 
mobservers.get(i).onitemrangeremoved(positionstart, itemcount); 
} 
} 
} 
static class savedstate extends basesavedstate { 
parcelable mlayoutstate; 
/** 
* called by creator 
*/ 
savedstate(parcel in) { 
super(in); 
mlayoutstate = in.readparcelable(layoutmanager.class.getclassloader()); 
} 
/** 
* called by onsaveinstancestate 
*/ 
savedstate(parcelable superstate) { 
super(superstate); 
} 
@override 
public void writetoparcel(parcel dest, int flags) { 
super.writetoparcel(dest, flags); 
dest.writeparcelable(mlayoutstate, 0); 
} 
private void copyfrom(savedstate other) { 
mlayoutstate = other.mlayoutstate; 
} 
public static final parcelable.creator<savedstate> creator 
= new parcelable.creator<savedstate>() { 
@override 
public savedstate createfromparcel(parcel in) { 
return new savedstate(in); 
} 
@override 
public savedstate[] newarray(int size) { 
return new savedstate[size]; 
} 
}; 
} 
/** 
* <p>contains useful information about the current recyclerview state like target scroll 
* position or view focus. state object can also keep arbitrary data, identified by resource 
* ids.</p> 
* <p>often times, recyclerview components will need to pass information between each other. 
* to provide a well defined data bus between components, recyclerview passes the same state 
* object to component callbacks and these components can use it to exchange data.</p> 
* <p>if you implement custom components, you can use state's put/get/remove methods to pass 
* data between your components without needing to manage their lifecycles.</p> 
*/ 
public static class state { 
private int mtargetposition = recyclerview.no_position; 
private arraymap<viewholder, itemholderinfo> mprelayoutholdermap = 
new arraymap<viewholder, itemholderinfo>(); 
private arraymap<viewholder, itemholderinfo> mpostlayoutholdermap = 
new arraymap<viewholder, itemholderinfo>(); 
private sparsearray<object> mdata; 
/** 
* number of items adapter has. 
*/ 
private int mitemcount = 0; 
/** 
* number of items adapter had in the previous layout. 
*/ 
private int mpreviouslayoutitemcount = 0; 
/** 
* number of items that were not laid out but has been deleted from the adapter after the 
* previous layout. 
*/ 
private int mdeletedinvisibleitemcountsincepreviouslayout = 0; 
private boolean mstructurechanged = false; 
private boolean minprelayout = false; 
state reset() { 
mtargetposition = recyclerview.no_position; 
if (mdata != null) { 
mdata.clear(); 
} 
mitemcount = 0; 
mstructurechanged = false; 
return this; 
} 
public boolean isprelayout() { 
return minprelayout; 
} 
/** 
* removes the mapping from the specified id, if there was any. 
* @param resourceid id of the resource you want to remove. it is suggested to use r.id.* to 
* preserve cross functionality and avoid conflicts. 
*/ 
public void remove(int resourceid) { 
if (mdata == null) { 
return; 
} 
mdata.remove(resourceid); 
} 
/** 
* gets the object mapped from the specified id, or <code>null</code> 
* if no such data exists. 
* 
* @param resourceid id of the resource you want to remove. it is suggested to use r.id.* 
* to 
* preserve cross functionality and avoid conflicts. 
*/ 
public <t> t get(int resourceid) { 
if (mdata == null) { 
return null; 
} 
return (t) mdata.get(resourceid); 
} 
/** 
* adds a mapping from the specified id to the specified value, replacing the previous 
* mapping from the specified key if there was one. 
* 
* @param resourceid id of the resource you want to add. it is suggested to use r.id.* to 
* preserve cross functionality and avoid conflicts. 
* @param data the data you want to associate with the resourceid. 
*/ 
public void put(int resourceid, object data) { 
if (mdata == null) { 
mdata = new sparsearray<object>(); 
} 
mdata.put(resourceid, data); 
} 
/** 
* if scroll is triggered to make a certain item visible, this value will return the 
* adapter index of that item. 
* @return adapter index of the target item or 
* {@link recyclerview#no_position} if there is no target 
* position. 
*/ 
public int gettargetscrollposition() { 
return mtargetposition; 
} 
/** 
* returns if current scroll has a target position. 
* @return true if scroll is being triggered to make a certain position visible 
* @see #gettargetscrollposition() 
*/ 
public boolean hastargetscrollposition() { 
return mtargetposition != recyclerview.no_position; 
} 
/** 
* @return true if the structure of the data set has changed since the last call to 
* onlayoutchildren, false otherwise 
*/ 
public boolean didstructurechange() { 
return mstructurechanged; 
} 
/** 
* @return total number of items to be laid out. note that, this number is not necessarily 
* equal to the number of items in the adapter, so you should always use this number for 
* your position calculations and never call adapter directly. 
*/ 
public int getitemcount() { 
return minprelayout ? 
(mpreviouslayoutitemcount - mdeletedinvisibleitemcountsincepreviouslayout) : 
mitemcount; 
} 
} 
/** 
* internal listener that manages items after animations finish. this is how items are 
* retained (not recycled) during animations, but allowed to be recycled afterwards. 
* it depends on the contract with the itemanimator to call the appropriate dispatch*finished() 
* method on the animator's listener when it is done animating any item. 
*/ 
private class itemanimatorrestorelistener implements itemanimator.itemanimatorlistener { 
@override 
public void onremovefinished(viewholder item) { 
item.setisrecyclable(true); 
removeanimatingview(item.itemview); 
removedetachedview(item.itemview, false); 
} 
@override 
public void onaddfinished(viewholder item) { 
item.setisrecyclable(true); 
removeanimatingview(item.itemview); 
} 
@override 
public void onmovefinished(viewholder item) { 
item.setisrecyclable(true); 
removeanimatingview(item.itemview); 
} 
}; 
/** 
* this class defines the animations that take place on items as changes are made 
* to the adapter. 
* 
* subclasses of itemanimator can be used to implement custom animations for actions on 
* viewholder items. the recyclerview will manage retaining these items while they 
* are being animated, but implementors must call the appropriate "finished" 
* method when each item animation is done ({@link #dispatchremovefinished(viewholder)}, 
* {@link #dispatchmovefinished(viewholder)}, or {@link #dispatchaddfinished(viewholder)}). 
* 
* <p>by default, recyclerview uses {@link defaultitemanimator}</p> 
* 
* @see #setitemanimator(itemanimator) 
*/ 
public static abstract class itemanimator { 
private itemanimatorlistener mlistener = null; 
private arraylist<itemanimatorfinishedlistener> mfinishedlisteners = 
new arraylist<itemanimatorfinishedlistener>(); 
private long maddduration = 120; 
private long mremoveduration = 120; 
private long mmoveduration = 250; 
/** 
* gets the current duration for which all move animations will run. 
* 
* @return the current move duration 
*/ 
public long getmoveduration() { 
return mmoveduration; 
} 
/** 
* sets the current duration for which all move animations will run. 
* 
* @param moveduration the current move duration 
*/ 
public void setmoveduration(long moveduration) { 
mmoveduration = moveduration; 
} 
/** 
* gets the current duration for which all add animations will run. 
* 
* @return the current add duration 
*/ 
public long getaddduration() { 
return maddduration; 
} 
/** 
* sets the current duration for which all add animations will run. 
* 
* @param addduration the current add duration 
*/ 
public void setaddduration(long addduration) { 
maddduration = addduration; 
} 
/** 
* gets the current duration for which all remove animations will run. 
* 
* @return the current remove duration 
*/ 
public long getremoveduration() { 
return mremoveduration; 
} 
/** 
* sets the current duration for which all remove animations will run. 
* 
* @param removeduration the current remove duration 
*/ 
public void setremoveduration(long removeduration) { 
mremoveduration = removeduration; 
} 
/** 
* internal only: 
* sets the listener that must be called when the animator is finished 
* animating the item (or immediately if no animation happens). this is set 
* internally and is not intended to be set by external code. 
* 
* @param listener the listener that must be called. 
*/ 
void setlistener(itemanimatorlistener listener) { 
mlistener = listener; 
} 
/** 
* called when there are pending animations waiting to be started. this state 
* is governed by the return values from {@link #animateadd(viewholder) animateadd()}, 
* {@link #animatemove(viewholder, int, int, int, int) animatemove()}, and 
* {@link #animateremove(viewholder) animateremove()}, which inform the 
* recyclerview that the itemanimator wants to be called later to start the 
* associated animations. runpendinganimations() will be scheduled to be run 
* on the next frame. 
*/ 
abstract public void runpendinganimations(); 
/** 
* called when an item is removed from the recyclerview. implementors can choose 
* whether and how to animate that change, but must always call 
* {@link #dispatchremovefinished(viewholder)} when done, either 
* immediately (if no animation will occur) or after the animation actually finishes. 
* the return value indicates whether an animation has been set up and whether the 
* itemanimators {@link #runpendinganimations()} method should be called at the 
* next opportunity. this mechanism allows itemanimator to set up individual animations 
* as separate calls to {@link #animateadd(viewholder) animateadd()}, 
* {@link #animatemove(viewholder, int, int, int, int) animatemove()}, and 
* {@link #animateremove(viewholder) animateremove()} come in one by one, then 
* start the animations together in the later call to {@link #runpendinganimations()}. 
* 
* <p>this method may also be called for disappearing items which continue to exist in the 
* recyclerview, but for which the system does not have enough information to animate 
* them out of view. in that case, the default animation for removing items is run 
* on those items as well.</p> 
* 
* @param holder the item that is being removed. 
* @return true if a later call to {@link #runpendinganimations()} is requested, 
* false otherwise. 
*/ 
abstract public boolean animateremove(viewholder holder); 
/** 
* called when an item is added to the recyclerview. implementors can choose 
* whether and how to animate that change, but must always call 
* {@link #dispatchaddfinished(viewholder)} when done, either 
* immediately (if no animation will occur) or after the animation actually finishes. 
* the return value indicates whether an animation has been set up and whether the 
* itemanimators {@link #runpendinganimations()} method should be called at the 
* next opportunity. this mechanism allows itemanimator to set up individual animations 
* as separate calls to {@link #animateadd(viewholder) animateadd()}, 
* {@link #animatemove(viewholder, int, int, int, int) animatemove()}, and 
* {@link #animateremove(viewholder) animateremove()} come in one by one, then 
* start the animations together in the later call to {@link #runpendinganimations()}. 
* 
* <p>this method may also be called for appearing items which were already in the 
* recyclerview, but for which the system does not have enough information to animate 
* them into view. in that case, the default animation for adding items is run 
* on those items as well.</p> 
* 
* @param holder the item that is being added. 
* @return true if a later call to {@link #runpendinganimations()} is requested, 
* false otherwise. 
*/ 
abstract public boolean animateadd(viewholder holder); 
/** 
* called when an item is moved in the recyclerview. implementors can choose 
* whether and how to animate that change, but must always call 
* {@link #dispatchmovefinished(viewholder)} when done, either 
* immediately (if no animation will occur) or after the animation actually finishes. 
* the return value indicates whether an animation has been set up and whether the 
* itemanimators {@link #runpendinganimations()} method should be called at the 
* next opportunity. this mechanism allows itemanimator to set up individual animations 
* as separate calls to {@link #animateadd(viewholder) animateadd()}, 
* {@link #animatemove(viewholder, int, int, int, int) animatemove()}, and 
* {@link #animateremove(viewholder) animateremove()} come in one by one, then 
* start the animations together in the later call to {@link #runpendinganimations()}. 
* 
* @param holder the item that is being moved. 
* @return true if a later call to {@link #runpendinganimations()} is requested, 
* false otherwise. 
*/ 
abstract public boolean animatemove(viewholder holder, int fromx, int fromy, 
int tox, int toy); 
/** 
* method to be called by subclasses when a remove animation is done. 
* 
* @param item the item which has been removed 
*/ 
public final void dispatchremovefinished(viewholder item) { 
if (mlistener != null) { 
mlistener.onremovefinished(item); 
} 
} 
/** 
* method to be called by subclasses when a move animation is done. 
* 
* @param item the item which has been moved 
*/ 
public final void dispatchmovefinished(viewholder item) { 
if (mlistener != null) { 
mlistener.onmovefinished(item); 
} 
} 
/** 
* method to be called by subclasses when an add animation is done. 
* 
* @param item the item which has been added 
*/ 
public final void dispatchaddfinished(viewholder item) { 
if (mlistener != null) { 
mlistener.onaddfinished(item); 
} 
} 
/** 
* method called when an animation on a view should be ended immediately. 
* this could happen when other events, like scrolling, occur, so that 
* animating views can be quickly put into their proper end locations. 
* implementations should ensure that any animations running on the item 
* are canceled and affected properties are set to their end values. 
* also, appropriate dispatch methods (e.g., {@link #dispatchaddfinished(viewholder)} 
* should be called since the animations are effectively done when this 
* method is called. 
* 
* @param item the item for which an animation should be stopped. 
*/ 
abstract public void endanimation(viewholder item); 
/** 
* method called when all item animations should be ended immediately. 
* this could happen when other events, like scrolling, occur, so that 
* animating views can be quickly put into their proper end locations. 
* implementations should ensure that any animations running on any items 
* are canceled and affected properties are set to their end values. 
* also, appropriate dispatch methods (e.g., {@link #dispatchaddfinished(viewholder)} 
* should be called since the animations are effectively done when this 
* method is called. 
*/ 
abstract public void endanimations(); 
/** 
* method which returns whether there are any item animations currently running. 
* this method can be used to determine whether to delay other actions until 
* animations end. 
* 
* @return true if there are any item animations currently running, false otherwise. 
*/ 
abstract public boolean isrunning(); 
/** 
* like {@link #isrunning()}, this method returns whether there are any item 
* animations currently running. addtionally, the listener passed in will be called 
* when there are no item animations running, either immediately (before the method 
* returns) if no animations are currently running, or when the currently running 
* animations are {@link #dispatchanimationsfinished() finished}. 
* 
* <p>note that the listener is transient - it is either called immediately and not 
* stored at all, or stored only until it is called when running animations 
* are finished sometime later.</p> 
* 
* @param listener a listener to be called immediately if no animations are running 
* or later when currently-running animations have finished. a null listener is 
* equivalent to calling {@link #isrunning()}. 
* @return true if there are any item animations currently running, false otherwise. 
*/ 
public final boolean isrunning(itemanimatorfinishedlistener listener) { 
boolean running = isrunning(); 
if (listener != null) { 
if (!running) { 
listener.onanimationsfinished(); 
} else { 
mfinishedlisteners.add(listener); 
} 
} 
return running; 
} 
/** 
* the interface to be implemented by listeners to animation events from this 
* itemanimator. this is used internally and is not intended for developers to 
* create directly. 
*/ 
private interface itemanimatorlistener { 
void onremovefinished(viewholder item); 
void onaddfinished(viewholder item); 
void onmovefinished(viewholder item); 
} 
/** 
* this method should be called by itemanimator implementations to notify 
* any listeners that all pending and active item animations are finished. 
*/ 
public final void dispatchanimationsfinished() { 
final int count = mfinishedlisteners.size(); 
for (int i = 0; i < count; ++i) { 
mfinishedlisteners.get(i).onanimationsfinished(); 
} 
mfinishedlisteners.clear(); 
} 
/** 
* this interface is used to inform listeners when all pending or running animations 
* in an itemanimator are finished. this can be used, for example, to delay an action 
* in a data set until currently-running animations are complete. 
* 
* @see #isrunning(itemanimatorfinishedlistener) 
*/ 
public interface itemanimatorfinishedlistener { 
void onanimationsfinished(); 
} 
} 
/** 
* internal data structure that holds information about an item's bounds. 
* this information is used in calculating item animations. 
*/ 
private static class itemholderinfo { 
viewholder holder; 
int left, top, right, bottom; 
int position; 
itemholderinfo(viewholder holder, int left, int top, int right, int bottom, int position) { 
this.holder = holder; 
this.left = left; 
this.top = top; 
this.right = right; 
this.bottom = bottom; 
this.position = position; 
} 
} 
} 

以上所述是小编给大家介绍的解决recyclerview无法onitemclick问题的两种方法,希望对大家有所帮助

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网