Today we will talk about recyclerView 1.2.0 version of the new MergeAdapter function
Add dependencies as follows:
// We are still in beta now, please change to official implementation later'androidx. Recyclerview: recyclerview: 1.2.0 - alpha03'
Copy the code
MergeAdapter
Since RecyclerView can only bind one Adapter, if different ViewHolder needs to be implemented, getItemViewType can only be rewritten to achieve this, which will cause a little high coupling and not easy to expand
MergeAdapter as its name implies is to merge multiple adapters into one adapter, set to RecyclerView, so as to realize a adapter responsible for the rendering of a layout (or a business logic data rendering), so as to achieve business code decoupling
In the actual business process, there is often a list of the same data, nested in different pages; This is a good time to use MergeAdapter;
For example, different page headers display different layouts, but the following data list is the same (HeaderAdapter) recommendation/introduction/advertising list + (Comment list/video list), which can well realize the decoupling of different business logic code. Otherwise you must couple the HeaderAdapter code into NormalAdapter
Another feature that we use a lot is to pull up and load more features; It is ideal for MergeAdapter implementation
Let’s take a quick look at the use of MergeAdapter
class MergeAdapterActivity : AppCompatActivity() {
private var mergeAdapter: MergeAdapter = MergeAdapter()
private var headerAdapter: HeaderAdapter = HeaderAdapter()
private var normalAdapter: NormalAdapter = NormalAdapter()
private var footAdapter: FootAdapter = FootAdapter()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_merge_adapter) recycler_view.layoutManager = LinearLayoutManager(this) val mergeAdapter = Add (0) normalAdapter.data.add(0) footadapter.data.add (0) // Mergeadapter.addadapter (headerAdapter) // Render normalAdapter data mergeAdapter.addAdapter(normalAdapter) Mergeadapter.addadapter (footAdapter) recycler_view.adapter = mergeAdapter} // Add data to normalAdapter fun addNormalData(view: View) { normalAdapter.data.add(normalAdapter.data.size) normalAdapter.notifyItemInserted(normalAdapter.data.size - 1) } Funremovefootdata (view: View) { normalAdapter.data.removeAt(0) normalAdapter.notifyItemRemoved(0) } }Copy the code
The renderings are as follows
It’s very simple to use, you just call addAdapter to addAdapter, how do you use the old Adapter to add and remove the refresh data or how do you use it, basically the same thing
MergeAdapter also has the removeAdapter method to remove the Adapter, so that all attempts to render the Adapter are removed
If you look at the MergeAdapter source code, you will find that the core logic is implemented by MergeAdapterController
MergeAdapter.Config
MergeAdapter Supports setting mergeAdapter.config in the constructor
/**
* Creates a MergeAdapter with the given config and the given adapters in the given order.
*
* @param config The configuration for this MergeAdapter
* @param adapters The list of adapters to add
* @see Config.Builder
*/
@SafeVarargs
public MergeAdapter(
@NonNull Config config,
@NonNull Adapter<? extends ViewHolder>... adapters) {
this(config, Arrays.asList(adapters));
}
Copy the code
Mergeadapter. Config can now configure isolateViewTypes and stableIdMode
isolateViewTypes
/**
* If {@code false}, {@link MergeAdapter} assumes all assigned adapters share a global
* view type pool such that they use the same view types to refer to the same
* ....
*/
public final boolean isolateViewTypes;
Copy the code
The isolateViewTypes are used to set whether each Adapter has its own view Type pool. False indicates that each Adapter shares a view Type pool. True indicates that each Adapter uses its own view Type pool. The default value is true
Let’s start with some simple code
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(r.layout.activity_merge_adapter) // Add a data.headerAdapter.data.add (0) normalAdapter.data.add(0) Footadapter.data.add (0) recycler_view1.layoutManager = LinearLayoutManager(this) // Set IsolateViewTypes=false
val mergeAdapter1 = MergeAdapter(MergeAdapter.Config.Builder().setIsolateViewTypes(false).build()) mergeAdapter1.addAdapter(headerAdapter) mergeAdapter1.addAdapter(normalAdapter) mergeAdapter1.addAdapter(footAdapter) recycler_view1.adapter = mergeAdapter1 recycler_view2.layoutManager = LinearLayoutManager(this) // Set IsolateViewTypes=true
val mergeAdapter2 = MergeAdapter(MergeAdapter.Config.Builder().setIsolateViewTypes(true).build())
mergeAdapter2.addAdapter(headerAdapter)
mergeAdapter2.addAdapter(normalAdapter)
mergeAdapter2.addAdapter(footAdapter)
recycler_view2.adapter = mergeAdapter2
}
Copy the code
The renderings are as follows:
NormalAdapter and footAdapter do not create their own ViewHolder when isolateViewTypes=false and itemViewType is the same for each adapter (default is 0). It’s the ViewHolder of the created headerAdapter (HeaderViewHolder background is red, NormalViewHolder background is blue, FootViewHolder background is black)
Key source code is as follows
class MergeAdapterController implements NestedAdapterWrapper.Callback {
...
public ViewHolder onCreateViewHolder(ViewGroup parent, int globalViewType) {
NestedAdapterWrapper wrapper = mViewTypeStorage.getWrapperForGlobalType(globalViewType);
returnwrapper.onCreateViewHolder(parent, globalViewType); } @NonNull @Override public NestedAdapterWrapper getWrapperForGlobalType(int globalViewType) { // List<NestedAdapterWrapper> nestedAdapterWrappers = mGlobalTypeToWrapper. Get ( globalViewType);if (nestedAdapterWrappers == null || nestedAdapterWrappers.isEmpty()) {
throw new IllegalArgumentException("Cannot find the wrapper for global view"
+ " type " + globalViewType);
}
// just returnThe first one since they are shared The ViewHolder is then created by calling the onCreateViewHolder method of the first AdapterreturnnestedAdapterWrappers.get(0); }}Copy the code
Therefore, when isolateViewTypes=false, it is relatively important to clarify which Adapter ViewHolder is being created to avoid confusing problems
When isolateViewTypes=true, each Adapter creates its own ViewHolder, which needs no further explanation
stableIdMode
StableIdMode is an enumeration with three values: NO_STABLE_IDS, ISOLATED_STABLE_IDS, SHARED_STABLE_IDS; It is used to set the return value of Adapter’s hasStableIds
class MergeAdapterController implements NestedAdapterWrapper.Callback {
...
public boolean hasStableIds() {
return mStableIdMode != NO_STABLE_IDS;
}
}
Copy the code
For this parameter, use the default value NO_STABLE_IDS
MergeAdapter limit
- Can’t mistake complex dynamic display of different types
ViewHolder
.MergeAdapter
Is based on eachAdapter
Displaying data in the order of; Or you need to cooperateItemViewType
Implement complex business scenarios - Due to the
LayoutManager
Is set inRecyclerView
On, so eachAdapter
The layout is the sameLayoutManager
)
That’s all I can think of for now.
GetItemViewType Possible pits
This refers to the getItemViewType method of the ViewHolder, not the getItemViewType method of the Adapter
Here said the pit for androidx. Recyclerview: recyclerview: 1.2.0 – alpha03 closed beta version, if later versions Please ignore
Why would you say that, because it might not return the value that you expected
When setting isolateViewTypes=true, we code and log
Class MergeAdapterActivity:AppCompatActivity() {
private var headerAdapter: HeaderAdapter = HeaderAdapter()
private var normalAdapter: NormalAdapter = NormalAdapter()
private var footAdapter: FootAdapter = FootAdapter()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_merge_adapter)
headerAdapter.data.addAll(listOf(0, 1, 2))
normalAdapter.data.addAll(listOf(0, 1, 2))
footAdapter.data.addAll(listOf(0, 1, 2))
...
recycler_view2.layoutManager = LinearLayoutManager(this)
val mergeAdapter2 = MergeAdapter(MergeAdapter.Config.Builder().setIsolateViewTypes(true).build()) mergeAdapter2.addAdapter(headerAdapter) mergeAdapter2.addAdapter(normalAdapter) Mergeadapter2.addadapter (footAdapter) recycler_view2.adapter = mergeAdapter2}} // HeaderAdapter source code class HeaderAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() { var data: MutableList<Any? > = mutableListOf() override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { Log.i("Adapter"."create HeaderViewHolder, viewType:$viewType")
return HeaderViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_header, parent, falseOverride fun getItemViewType(position: Int): Int {return if (position == 0) 100 else 200
}
override fun getItemCount(): Int = data.size
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
Log.i("Adapter"."HeaderViewHolder onBindViewHolder, viewHolder getItemViewType: ${holder.itemViewType}, adapter getItemViewType: ${getItemViewType(position)}")
holder.itemView.tv_text.text = "header data: $position, itemViewType: ${holder.itemViewType}"}... } // NormalAdapter source code class NormalAdapter: recyclerview. Adapter< recyclerview. ViewHolder>() {var data: MutableList<Any? > = mutableListOf() override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { Log.i("Adapter"."create NormalViewHolder, viewType:$viewType")
return NormalViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_normal, parent, falseOverride fun getItemViewType(position: Int): Int {return if (position == 0) 300 else 400
}
override fun getItemCount(): Int = data.size
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
Log.i("Adapter"."NormalViewHolder onBindViewHolder, viewHolder getItemViewType: ${holder.itemViewType}, adapter getItemViewType: ${getItemViewType(position)}")
holder.itemView.tv_text.text = "normal data: $position, itemViewType: ${holder.itemViewType}"}... } // FootAdapter source code class FootAdapter: recyclerview. Adapter< recyclerview. ViewHolder>() {var data: MutableList<Any? > = mutableListOf() override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { Log.i("Adapter"."create FootViewHolder, viewType:$viewType")
return FootViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_foot, parent, falseOverride fun getItemViewType(position: Int): Int {return if (position == 0) 500 else 600
}
override fun getItemCount(): Int = data.size
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
Log.i("Adapter"."FootViewHolder onBindViewHolder, viewHolder getItemViewType: ${holder.itemViewType}, adapter getItemViewType: ${getItemViewType(position)}")
holder.itemView.tv_text.text = "foot data: $position, itemViewType: ${holder.itemViewType}"}... }Copy the code
The following logs are displayed:
Adapter: create HeaderViewHolder, viewType:100
Adapter: HeaderViewHolder onBindViewHolder, viewHolder getItemViewType: 0, adapter getItemViewType: 100
Adapter: create HeaderViewHolder, viewType:200
Adapter: HeaderViewHolder onBindViewHolder, viewHolder getItemViewType: 1, adapter getItemViewType: 200
Adapter: create HeaderViewHolder, viewType:200
Adapter: HeaderViewHolder onBindViewHolder, viewHolder getItemViewType: 1, adapter getItemViewType: 200
Adapter: create NormalViewHolder, viewType:300
Adapter: NormalViewHolder onBindViewHolder, viewHolder getItemViewType: 2, adapter getItemViewType: 300
Adapter: create NormalViewHolder, viewType:400
Adapter: NormalViewHolder onBindViewHolder, viewHolder getItemViewType: 3, adapter getItemViewType: 400
Adapter: create NormalViewHolder, viewType:400
Adapter: NormalViewHolder onBindViewHolder, viewHolder getItemViewType: 3, adapter getItemViewType: 400
Adapter: create FootViewHolder, viewType:500
Adapter: FootViewHolder onBindViewHolder, viewHolder getItemViewType: 4, adapter getItemViewType: 500
Adapter: create FootViewHolder, viewType:600
Adapter: FootViewHolder onBindViewHolder, viewHolder getItemViewType: 5, adapter getItemViewType: 600
Copy the code
The getItemViewType method of viewHolder does not return a custom value, but a value that increments from zero.
After analyzing the source code, let’s look at the effect of isolateViewTypes=false
class MergeAdapterActivity : AppCompatActivity() {
private var headerAdapter: HeaderAdapter = HeaderAdapter()
private var normalAdapter: NormalAdapter = NormalAdapter()
private var footAdapter: FootAdapter = FootAdapter()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_merge_adapter)
headerAdapter.data.addAll(listOf(0, 1, 2))
normalAdapter.data.addAll(listOf(0, 1, 2))
footAdapter.data.addAll(listOf(0, 1, 2))
recycler_view1.layoutManager = LinearLayoutManager(this)
val mergeAdapter1 = MergeAdapter(MergeAdapter.Config.Builder().setIsolateViewTypes(false).build()) mergeAdapter1.addAdapter(headerAdapter) mergeAdapter1.addAdapter(normalAdapter) Mergeadapter1.addadapter (footAdapter) recycler_view1.adapter = mergeAdapter1}}Copy the code
The following logs are displayed:
Adapter: create HeaderViewHolder, viewType:100
Adapter: HeaderViewHolder onBindViewHolder, viewHolder getItemViewType: 100, adapter getItemViewType: 100
Adapter: create HeaderViewHolder, viewType:200
Adapter: HeaderViewHolder onBindViewHolder, viewHolder getItemViewType: 200, adapter getItemViewType: 200
Adapter: create HeaderViewHolder, viewType:200
Adapter: HeaderViewHolder onBindViewHolder, viewHolder getItemViewType: 200, adapter getItemViewType: 200
Adapter: create NormalViewHolder, viewType:300
Adapter: NormalViewHolder onBindViewHolder, viewHolder getItemViewType: 300, adapter getItemViewType: 300
Adapter: create NormalViewHolder, viewType:400
Adapter: NormalViewHolder onBindViewHolder, viewHolder getItemViewType: 400, adapter getItemViewType: 400
Adapter: create NormalViewHolder, viewType:400
Adapter: NormalViewHolder onBindViewHolder, viewHolder getItemViewType: 400, adapter getItemViewType: 400
Adapter: create FootViewHolder, viewType:500
Adapter: FootViewHolder onBindViewHolder, viewHolder getItemViewType: 500, adapter getItemViewType: 500
Adapter: create FootViewHolder, viewType:600
Adapter: FootViewHolder onBindViewHolder, viewHolder getItemViewType: 600, adapter getItemViewType: 600
Adapter: create FootViewHolder, viewType:600
Adapter: FootViewHolder onBindViewHolder, viewHolder getItemViewType: 600, adapter getItemViewType: 600
Copy the code
The getItemViewType method of the viewHolder returns the custom value
Personally, I think it may be a Google bug, because after all, it is still in private beta. Let’s see if there is still such a problem after the official release
Let’s look at the source code, why?
/ / this is RecyclerView ViewHolder creation method, removed most of the code @ Nullable ViewHolder tryGetViewHolderForPositionByDeadline (int the position, boolean dryRun, long deadlineNs) { ...if(holder == null) { ... // Get ViewType, where mAdapter is MergeAdapter final inttype= mAdapter.getItemViewType(offsetPosition); .if(holder == null) { ... / / to createViewHolder holder = mAdapter. CreateViewHolder (RecyclerView. This,type); . }}...returnholder; } @nonNULL public final VH createViewHolder(@nonnull ViewGroup parent, int viewType) { try { ... // Assign viewType directly to viewholder mItemViewType holder. MItemViewType = viewType;returnholder; } finally { TraceCompat.endSection(); }} // This is the getItemViewType method of MergeAdapter, @override public int getItemViewType(int position) {Override public int getItemViewType(int position) {returnmController.getItemViewType(position); } // MergeAdapterController getItemViewType public int getItemViewType(int globalPosition) {WrapperAndLocalPosition wrapperAndPos = findWrapperAndLocalPosition(globalPosition); // mWrapper is the NestedAdapterWrapper class int itemViewType = wrapperAndPos.mWrapper.getItemViewType(wrapperAndPos.mLocalPosition); releaseWrapperAndLocalPosition(wrapperAndPos);returnitemViewType; } // the getItemViewType method of NestedAdapterWrapper // mViewTypeLookup is the implementation class of ViewTypeLookuplocalPosition) {
return mViewTypeLookup.localToGlobal(adapter.getItemViewType(localPosition));
}
Copy the code
Let’s look at the definition of ViewTypeLookup and its two implementation classes, which correspond to IsolatedViewType= True and IsolatedViewType= False
Interface ViewTypeLookup {// Defines the conversion from each adapter's own viewType to global viewType intlocalToGlobal(int localType); Int globalToLocal(int globalType); void dispose(); }Copy the code
First look at the implementation class IsolatedViewType= True
class IsolatedViewTypeStorage implements ViewTypeStorage { SparseArray<NestedAdapterWrapper> mGlobalTypeToWrapper = new SparseArray<>(); int mNextViewType = 0; Int obtainViewType(NestedAdapterWrapper) {int nextId = mNextViewType++; mGlobalTypeToWrapper.put(nextId, wrapper);return nextId;
}
class WrapperViewTypeLookup implements ViewTypeLookup {
private SparseIntArray mLocalToGlobalMapping = new SparseIntArray(1);
private SparseIntArray mGlobalToLocalMapping = new SparseIntArray(1);
final NestedAdapterWrapper mWrapper;
WrapperViewTypeLookup(NestedAdapterWrapper wrapper) {
mWrapper = wrapper;
}
@Override
public int localToGlobal(int localType) {
int index = mLocalToGlobalMapping.indexOfKey(localType);
if (index > -1) {
returnmLocalToGlobalMapping.valueAt(index); Int globalType = obtainViewType(mWrapper); / / cached mLocalToGlobalMapping. Put (localType, globalType);
mGlobalToLocalMapping.put(globalType, localType);
returnglobalType; } // Directly from the mGlobalToLocalMapping cachelocalThe Type,returnOut @ Override public int globalToLocal (int globalType) {int index = mGlobalToLocalMapping. IndexOfKey (globalType);if (index < 0) {
throw new IllegalStateException("requested global type " + globalType + " does"
+ " not belong to the adapter:" + mWrapper.adapter);
}
returnmGlobalToLocalMapping.valueAt(index); }... }}Copy the code
You can see that globalType is incremented from 0 when IsolatedViewType=true
Let’s look at the subclass implementation when IsolatedViewType=false
class SharedIdRangeViewTypeStorage implements ViewTypeStorage { ... class WrapperViewTypeLookup implements ViewTypeLookup { final NestedAdapterWrapper mWrapper; WrapperViewTypeLookup(NestedAdapterWrapper wrapper) { mWrapper = wrapper; } // Return directlylocalType as globalType@override public intlocalToGlobal(int localType) {
...
return localType;
}
@Override
public int globalToLocal(int globalType) {
returnglobalType; }... }}Copy the code
Let’s go back to the getItemViewType method of MergeAdapterController
public int getItemViewType(int globalPosition) { WrapperAndLocalPosition wrapperAndPos = findWrapperAndLocalPosition(globalPosition); GlobalType is returned when 'IsolatedViewType='true// When 'IsolatedViewType=false`, here will return the value of adapter own custom int itemViewType = wrapperAndPos. MWrapper. GetItemViewType (wrapperAndPos. MLocalPosition); releaseWrapperAndLocalPosition(wrapperAndPos); // This results in the ViewHolder value being incorrectreturn itemViewType;
}
Copy the code
So why is the value of the viewType parameter of the onCreateViewHolder method okay? That’s because MergeAdapterController converts the globalType to the localType of each Adapter in onCreateViewHolder (see the source code below)
Public ViewHolder onCreateViewHolder(ViewGroup parent) int globalViewType) { NestedAdapterWrapper wrapper = mViewTypeStorage.getWrapperForGlobalType(globalViewType);returnwrapper.onCreateViewHolder(parent, globalViewType); } // NestedAdapterWrapper onCreateViewHolder onCreateViewHolder(ViewGroup parent, Int globalViewType) {// Convert globalViewType tolocalType
int localType = mViewTypeLookup.globalToLocal(globalViewType);
return adapter.onCreateViewHolder(parent, localType);
}
Copy the code
The source code for this problem has been analyzed to this point. Personally, I think this problem may be a bug or the ViewHolder should provide a new method to retrieve the value of the viewType
Note: this question is for androidx. Recyclerview: recyclerview: 1.2.0 – alpha03 closed beta version of the source code analysis, may be in later versions, if you have any change or repair the later versions of the source code to this problem Please ignore the source analysis
So if you use MergeAdapter in the process must pay attention to this problem, otherwise will crash or inexplicable problems yo
Let’s take a look at some of the methods we use to get position
getAdapterPosition vs getLayoutPosition
GetAdapterPosition returns the position of the ViewHolder bound data in the Adapter
GetLayoutPosition returns the most recent computed position of the ViewHolder layout after rendering, consistent with the position seen by the user
GetLayoutPosition and getAdapterPosition are usually the same; Only when the Adapter data has changed and the Layout has not been drawn in time can it be different
While this simple explanation may seem a bit confusing, let’s try an experiment
Try the notifyDataSetChanged pair first
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
holder.itemView.tv_text.text = "data: $position"
holder.itemView.setOnClickListener {
Log.i("Adapter"."click viewHolder, adapterPosition: ${holder.adapterPosition}, layoutPosition: ${holder.layoutPosition}"Thread(Runnable {Thread(Runnable {Thread(Runnable {Thread(Runnable {Thread));while (true) {
Log.i("Adapter"."data: $position, adapterPosition: ${holder.adapterPosition}, layoutPosition: ${holder.layoutPosition}")
Thread.sleep(5L)
}
}).start()
}
Handler().postDelayed({
Log.i("Adapter"."==notifyDataSetChanged==")
notifyDataSetChanged()
}, 20)
}
Copy the code
Click on the third piece of data; The logs are as follows
Adapter: click viewHolder, adapterPosition: 2, layoutPosition: 2
Adapter: data: 2, adapterPosition: 2, layoutPosition: 2
Adapter: data: 2, adapterPosition: 2, layoutPosition: 2
Adapter: ==notifyDataSetChanged==
Adapter: data: 2, adapterPosition: -1, layoutPosition: 2
Adapter: data: 2, adapterPosition: -1, layoutPosition: 2
Adapter: data: 2, adapterPosition: -1, layoutPosition: -1
Adapter: data: 2, adapterPosition: 2, layoutPosition: 2
Adapter: data: 2, adapterPosition: 2, layoutPosition: 2
...
Copy the code
Because notifyDataSetChanged requires all data to be redrawn, the ViewHolder does not know which position in the Adapter is bound to until the drawing is complete, so -1 (NO_POSITION) is returned.
Instead, layoutPosition caches the previous value for a short time, and adapterPosition and layoutPosition are the same after a Layout is rendered
Let’s try notifyItemRemoved again
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
holder.itemView.tv_text.text = "data: $position"
holder.itemView.setOnClickListener {
Log.i("Adapter"."click viewHolder, adapterPosition: ${holder.adapterPosition}, layoutPosition: ${holder.layoutPosition}"Thread(Runnable {Thread(Runnable {Thread(Runnable {Thread(Runnable {Thread));while (true) {
Log.i("Adapter"."data: $position, adapterPosition: ${holder.adapterPosition}, layoutPosition: ${holder.layoutPosition}") thread.sleep (5L)}}).start()} Handler().postdelayed ({data.removeat (0)} log.i ()"Adapter"."==notifyItemRemoved==")
notifyItemRemoved(0)
}, 20)
}
Copy the code
Click on the third piece of data; The logs are as follows
Adapter: click viewHolder, adapterPosition: 2, layoutPosition: 2
Adapter: data: 2, adapterPosition: 2, layoutPosition: 2
Adapter: data: 2, adapterPosition: 2, layoutPosition: 2
Adapter: ==notifyItemRemoved==
Adapter: data: 2, adapterPosition: 1, layoutPosition: 2
Adapter: data: 2, adapterPosition: 1, layoutPosition: 2
Adapter: data: 2, adapterPosition: 1, layoutPosition: 1
Adapter: data: 2, adapterPosition: 1, layoutPosition: 1
Adapter: data: 2, adapterPosition: 1, layoutPosition: 1
Copy the code
Because RecyclerView can automatically calculate the position of the data bound to other ViewHolder in the Adapter based on the position removed by notifyItemRemoved, Therefore, the correct adapterPosition can be obtained immediately, but the Layout is not completed, so layoutPosition is still the previous value, until the Layout is completed, layoutPosition is the latest value
getBindingAdapterPosition vs getAbsoluteAdapterPosition
From recyclerview ` ` 1.2.0 ViewHolder has added new getBindingAdapterPosition and getAbsoluteAdapterPosition two methods;
The getAdapterPosition method introduced above is deprecated. The source code is as follows
/ * * * @return {@link #getBindingAdapterPosition()}
* @deprecated This method is confusing when adapters nest other adapters.
* If you are calling this in the context of an Adapter, you probably want to call
* {@link #getBindingAdapterPosition()} or if you want the position as {@link RecyclerView}
* sees it, you should call {@link #getAbsoluteAdapterPosition()}.
*/
@Deprecated
public final int getAdapterPosition() {
return getBindingAdapterPosition();
}
Copy the code
Value is returned directly visible getAdapterPosition getBindingAdapterPosition;
So what is the difference between getBindingAdapterPosition and getAbsoluteAdapterPosition?
Override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ... Recycler_view.layoutmanager = LinearLayoutManager(this) val mergeAdapter = mergeAdapter () Mergeadapter.addadapter (HeaderAdapter().apply {data.addall (listOf(1, 2, 3, 4), }) // Add NormalAdapter Mergeadapter.addadapter (NormalAdapter().apply {data.addall (listOf(1, 2, 3, 4), 5)) }) recycler_view.adapter = mergeAdapter } ... // NormalAdapter onBindViewHolder code Override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { holder.itemView.tv_text.text ="data: $position"
holder.itemView.setOnClickListener {
Log.i("Adapter"."click data: $position, bindPosition: ${holder.bindingAdapterPosition}, absolutePosition: ${holder.absoluteAdapterPosition}, layoutPosition: ${holder.layoutPosition}")}}Copy the code
When the first data of NormalAdapter is clicked, the following log is printed
// absolutePosition == 5; Adapter: click data: 0bindPosition: 0, absolutePosition: 5, layoutPosition: 5
Copy the code
Visible getBindingAdapterPosition returns the ViewHolder binding of the location of the data in their own Adapter (not considering the MergeAdapter add other Adapter)
GetAbsoluteAdapterPosition returns the ViewHolder binding data position in the MergeAdapter (according to the position of Adapter, then add the offset in the MergeAdapter)
So in most cases, after should be use getBindingAdapterPosition (instead of getAdapterPosition), if you need to consider the offset, use the getAbsoluteAdapterPosition
As for getLayoutPosition with getAbsoluteAdapterPosition similar, can calculate their adapter offset in the MergeAdapter, returns to its position in the MergeAdapter;
Since getLayoutPosition may be used in older code, it is important to prevent data from crossing boundaries when working with MergeAdapter
Well, that’s all for MergeAdapter!!