English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
PS: To be precise, it's been about three months since I last wrote a blog... I was busy as a bee for a while... Finally, I can spare some time to study other things...
1.The reuse mechanism of ListView
ListView is a control we often use. Although everyone uses it, not everyone is fully aware of the reuse mechanism of ListView, although in Android 5.After version 0, RecycleView was provided to replace ListView and GridView, offering a plug-and-play experience, which is what we call modularization. This article mainly discusses the reuse mechanism of ListView, so we will mention RecycleView. I took a look at a blog post by Guo Lin, a big shot in the field, on the deep analysis of ListView principles, so I have been studying for a while and would like to share my own understanding.
i.RecycleBin's basic principle
First, we need to talk about the basic principle of RecycleBin, which is also a key class for implementation of reuse. Next, we need to clarify the concept of ActiveView, which is actually the view visible on the UI screen (onScreenView) and also the View that interacts with the user. These views will be directly stored in the mActivityView array through RecycleBin for direct reuse. So when we scroll the ListView, some views are scrolled off the screen (offScreen View), and these views become ScrapView, which is the discarded View and can no longer interact with the user. Therefore, there is no need to draw these unnecessary views when the UI view changes. They will be stored in the mScrapView array by RecycleBin, but not destroyed, with the purpose of secondary reuse, that is, indirect reuse. When a new View needs to be displayed, first check if it exists in the mActivityView. If it exists, we can directly reuse it from the mActivityView array, which is direct reuse. Otherwise, we need to inflate View.
This is an overall flowchart, and the reuse mechanism is like this. So let's first understand what work ListView does when it is loaded for the first time, first executing the onLayout method...
/** * Subclasses should NOT override this method but {@link #layoutChildren()} * instead. */ @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); mInLayout = true; if (changed) { int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { getChildAt(i).forceLayout(); } mRecycler.markChildrenDirty(); } layoutChildren(); mInLayout = false; }
Here we can see that the onLayout method calls the layoutChildren() method, which is the process of laying out items, the layoutChildren() method will not be pasted as the code is too long. We just need to know that this is a way to layout the child Views in the ListView. When we first load the ListView, the array in RecycleBin has no data at all, so the first load always needs to inflate View, that is, create a new View. And when the first load happens, it loads the data from top to bottom, so the fillFromTop() method will be executed in layoutChildren(). fillFromTop() will execute filleDown() method.
/** * Fills the list from pos down to the end of the list view. * * @param pos The first position to put in the list * * @param nextTop The location where the top of the item associated with pos * should be drawn * * @return The view that is currently selected, if it happens to be in the * range that we draw. * * @param pos: The position of a drawn Item in the Adapter data source * @param nextTop: Represents the actual position of the currently drawn Item in the ListView... */ private View fillDown(int pos, int nextTop) { View selectedView = null; /** * end is used to determine whether the Item has filled the ListView */ int end = (getBottom() - getTop()) - mListPadding.bottom; while (nextTop < end && pos < mItemCount) { /** * nextTop < end ensures that we only need to cover the interface of the ListView with the newly added child View *pos < mItemCount ensures that the newly added sub-View has a corresponding data source item in the Adapter */ // is this the selected item?63; boolean selected = pos == mSelectedPosition; View child = makeAndAddView(pos, nextTop, true, mListPadding.left, selected); /** *Take the latest child's bottom value as the next child's top value and store it in nextTop */ nextTop = child.getBottom() + mDividerHeight; if (selected) { selectedView = child; } pos++; } return selectedView; }
There is a key method here, which is the makeAndAddView() method. This is the core part of how ListView displays items and also involves the reuse of ListView.
private View makeAndAddView(int position, int y, boolean flow, int childrenLeft, boolean selected) { View child; //Determine whether the data source has changed. if (!mDataChanged) { // Try to use an existing view for this position //If there is a View that can be reused directly in the mActivityView[] array, then directly obtain it and rearrange the layout. child = mRecycler.getActiveView(position); if (child != null) { // Found it -- we're using an existing child // This just needs to be positioned setupChild(child, position, y, flow, childrenLeft, selected, true); return child; } } // Make a new view for this position, or convert an unused view if possible /** *If there is no available View in the mActivityView[] array, then try to read from the mScrapView array. Then rearrange the layout. *If it can be obtained from the mScrapView array, then directly return by calling mAdapter.getView(position, scrapView, this); *If it cannot be obtained, then execute the method mAdapter.getView(position, null, this). */ child = obtainView(position, mIsScrap); // This needs to be positioned and measured setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]); return child; }
Here we can see that when the data source does not change, it will judge from the mActivityView array whether there is a View that can be directly reused. Many readers may not understand the process of direct reuse very well. For example, let's say a page of ListView can display10of the data, then at this time, when we slide an Item's distance, that is, remove the Item with position = 0 from the screen, and place the position = 10 of the Item into the screen, then position = 1of the Item can be directly obtained from the mActivityView array? This is possible. When we load the Item data for the first time, we have already added position = 0~9is added to the mActivityView array, then during the second load, since position = 1 If the Item is still in the mActivityView array, we can directly obtain it from the array and then re-layout. This means the direct reuse of the Item.
If we cannot obtain the View corresponding to position in the mActivityView array, then we try to obtain it from the discarded View array in mScrapView. Taking the previous example, when the Item with position = 0 is removed from the screen, it will first be Detached to separate the View from the view, clear the children, and then add the discarded View to the mScrapView array. When loading the Item with position = 10When the Item is not in the mActivityView array, it cannot be obtained, and the same is true for mScrapView where there is no postion = 10The corresponding discarded View, to put it simply, is that the mScrapView array only has one item, mScrapView[0], and there is definitely no mScrapView[10If we have this data, then we might think that it must be new data obtained from the getView method of the Adapter, but that is not the case. Although there is no corresponding discarded View in mScrapView, it will return the last cached View to convertview. That is, it returns the View corresponding to mScrapView[0]. The overall process is like this.
Here we can see that ListView always inflates only one page of Item in the getView method, that is, new View only executes the number of times for one page of Item. The subsequent Items are completed through direct reuse and indirect reuse.
Pay attention to a situation: for example, it is still a page of Item, but the Item with position = 0 has not completely slid out of the UI, position = 10The Item has not completely entered the UI, then the Item with position = 0 will not be detached, nor will it be added to the discarded View array, at this time mScrapView is empty, with no data, so position = 10The Item cannot directly reuse View from mActivityView because it is the first load. mActivityView[10is not existing, at the same time, mScrapView is empty, so position = 10The Item can only be re-generated View, that is, from the inflate method in the getView. The obtainView method is not specifically listed here, everyone can take a look themselves. obtainView actually determines whether a View can be obtained from the discarded View, if obtained, then execute:
if (scrapView != null) { child = mAdapter.getView(position, scrapView, this); }
If it can be obtained here, getView will pass scrapView. Otherwise:
else { child = mAdapter.getView(position, null, this); }
If it cannot be obtained, pass null, which will execute the method defined in our Adapter.
@Override public View getView(int position, View convertView, ViewGroup parent) { if(convertView == null){ convertView = View.inflate(context, R.layout.list_item_layout, null); } return convertView; }
As for the methods executed when swiping up, it is to fill the ListView from bottom to top, and it will also directly or indirectly reuse controls. Understanding the reuse mechanism is the key, so it is not difficult to understand the swipe up. To supplement, there is also a method in RecycleBin, setViewTypeCount(). This is for the getViewTypeCount() set in the Adapter. For each data type, setViewTypeCount() will open a separate RecycleBin recycling mechanism for each data type. We just need to know this. As for the multiple onLayout calls of ListView seen in Guo's blog, it is certain, due to the Android View loading mechanism, child controls need to be remeasured according to the size of the parent control, and it takes multiple measurements to be displayed on the UI. This is the reason for multiple view measurements. As for the multiple layout issues of ListView, I will not go into detail, in short, regardless of how many measurements, ListView will not execute repeated logic multiple times, that is, the data will not have multiple copies, only one copy will exist.
This is where the basic principle of ListView recycling and the recycling mechanism of RecycleBin are explained. The code posted is very little, just some key code, there is no need to study the code line by line, after all, there is still a big gap between us and the big shot. We just need to know the execution process and principle.
2.ViewHolder
To talk about this ViewHolder thing, many Android learners tend to confuse it with the ListView's recycling mechanism. Here, ViewHolder is also used during recycling, but it is not very related to the recycling mechanism.
@Override public View getView(int position, View convertView, ViewGroup parent) { final ViewHolder holder; ListViewItem itemData = items.get(position); if(convertView == null){ convertView = View.inflate(context, R.layout.list_item_layout, null); holder = new ViewHolder(); holder.userImg = (ImageView) convertView.findViewById(R.id.user_header_img); holder.userName = (TextView) convertView.findViewById(R.id.user_name); holder.userComment = (TextView) convertView.findViewById(R.id.user_comment); convertView.setTag(holder); }else{ holder = (ViewHolder) convertView.getTag(); } holder.userImg.setImageResource(itemData.getUserImg()); holder.userName.setText(itemData.getUserName()); holder.userComment.setText(itemData.getUserComment()); return convertView; } static class ViewHolder{ ImageView userImg; TextView userName; TextView userComment; }
When implementing Adapter, we usually add this ViewHolder thing. ViewHolder is unrelated to the reuse mechanism and principle. Its main purpose is to hold the reference of the controls in the item, thereby reducing the number of findViewById() calls, because findViewById() method also affects efficiency. Therefore, when reusing, it plays this role, reducing the number of method execution calls to increase efficiency. Here is a simple reminder, don't mix it up.
That's all for this article. I hope it will be helpful to everyone's learning and that everyone will support the Yelling Tutorial more.
Statement: The content of this article is from the Internet, and the copyright belongs to the original author. The content is contributed and uploaded by Internet users spontaneously. This website does not own the copyright, has not been manually edited, and does not assume any relevant legal liability. If you find any content suspected of copyright infringement, please send an email to: notice#w3Please report via email to codebox.com (replace # with @ when sending an email) and provide relevant evidence. Once verified, this site will immediately delete the content suspected of infringement.