Problem
Recently, I encountered a very difficult problem in the project, that is, the display of ListView after sliding is inexplicably disordered, and the problem is easily solved after I check the information on the Internet, but the cause of the problem is still unknown, so I am unwilling to settle down, read the source code, and finally clarify the "mystery".
Origin
The getView method in the Adapter is similar to the following:
The code is as follows: |
Copy code |
@ Override Public View getView (int position, View convertView, ViewGroup parent ){ ViewHolder holder; If (convertView = null ){ ConvertView = mLayout. inflate (R. layout ....); Holder = new ViewHolder (); Holder. textView = (TextView) convertView . FindViewById (R. id. textview ); ...... ConvertView. setTag (holder ); } Else { Holder = (ViewHolder) convertView. getTag (); } Holder. textView. setText (mText + position ); Return convertView; } |
In the Android source code, the getView method is implemented in the above form, such as ArrayAdapter. The advantage of this method is obvious. If the convertview of the position has been loaded, the system automatically caches the convertview of the position without changing the data set, avoid resource consumption during repeated loading.
Then the problem came. At that time, I was "self-made smart" and thought that when convertview = null, I only loaded the item layout and bound the related control ID, why do I need to add the content loading operation to it? In this way, the next time the cache is loaded, the content set operation will be omitted. Then, the problem of data display dislocation after the ListView sliding occurs -. -.
Cause
After looking at the source code, we found that the getView () and slide operations in the original AbListView were performed asynchronously, and the slide operation was executed in a FlingRunnable branch thread, therefore, the ListView may have moved to the tenth row when sliding, but the data in the second row may be directly used, which is the root cause of data loading disorder.
Add the FlingRunnable annotation in the source code:
The code is as follows: |
Copy code |
/** * Responsible for fling behavior. Use {@ link # start (int)} * Initiate a fling. Each frame of the fling is handled in {@ link # run ()}. * A FlingRunnable will keep re-posting itself until the fling is done. * */ Private class FlingRunnable implements Runnable { /** * Tracks the decay of a fling scroll */ Private final OverScroller mScroller; ...... } |
Solution
Therefore, the only solution is to cache the layout of the ChildView in convertview, but the data in the ChildView must be retrieved and loaded every time. In fact, ListView data loading and data caching are relatively complicated (a few related classes add up to finish the line =. =), So you have the opportunity to carefully study the source code in the future to better understand the principle.
In another case, the content is duplicated after the ListView is rolled.
The simplest solution is to directly set the value of the corresponding control, that is, to use the comments section in the case code, instead of determining whether the value of the assignment content is null. (Remove the if segment in the above code)
To solve this problem, avoid assigning values to elements in ViewHolder. Take the above code as an example:
The code is as follows: |
Copy code |
If (! T = null ){ MViewHolder. nameText. setText (t ); }
|
At this time, when t = null, viewHolder is not assigned a value. Therefore, when t = null, elements on the interface may not be updated, that is, repeat the previous one (this location View ). Therefore, you can add an else and assign a value to viewholder.
The code is as follows: |
Copy code |
If (! T = null ){ MViewHolder. nameText. setText (t ); } Else { MViewHolder. nameText. setText ("unknow "); }
|
In this way, the problem is solved.
Another scenario is:
// If the sub-control is ImageView, when the value is empty, the default image resource is set directly. If you use setBackgroundResource, the default image resource may not be set to ImageView. You must use setImageResource. SetBackgroundResource is the method in the View class, and setImageResource is the ImageView method. Using the former may cause the image to be not actually set to the ImageView. In this case, the ImageView displays the image with the last value assigned.