SoFunction
Updated on 2025-04-07

Things to note when implementing custom controls with ListView with multiple check boxes and other states

Android itself provides several convenient Adapters for ListView, such as ArrayAdapter, SimpleCurrentAdapter, etc. However, in order to implement more complex list views and controls, you generally need to inherit BaseAdapter to implement your own Adapter.

The ListView I need is to implement the SD card resource file browsing list as shown in the figure. Each list item consists of an ImageView, TextView, and CheckBox. It requires that when one or more Checkboxes are selected in the entire list, the search button in the upper right corner will be displayed, otherwise it will be hidden. Therefore, it is necessary to set a listener for the CheckBox of each list item. If it is more complicated to implement using the Adapter provided by Android, I chose to inherit BaseAdapter to implement my own Adapter.

The first thing to know is the ListView display principle. After ListView receives the Adapter, the ListView's list item data comes from the received Adapter. When the ListView is to be displayed, the ListView will call the Adapter's getCount method to obtain the total number of list items to be drawn, and then start calling the getView method to obtain the View of each list item for loading. In other words, the list item of ListView is the View returned by each call to getView, what does the list item obtained by each call to getView look like, and what does the list item we see look like.

I inherit BaseAdapter to implement my own Adapter, and at least I need to rewrite the basic methods: getView, getCount, getItem, and getItemID. The functions of getCount and getView are as mentioned above, so if I want to listen to the multi-check button for each list item, I need to set a listener for the multi-check box in the View before returning the View to the ListView in the getView. The getView method has three parameters public View (int position, View convertView, ViewGroup parent), which is generally the view that convertView is the most returned.

Here, we need to insert and explain the small details of the Android system's implementation of ListView. When Android constructs ListView list items, only list items that are sufficient to meet the number of screen displays will be constructed at a time, usually about 10. When there are more list items in ListView than the list items that can be displayed on the screen, ListView can be pulled up and down. Each time you pull to display the subsequent list item, the getView method will be called again to construct the View of the subsequent list item. If the ListView is displayed for the first time, then the parameter View convertView of getView is null empty; if it is pulling the getView called by the ListView, then the parameter convertView of getView is no longer null, but pulling the view that has just been pulled away from the hidden list item. The advantage of doing this is that it can save resources.

Based on this detail, if the parameter convertView is to be used as a return View when rewriting the getView method, then the getView should determine whether the convertView is null. If it is empty, you need to use Inflater to construct it. If it is not empty, you can use it directly. My needs need to listen for multi-check boxes, so before returning to convertView, you need to get the multi-check box control and set the listener in the convertView.

At first, I thought this would be able to achieve my needs, but the result was unexpected. When I click on a multi-check box, I pull down the list, and the unselected list box below becomes a selected state. I noticed that the distance between the selected multi-check boxes is unchanged every time I click a multi-check box and then pull down to synchronize the selected multi-check boxes, and is always separated by 11 items. So, recalling the characteristics of the convertView parameter in getView, when I pull down, the convertView in the ListView called the convertView in the getView method is recycled because of pulling. In my example, since the multi-check box is a control with a status label, my getView does not reset its status, so this strange phenomenon is caused. My solution is to create a boolean array in the Adapter class I implemented to save the status of the corresponding list item multi-check box (the first parameter position in the getView is the list item ID, which is identified based on the data, not based on the list item View, so the list item data can be selected and unchecked according to the position). Each time I call getView, the boolean value at the position position will determine the status of the multi-check box.

Similarly, based on this principle, you also need to pay attention to the problem of getView recycling when using other stateful controls. Of course, you can not use convertView as the most getView return result, but reconstruct a View every time you call the getView, or construct a View array of the same length as the number of data in the Adapter class. However, doing so will consume more resources.

In addition, the getItem and getItemId methods in BaseAdapter have not been used in the construction process of the ListView construction process, but it is said that they will be called in some listeners about ListView, so it is best to return a meaningful value to these two methods when inheriting BaseAdapter. getItemId generally returns the corresponding position, and getItem returns the list data object of the corresponding position.