The strongest ListView optimization solution in the history of Android
In android development, Listview is a very important component. It automatically displays the specific content in the form of a list based on the length of the data. You can freely define the layout of each column of listview, however, when the listview has a large amount of data to load, it will occupy a large amount of memory, affecting performance.
The focus of this article is to introduce how to optimize ListView from the following aspects.
1. convertView Reuse
In Android SDK:
The old view to reuse, if possible. note: You shoshould check that this view is non-null and of an appropriate type before using. if it is not possible to convert this view to display the correct data, this method can create a new view
Use convertView to reuse View. do not create a new View every time getView () is used. The core principle of ListView is to reuse the View. If the reused view does not change its width and height, reusing the View can reduce the frequent memory allocation/recovery caused by cache reallocation;
The android: layout_height attribute value of ListView is set to fill_parent or ''wrap _ content, but the convertView mechanism is the same.
If it is set to fill_parent: The convertview of the items displayed on the screen is empty, and the convetview of the newly generated Item is swiped down.
If it is set to wrap_content: Only convertview of the first Item is null, and others are not empty.
Summary:
During initial display, the getview method is called every time an item is displayed, but the covertview is empty (because there is no old view) at each call. After the display is complete, the getview method is called. If the screen is moved, and some items (or views) run out of the screen, if new items need to be generated, the convertview parameter in the getview method called when these items are displayed is not null, but the view (old view) that is removed from the screen ), all we need to do is to fill the items to be displayed in the recycled view (old view). Finally, note that convertview is null, not just the items initially displayed, there are also some items that have been moved into the screen but have not been recycled by the view.
2. ViewHolder Optimization
The reason for using ViewHolder is that the findViewById method takes a lot of time. If there are too many controls, it will seriously affect the performance. ViewHolder is used mainly to save this time. Use setTag and getTag to directly obtain the View
Summary:
The setTag and getTag methods of view are actually very simple. When writing code, a view is not only used to display strings and images, sometimes we need them to carry some other data so that we can identify the view or perform other operations. Therefore, android designers created the setTag (Object) method to store some data and bind the view, we can understand that this is a view label or that view stores some data as a container. The data can also be obtained through the getTag () method.
At this point, we should have understood the setTag and getTag. Back to the above topic, we use the setTag method and getTag method of convertview to bind the data we want to display to convertview. If convertview is displayed for the first time, we will create a new Holder object and bind it to it. At last, return convertview to display it; if convertview is recycled, we do not need to create a new holder object. We only need to retrieve the original bound holder and add new data.
Class ViewHolder {ImageView img; TextView name;} public View getView (int position, View convertView, ViewGroup parent) {ViewHolder holder = null; if (convertView = null) {convertView = inflater. inflate (R. layout. list_item, parent, false); holder. img = (ImageView) convertView. findViewById (R. id. img); holder. name = (TextView) convertView. findViewById (R. id. name); holder = new ViewHolder (); convertView. setTag (holder);} else {holder = (ViewHolder) convertView. getTag ();} // sets holder. img. setImageResource (R. drawable. ic_launcher); holder. name. setText (list. get (position ). partname); return convertView ;}
3. image loading Optimization
If the ListView needs to load the display network image, we should try not to load the image when the ListView slides, which will cause the ListView to become stuck, so we need to listen to the ListView status in the listener, if the ListView slide (SCROLL_STATE_TOUCH_SCROLL) or SCROLL_STATE_FLING, Stop loading the image. If it does not slide (SCROLL_STATE_IDLE), start loading the image.
If we want to implement what we should do on our own, here is an idea.
/*** List rolling listener */listView. setOnScrollListener (new OnScrollListener () {@ Override public void onScrollStateChanged (AbsListView view, int scrollState) {if (scrollState = OnScrollListener. SCROLL_STATE_IDLE) {// load the image loadImage (startPos, endPos) when the list stops scrolling; // load the image asynchronously, load only images that can be seen} @ Override public void onScroll (AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {// set the start and end pos of the current screen to startPos = firstVisibleItem; endPos = firstVisibleItem + visibleItemCount; if (endPos> = totalItemCount) {endPos = totalItemCount-1 ;}}});
In fact, this function exists in the Universal-Image-loader framework and is very good and can be used directly. In the code, we usually set it like this:
listView = (ListView) rootView.findViewById(R.id.fragment_user_info_lisiview); listView.setOnScrollListener(DisplayImageOptionsUtil.getPauseOnScrollListener(this)); listView.setOnItemClickListener(this);
public static PauseOnScrollListener getPauseOnScrollListener(OnScrollListener scrollListener) { PauseOnScrollListener listener = new PauseOnScrollListener(ImageLoader.getInstance(), false, true, scrollListener); return listener; }
The first parameter of PauseOnScrollListener refers to the image loading object ImageLoader, and the second parameter is pauseOnScroll to control whether to pause image loading during the sliding process. If the image needs to be paused, true is passed, the third parameter controls whether the image is loaded when the interface is swiped.
Open the source code of PauseOnScrollListener. We can see that the imageLoader. pause () method is called when the listview slide or is swiped.
/** * Constructor * * @param imageLoader {@linkplain ImageLoader} instance for controlling * @param pauseOnScroll Whether {@linkplain ImageLoader#pause() pause ImageLoader} during touch scrolling * @param pauseOnFling Whether {@linkplain ImageLoader#pause() pause ImageLoader} during fling * @param customListener Your custom {@link OnScrollListener} for {@linkplain AbsListView list view} which also * will be get scroll events */public PauseOnScrollListener(ImageLoader imageLoader, boolean pauseOnScroll, boolean pauseOnFling,OnScrollListener customListener) {this.imageLoader = imageLoader;this.pauseOnScroll = pauseOnScroll;this.pauseOnFling = pauseOnFling;externalListener = customListener;}@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {switch (scrollState) {case OnScrollListener.SCROLL_STATE_IDLE:imageLoader.resume();break;case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:if (pauseOnScroll) {imageLoader.pause();}break;case OnScrollListener.SCROLL_STATE_FLING:if (pauseOnFling) {imageLoader.pause();}break;}if (externalListener != null) {externalListener.onScrollStateChanged(view, scrollState);}}
4. onClickListener: When a ListView item contains a child view such as a button, you need to set an onclickListener for it. The general method is to set one of the settings in the getView method, such
holder.img.setonClickListener(new onClickListenr)...
However, this method sets a new onClick event each time it calls getView, Which is inefficient. For efficient writing, you can directly set a position in ViewHolder, and then viewHolder implements OnClickListenr:
Class ViewHolder implements OnClickListener {int position; TextView name; public void setPosition (int position) {this. position = position ;}@ Override public void onClick (View v) {switch (v. getId () {// XXXX }}public View getView (int position, View convertView, ViewGroup parent) {ViewHolder holder = null; if (convertView = null) {convertView = inflater. inflate (R. layout. list_item, parent, false); holder = new ViewHolder (); holder. name = (TextView) convertView. findViewById (R. id. name); holder. name. setOnClickListener (this); convertView. setTag (holder);} else {holder = (ViewHolder) convertView. getTag ();} // sets holder. name. setText (list. get (position ). partname); // set position holder. setPosition (position); return convertView ;}
Supplement: When the listitem of ListView contains a child control such as Button CheckBox, the Child control will snatch the Focus, the simplest and most effective solution is to set the property android: descendantFocusability = blocksDescendants in the item layout file root element of ListView.
5. Reduce the layout level of the Item View, which must be followed by all layout items. A deep layout level will directly lead to a waste of time in measurement and drawing of the View.
6. The getView method in the adapter should use as little logic as possible. Do not make too complicated logic in the getView method. You can find a way to pull it from another place. For example, getView () before optimization ():
@ Overridepublic View getView (int position, View convertView, ViewGroup paramViewGroup) {Object current_event = mObjects. get (position); ViewHolder holder = null; if (convertView = null) {holder = new ViewHolder (); convertView = inflater. inflate (R. layout. row_event, null); holder. threeDimension = (ImageView) convertView. findViewById (R. id. threeDim); holder. eventPoster = (ImageView) convertView. findViewById (R. id. eventPoster); convertView. setTag (holder);} else {holder = (ViewHolder) convertView. getTag () ;}// perform logic judgment here. This is a problem if (doesSomeComplexChecking () {holder. threeDimention. setVisibility (View. VISIBLE);} else {holder. threeDimention. setVisibility (View. GONE);} // This is the parameter for setting the image. This code is executed every time the getView method is executed. This is obviously a problematic RelativeLayout. layoutParams imageParams = new RelativeLayout. layoutParams (measuredwidth, rowHeight); holder. eventPoster. setLayoutParams (imageParams); return convertView ;}
GetView () after optimization ():
@ Overridepublic View getView (int position, View convertView, ViewGroup paramViewGroup) {Object object = mObjects. get (position); ViewHolder holder = null; if (convertView = null) {holder = new ViewHolder (); convertView = inflater. inflate (R. layout. row_event, null); holder. threeDimension = (ImageView) convertView. findViewById (R. id. threeDim); holder. eventPoster = (ImageView) convertView. findViewById (R. id. eventPoster); // set the parameter here, only the first time will be executed, and then the RelativeLayout will be reused. layoutParams imageParams = new RelativeLayout. layoutParams (measuredwidth, rowHeight); holder. eventPoster. setLayoutParams (imageParams); convertView. setTag (holder);} else {holder = (ViewHolder) convertView. getTag ();} // we directly use the getter method of the object to replace the logic judgments we just made. The logic judgments are placed elsewhere and the holder is executed. threeDimension. setVisibility (object. getVisibility (); return convertView ;}
7. Use the getView method in the adapter as little as possible for time-consuming operations 8. Use the getView method in the adapter to avoid creating a large number of objects 9. Set the scrollingCache and animateCache attributes of the ListView to false, by default, it is enabled, which consumes a lot of memory, so GC is frequently called. We can manually disable it (depending on the situation)
Others
1. Make good use of the View Type. For example, if there are several types of items in your ListView, you need to create different views for each Type, which is conducive to the recycling of ListView, of course, there cannot be too many types. 2. Make good use of custom views. Custom views can effectively reduce Layout levels and control the painting process; 3. Ensure that the hasStableIds () of the Adapter returns true. In this way, if the id of yydatasetchanged () remains unchanged, the ListView will not re-draw the View to achieve optimization; 4. Each Item cannot be too high, especially when the screen height is not exceeded. For details, refer to the Optimization Method of Facebook, break down particularly complex items into several small items 5. Avoid translucent elements in ListView 6. Enable hardware acceleration 7. Use RecycleView instead. NotifyDataSetChanged () is required for each data update of ListView, which is too violent. RecycleView greatly improves the performance and customization. It is recommended.
I took the time to sum up and added some of my own understandings. If you have any questions, please click it! Comments...