Android ListView.setOnItemClickListener NullPointerException when there are more items then the screen can hold
When this ListView has more items then can be shown on the screen (you have to scroll to see the others) and you select one of the items I get a NullPointerException
Here is the code:
studentList = (ListView)findViewBy开发者_运维技巧Id(R.id.studentListView);
getAllStudents();
studentList.setOnItemClickListener(new OnItemClickListener(){
@Override
public void onItemClick(AdapterView<?> a, View v, int position, long id) {
for (int i = 0; i < studentList.getCount(); i++){
if (i == position){
studentList.getChildAt(i).setBackgroundResource(R.color.selectedListViewBg);
}
else{
studentList.getChildAt(i).setBackgroundResource(R.color.listViewBg);
}
}
To pull the students from the database and add them to the studentList ArrayAdapter:
getAllStudents();
I get the Exception at:
studentList.getChildAt(i).setBackgroundResource(R.color.<either one>);
If the ListView can hold all the items without having to scroll, you can select without the NullPointerException
What am I doing wrong?
EDIT :
After reading the answers to this question, I decided to change the logic at this part of my program.
I probably should have explained what I was trying to do better:
I have a ListView that is populated with students. There is also a button at the bottom that says "Next". I wanted the user to be able to click on their choice and have the background color change. This section of code was supposed to change the selected item's background to one color and all the other item's backgrounds back to the original color.
My Solution: I got rid of the button and move on to the next page when the user clicks on the ListView item.
getChildAt()
returns to you the child View of the ListView at the specified index in the screen. This will only encompass what is currently rendered (ListView children views are recycled), so it will always go from 0 to about 7 (for example). So if you have more than 8 students you're trying to get the one at the 9th position, which does not exist.
However, the problem with your code (if it were working) is that you would need to store the "selected" state internally in your model anyway, otherwise once you scroll the background color will go back to being the original one.
I would suggest adding a checkbox widget and set that as checked/unchecked. You will still need to save this information in your model and set the checkbox state accordingly when recycling (in getView()) but it will make more sense in an Android environment than using the background color as a "selected indicator".
But to answer the original question, the easiest way to "fix" your code is to make use of the View argument that you're getting (the v).
for (int i = 0; i < studentList.getChildCount(); i ++) {
studentList.getChildAt(i).setBackground(unselected);
}
v.setBackground(selected);
But again, you will notice this will not really work as soon as you scroll.
Quote from Android Developers' Reference
public int getCount ()
Since: API Level 1
Returns
* The number of items owned by the Adapter associated with this AdapterView. (This is the number of data items, which may be larger than the number of visible views.)
If the ListView
holds more items than screen can display, then the invisible list item row will be held in a cycle, of course, they would be NULL. And when you're trying to scroll up/down to display them, they will be inflated to display (getView()
method is called to inflate these items).
So you can only use all item rows that are visible on screen (you can see them).
The solution is to use these two methods: getFirstVisibleItem()
and getLastVisibleItem()
. Don't use getChildAtPosition()
unless you know what you're doing.
精彩评论