I have searched these forums for nearly 3 hours and seen several similar questions but none of the answers works for me.
I have a single Activity, with several card views. One of the card views has a Spinner with string values and a very simple ListView. The user selects a value from the Spinner, between 1 and 12. The ListView should then display a number of strings equal to the value selected, based on the position in the spinner list. For example, user selects 2nd item in spinner list and the ListView displays 2 strings. I have a custom adapter on the listview. The ListView itself initially displays a single row, which is correct. However, after the user selects a value from the spinner, the listview is not displaying the extra rows, it still only displays one row. The data for the ListView comes from an ArrayList. I have checked the data model of the adapter after the user selects a value and it has the correct number of entries, as does the ArrayList itself, yet no matter what I try the ListView itself still only display the first row. I have tried NotifyDataSetChanged and every variation of Invalidate without success.
The various code samples:
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (parent == spDoseFrequency){
Toast.makeText(this,String.valueOf(position),Toast.LENGTH_SHORT).show();
rebuildReminderTimesList(position + 1);
}
}
private void rebuildReminderTimesList(int numberOfTimes){
Toast.makeText(this,"yup",Toast.LENGTH_SHORT).show();
//reset selected item to position 1
myApp.iSelectedReminderTimeIndex = 0;
//clear array and list, then rebuild with hourly timeslots
iarrTimes = new int[numberOfTimes][2];
liReminderTimes.clear();
int startTime = 8;
for (int i = 0; i < numberOfTimes; i++){
iarrTimes[i][0] = startTime + i;
iarrTimes[i][1] = 0;
liReminderTimes.add(pad(startTime + i) + ":00");
}
//refresh the listview
myAdapter.notifyDataSetChanged();
}
public class ReminderListAdapter extends ArrayAdapter {
List<String> liTimes;
Context ctx;
LayoutInflater inf;
public ReminderListAdapter(Context ctx, List<String> liTimes) {
super(ctx, R.layout.reminder_time_listview, liTimes);
this.liTimes = liTimes;
this.ctx = ctx;
inf = LayoutInflater.from(ctx);
}
public void setLiTimes(List<String> liTimes){
this.liTimes = liTimes;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
ViewHolder viewHolder;
if (view == null){
view = inf.inflate(R.layout.reminder_time_listview,parent,false);
viewHolder = new ViewHolder();
viewHolder.sTime = (TextView) view.findViewById(R.id.tvTime);
view.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) view.getTag();
}
viewHolder.sTime.setText(liTimes.get(position));
return view;
}
private static class ViewHolder{
TextView sTime;
}
}
Any help would be appreciated as this is driving me crazy.
Quick update to this question: I have just tested supplying the initial list more than one value but even then it only displays the first item. Is there perhaps a problem with using ListView inside a CardView object? All my other cards work fine, only the ListView one fails to display properly.
Also, I have tried amending the code so that instead of changing the number of elements in the list, it just changes the text in the string of the first element and this works fine. So the notifyDataSetChanged appears to be working, but it just won't display more than one item. A quick check of the Adapter.getCount() method also gives the correct number of elements back, but won't display them.
A lot of folks forget to do the notifyDataSetChanged() call, but you've got that. Are you using a custom adapter? If so, that makes this sound like an issue with one or more of the adapter's methods. In particular, it sounds like getCount or getView might not be returning what they should be. That could either be because of a flawed logic issue, the underlying data source isn't being updated correctly, or the underlying data source isn't the same object you think it is. Without seeing your adapter though, it's hard to diagnose.
I found the problem. I had several CardView objects inside a LinearLayout, which itself was inside a ScrollView. As soon as I removed the ScrollView, the List inside the Card displayed properly. This has unfortunately created another problem in that I can no longer scroll the page to see later cards, which I have not yet found a solution for, but that is a different topic.
Related
In my QuestionsActivity, I am showing a question and bunch of answers. The question is displayed on TextView and answers are displayed on ListView that is composed of TextViews. There is an ActionButton named "Check Answer" and when it is clicked, it shows the correct answer by changing the background color of the TextView in the ListView.
The background changing code looks like this:
if (allAnswers.get(i).isCorrect())
{
mAnswerList.getChildAt(i).setBackgroundColor
(getResources().getColor(R.color.correct_answer_background));
return;
}
and now there are two Buttons at the footer section of this QuestionsActivity called PreviousQuestionButton and NextQuestionButton and they are basically navigation buttons between questions.
The problem is, when I go to the next question after clicking on "Check Answer" button, the answer background color doesn't go away and remains in the next question answer options. I tried invalidate(), refreshDrawableState() method of ListView but no luck!
This is the method which displays the answers for a given question:
private void showAnswers(int questionLocation)
{
int questionId = mAllQuestions.get(questionLocation).getQuestionId();
List<Answer> answers = mAnswerRepository.getAllByQuestionId(questionId);
mAnswerAdapter.clear();
for (int i = 0; i < answers.size(); i++)
{
mAnswerAdapter.add(mOptionLetters[i] + ". "
+ answers.get(i).getAnswerText());
}
mAnswerAdapter.notifyDataSetChanged();
}
My Question
What I want is that when I click on next or previous buttons, the background color of the correct answer in ListView should disappear so that next and previous question button can show non-selected answer options list to the user. Is there any method which resets ListView to a state which does not have any background applied?
For selected answer option, I am using mAnswerList.clearChoices() in order to unselect but it does not apply for correct answer background color.
Well, to reset the color you can very well hard-reset the adapter by creating a new one. So don't clear and add as that may keep the views in the state they were before. I am not too sure about this since I am not clearing or adding from an adapter, but always creating a new one to fulfill my new needs.
Anyway, another reason why things may not go in the direction you want is that the views may get recycled, since we're talking about a ListView. So if you want to highlight a list item, you should keep in the data model the information about highlight by initializing it to false and if the user selects one set the highlight state to true. I suppose the Answer class has as a minimum the following:
public class Answer {
private String data;
private boolean correct;
public String getData() {
return data;
}
public boolean isCorrect() {
return correct;
}
#Override
public String toString() {
return data;
}
}
So your adapter could look close to this - getView method is the most important to notice (don't forget to set to default background if the answer is incorrect or the adapter should not highlight correct answer):
public class MyAdapter extends ArrayAdapter<Answer> {
private boolean showCorrectAnswer;
private List<Answer> modelAnswers;
public MyAdapter(Context context, List<Answer> answers) {
super(context, android.R.layout.simple_list_item_1, answers);
this.modelAnswers = answers;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
if(showCorrectAnswer && modelAnswers.get(position).isCorrect()) {
view.setBackgroundColor(getContext().getResources().getColor(R.color.correct_answer_background));
} else {
view.setBackgroundColor(getContext().getResources().getColor(R.color.default_background));
}
}
public void setShowCorrectAnswer(boolean showCorrectAnswer) {
this.showCorrectAnswer = showCorrectAnswer;
notifyDataSetChanged();
}
}
What you need to do is to keep a reference to this custom adapter and if you need to highlight the correct answer or not simply call setShowCorrectAnswer(true / false);. It will trigger a redraw and in the getView() it will decide what to do based on adapter state and correct answer.
Hope it make sense ... I wrote all this while drinking a beer :)
Basically, I agree with the answer from payeli - you should change the background of selected answer in the "next/previous question" button.
But then there is this question:
"Is there any method which resets ListView to a state which does not have any background applied?"
Answer to this (as far as I know) is: not directly. But there are two workarounds.
If you call notfiyDataSetChange, not all views are completely redrawn. If possible, just the appropriate values are changed. It's mainly for performance reasons. (Imagine having 1000 contacts with images and names dumped and redrawn)
So how can you deal with this? (Other then reseting the background in the onClick method) Since you said these items are answers for a question, I assume you are not concerned about performance because there won't be too many of them.
Then you can simply:
A) Create new instance of Adapter instead of changing data in the old one. When you switch adapters, all views in list are removed, so, no recycling can happen in the new adapter.
B) Create custom Adapter, override getView method and make sure every time view is requested, you return a new view, so no recycling can happen.
Again: this really isn't "performance friendly" and should not be used with big lists with a lot of items!
If you want to know more, feel free to ask in comments or read the reference of Adapter class, especially the parameter convertView of getView method. (http://developer.android.com/reference/android/widget/Adapter.html)
As per my understanding U need to change color of text view in one of the list view row i.e row containing correct answer.
If this is your problem then On Click of button simply clear list view and recreate list view. Not when list view is recreated then check out position of row which contains correct answer. After getting row position simply change color of text view.
For Ex: In your Adapter class check as fallows in your getView();
for ( int i = 0; i < position.length ; i++ )
{
if(position == your_required_position){
{
textview.setColor(Color.BLUE);
}else{
textview.setColor(Color.BLACK);
}
}
In the onClickListener of "Check Answer" button, you need to reset the color:
void onClick (View v){
..............
mAnswerList.getChildAt(currentQuestion).setBackgroundColor
(getResources().getColor(R.color.default_answer_background));
}
you can save the default background somewhere, and when you press next/previous question button you should apply that color. Example:
TypedArray array = getTheme().obtainStyledAttributes(new int[] {
android.R.attr.colorBackground,
android.R.attr.textColorPrimary,
});
int backgroundColor = array.getColor(0, 0xFF00FF);
int textColor = array.getColor(1, 0xFF00FF);
array.recycle();
I found this code online to get background and text color (you can just keep the background part), when the onClick activates just set the background of your view to "backgroundColor"
I have one listview in my application,it contains two rows one for task and another one for alarm,date,severity. Initially first row of the list item only displayed for all list item and the second one is invisible. When i click the list item the second row displayed for that item as well as click another list item at that time the above list item closed that second row. Its working fine for me...My problem is if i open one list item and then swipe the listview at then i click the another list item at that time the above one cannot be closed because the above list item instance will be chnaged.please any one help me how to solve this problem...
int lastselectedPosition == -1
#Override
public void onItemClick(AdapterView<?> arg0, View view, int position,
long id) {
TextView textviewDate=(TextView)view.findViewById(R.id.taskTimeidDaytoDay);
selectedtaskDate=textviewDate.getText().toString().trim();
if (lastselectedPosition == -1) {
Log.i(TAG,"Loopif:"+lastselectedPosition);
TextView twTaskTime = (TextView) view
.findViewById(R.id.taskTimeidDaytoDay);
TextView twSeverity = (TextView) view
.findViewById(R.id.severityidDaytoDay);
TextView twAlarm = (TextView) view
.findViewById(R.id.alarmidDaytoDay);
twAlarm.setVisibility(view.VISIBLE);
twSeverity.setVisibility(view.VISIBLE);
twTaskTime.setVisibility(view.VISIBLE);
lastselectedPosition = position;
lastSelectedItem = arg0.getChildAt(position);
} else {
// Log.i(TAG,"LoopElse:"+lastselectedPosition);
lastSelectedItem.findViewById(R.id.taskTimeidDaytoDay)
.setVisibility(lastSelectedItem.GONE);
lastSelectedItem.findViewById(R.id.severityidDaytoDay)
.setVisibility(lastSelectedItem.GONE);
lastSelectedItem.findViewById(R.id.alarmidDaytoDay).setVisibility(
lastSelectedItem.GONE);
if (lastselectedPosition != position) {
view.findViewById(R.id.taskTimeidDaytoDay).setVisibility(
view.VISIBLE);
view.findViewById(R.id.severityidDaytoDay).setVisibility(
view.VISIBLE);
view.findViewById(R.id.alarmidDaytoDay).setVisibility(
view.VISIBLE);
lastselectedPosition = position;
lastSelectedItem = arg0.getChildAt(position);
} else {
lastselectedPosition = -1;
lastSelectedItem = null;
}
}
GetView():
#Override
public View getView(int position, View view, ViewGroup parent) {
Log.i("XXXX", "Inside getView");
final DaytoDayTaskGetterSetter objDaytoDaygetset=getItem(position);
TextView textviewTask;
TextView txtviewAlarm ,txtviewTaskTime ,txtviewSeverity;
Log.i(TAG,"InsideGetView:"+position);
LayoutInflater inflater=(LayoutInflater)context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
if(view==null)
{
view=inflater.inflate(R.layout.daytodaylistlayout,null);
}
Log.i("XXXX", "before first test");
textviewTask=(TextView)view.findViewById(R.id.tasknameidDaytoDay);
txtviewAlarm=(TextView)view.findViewById(R.id.alarmidDaytoDay);
txtviewSeverity=(TextView)view.findViewById(R.id.severityidDaytoDay);
txtviewTaskTime=(TextView)view.findViewById(R.id.taskTimeidDaytoDay);
return view;
}
In first i click the "gdfgdtet" list item it show another row and then i click the second list item "dfgsdgsd" at that time the above list item "gdfgdtet" closed the second row.This is a normal output.Suppose if i open the "gdfgdtet" list item and then swipe the listview at that time both of "gdfgdtet" "dfgsdgsd" will be opened and crashed...because the above one list item reference changed when i am swiping please how to solve this problem...
I'll try to provide you a good answer that explains why you are having this problems, but the general idea is that you have to see this video - http://www.youtube.com/watch?v=wDBM6wVEO70
please take my words kindly - you don't seems to understand what ListView + BaseAdapter recycling mechanism is all about, and I strongly recommend you see the full video I linked you to, and read more about that.
in general, the specific problem in your code is that you are holding reference to listview item (lastSelectedItem), then trying to use it latter assuming it's still representing same list item. that's wrong. in that stage (after scrolling) the view already been recycled to represent another item in the list (based on the adapter implementation).
listView's number of childs is not the size of adapter.getCount()!!!!!!!!
listViews's number of childs = number of visible list items on screen + 1 + headers + footers
let's say you have the 5 first items visible on screen, then you are scrolling down. when you see the 7 item you actually see the same view instance that used to show the first list item and been recycled.
getView will call in this stage with convertView != null and position in the adapter to let you reuse the item by putting new values such different text/image to the same instance
this mechanism provides ability to display list of "infinite" number of items in the list, and holding in memory only a few number of views. imagine that you have list of 5000 items in the list, and each one of them have different view instance - you would get outOfMemory exception in a sec!
complete explanation about that would be hard to write in stackoverflow answer's context.
it just too long trying to explain one of the most important and complex UI components in android, but this links would be a good start:
http://www.youtube.com/watch?v=wDBM6wVEO70
How ListView's recycling mechanism works
http://mobile.cs.fsu.edu/the-nuance-of-android-listview-recycling-for-n00bs/
if you are interstead in "quick" fix for your specific problem, the solution would be:
hold in the data structure represents your list item additional field indicating if it in "close" or "open state. when item been clicked - change the data accordinly and call notifyDatasetChanged(). inside the getView() check if item is open or close and populate it accordinly
by the way - it's not only "quick fix" solution, but also the right thing to do anyway
You should pay attention to Tal Kanel's answer and consider this one to be an extension to it. His advice will help you in the long run.
Add a boolean field to DaytoDayTaskGetterSetter class:
public class DaytoDayTaskGetterSetter {
....
....
boolean open;
public DaytoDayTaskGetterSetter (.., .., boolean o) {
....
....
open = o;
}
....
....
public boolean shouldOpen() {
return open;
}
public void setOpen(boolean o) {
open = o;
}
}
In your getView(), check if the object has its open value set:
DaytoDayTaskGetterSetter obj = (DaytoDayTaskGetterSetter) getItem(position);
if (obj.shouldOpen()) {
// Set visibility to true for the items
} else {
// Set visibility to false for the items
}
On list item click, traverse the list and set open for all list items to false. Use the position to retrieve DaytoDayTaskGetterSetter and set its open to true:
#Override
public void onItemClick(AdapterView<?> arg0, View view, int position, long id) {
for (DaytoDayTaskGetterSetter obj : listContainingObjects) {
obj.setOpen(false);
}
DaytoDayTaskGetterSetter clickedItem = (DaytoDayTaskGetterSetter)
yourAdapter.getItem(position);
clickedItem.setOpen(true);
yourAdapter.notifyDataSetChanged();
}
Edit 1:
#Override
public void onItemClick(AdapterView<?> arg0, View view, int position, long id) {
DaytoDayTaskGetterSetter clickedItem = (DaytoDayTaskGetterSetter)
yourAdapter.getItem(position);
if (clickedItem.shouldOpen()) {
clickedItem.setOpen(false);
} else {
for (DaytoDayTaskGetterSetter obj : listContainingObjects) {
obj.setOpen(false);
}
clickedItem.setOpen(true);
}
yourAdapter.notifyDataSetChanged();
}
I am previously working on PHP and js , and recently I am working on android listview
However, I encountered a problem in creating a custom adapter for listview
public View getView(int arg0, View arg1, ViewGroup arg2) {
// TODO
if (arg1 == null) {
arg1 = myInflater.inflate(R.layout.grid, arg2, false);
}
TextView name = (TextView) arg1.findViewById(R.id.text1);
TextView desc = (TextView) arg1.findViewById(R.id.text2);
ImageView image = (ImageView) arg1.findViewById(R.id.image1);
if (arg0 < images.length) {
image.setImageResource(images[arg0]);
}
name.setText(names[arg0]);
desc.setText(description[arg0]);
return arg1;
}
The problem is I have 3 array of content to pass to the listview grid, for the first two array, there are 10 element and the last one have 5 only. So , it is out of boundries for the last one. I added a condition to check whether it exceed 5 , but args0 seems not increased according to the row?
if (arg0 < images.length) {
image.setImageResource(images[arg0]);
}
The first five row and some other rows also has image setted, why is that and how to fix this? thanks
In General
since you want to display Data to your list, plx create an Object that represents Data.
like you named in your comment above:
public class ListEntry {
String name = "";
String gender = "";//use enum here perhaps -.-
String photoUrl = null; //or use byte[] photo or whatever you've stored in your array before
// write getters/setters for your members
}
then you can use one array ListEntry[] (or List<ListEntry>) to access all data. this way you get around your indexOutOfBoundsException.
lookup any listadapter tutorials online, e.g. the one from Vogella
Why do more than the first five entries have an image?
Androids Adapters for Listviews implement a caching mechanism to reduce the inflating (performance/memory cost intensive) of new list-items (e.g. rows) to a minimum. therefore there are only as many rows (or little more) created as displayed by the list. since you only set images if there are any, but never remove already set images from rows, you result in some rows that replay images they shouldn't. these rows are cached from previously outscrolling rows.
therefore add something like
if (listItem.photo != null) {
image.setImageResource(images[arg0]);
} else {
image.setVisibility(View.GONE);
}
as reference for listviews and their caching mechanism see Romain Guy on ListViews
Edit Regarding usage of Listadapter
The getView(..) you posted above is inside your ListAdapter implementation, prefarrably you've extended an ArrayAdapter<T>. if so, your T should now state ListEntry and you have any line of code that states
MyArrayAdapter myAdapter = new MyArrayAdapter() or something like that.
now you have an array or List of ListEntry like List<ListEntry> myCollection = new ArrayList<ListEntry>() or ListEntry[] listEntries = new ListEntry[10] and use
myAdapter.addAll(listEntries);
to get an item of your list inside your getView(..) you can use:
ListEntry currentEntry = getItem(arg0);
and refer the single members of currentEntry to set them ;-)
What about
if (images[arg0] != null) image.setImageResource(images[arg0]);
?
I create a dialog and populate it with a listview that uses a custom list adapter. It works fine, but I've noticed that when the list is long enough to scroll, doing so back and forth will cause some of my list items to randomly lose some of their data. I've noticed it is always the same list items too. For instance, each list item will have a title, image, and date on it. The dates seem to vanish on some when I scroll. They are always there when I start the dialog, and they always vanish once I scroll.
The weird thing is that my list row consists of a few TextViews in 2 rows and its only the bottom row TextViews that dissapear...Any ideas?
Code for my dialog
itemSendPickerDialog = new Dialog(this);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Select Item to Send");
ListView lv = new ListView(this);
Cursor c = mDbHelper.fetchItemsByDate(id);
c.moveToFirst();
int i = R.layout.send_item_menu_row;
MyListAdapter ia = new MyListAdapter(this, mainListBackground, c, true);
lv.setAdapter(ia);
builder.setView(lv);
itemSendPickerDialog = builder.create();
itemSendPickerDialog.show();
And my custom list adapter class:
class MyListAdapter extends ResourceCursorAdapter {
public MyListAdapter(Context context, int i, Cursor cursor, boolean...sending) {
super(context, i, cursor);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
TextView title = (TextView) view.findViewById(R.id.item_name);
title.setText(cursor.getString(cursor.getColumnIndex(TripsDbAdapter.KEY_ITEM_TITLE)));
Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
int width = display.getWidth();
width = width - 150;
ViewGroup.LayoutParams params = title.getLayoutParams();
params.width = width;
title.setLayoutParams(params);
String cat = cursor.getString(cursor.getColumnIndex(TripsDbAdapter.KEY_ITEM_CATEGORY));
if (cat.equalsIgnoreCase("trip notes")) {
LinearLayout ll = (LinearLayout) view.findViewById(R.id.item_datetime_holder);
ll.setVisibility(View.INVISIBLE);
}
TextView date = (TextView) view.findViewById(R.id.item_date);
date.setText(cursor.getString(cursor.getColumnIndex(TripsDbAdapter.KEY_DEP_DATE)));
TextView time = (TextView) view.findViewById(R.id.item_time);
time.setText(cursor.getString(cursor.getColumnIndex(TripsDbAdapter.KEY_DEP_TIME)));
ImageView iv = (ImageView) view.findViewById(R.id.image_icon);
if (iv != null) {
int index = cursor.getColumnIndex(TripsDbAdapter.KEY_ITEM_TYPE);
String type = cursor.getString(index);
if (type != null) {
} else {
type = "notes";
}
iv.setImageResource(getTypeResource(type));
}
}
}
I have faced this problem too...
The problem you are facing is due to the recycling of views by the LIstView when you scroll up/down. In your case, the ListView is giving you those recycled views , whose properties you have changed by making them invisible. Some possible solutions could be:
1) When cat.equalsIgnoreCase("trip notes")) is becoming true, you are making some views invisible. This invisible view is then recycled and given back. The recycled view is still invisible (when it is given back to you), so what you can do is make this invisible view visible in the beginning of your ListAdapter every time .
You can put this code at the beginning of bindView method where you make the layout visible first and then proceed with rest of the logic.( In short, the dates from your display are not vanishing but are just invisible).
2) Override getViewTypeCount() in you adapter . From your codesnippet, it looks like you have two types of rows( one in which R.id.item_datetime_holder is invisible and other in which it is visible) , so return 2 from this method( please do some trial and error ) . This should take care of the problem.
public int getViewTypeCount() {
return 2;
}
You will find an excellent explanation at this link http://logc.at/2011/10/10/handling-listviews-with-multiple-row-types/
3) You can inflate completely different layouts depending on your if condition. But the effeciency would be a little less.
I had a similar problem, when scrolling a list, only the items after the window height decided to get their data repeating from index 0 - so if the last visible item was 8, the next would be 0 again.
So you could try to check if the index of the view is correct, maybe this method of ListView would help
lv.getPositionForView(view);
I've figured out that the code that is doing this is in my custom list adapter class
String cat = cursor.getString(cursor.getColumnIndex(TripsDbAdapter.KEY_ITEM_CATEGORY));
if (cat.equalsIgnoreCase("trip notes")) {
LinearLayout ll = (LinearLayout) view.findViewById(R.id.item_datetime_holder);
ll.setVisibility(View.INVISIBLE);
}
I hide some of the layout items depending on what the category is, and for some reason when putting a list view in an AlertDialog builder it appears to mix up the items. I don't know exactly how I am going to fix it, but for now if I just remove the invisibility functionality, my data won't go away.
I would use two different layouts, depending on the "type" of list item. It looks like you are switching the "type" based on the cat string containing "trip notes". If it contains it, then you would have a layout that is the same as you have now, but without the item_datetime_holder view. If it doesn't contain it, then you would use the same layout as you're using now (send_item_menu_row).
Here is a good tutorial on how to use multiple layouts in a ListView: http://android.amberfog.com/?p=296
By the way, I think that the reason why some of your rows are not drawing correctly is due to view reuse by the ListView. Utilizing the multiple layouts like I've mentioned above should fix the problem since you won't be changing the visibility of views, but just utilizing two different views to render, depending on what type of list item you're rendering.
I'm running into a problem with a ListActivity.
The onListItemClick method needs to access the child views of the ListView in order to highlight the correct answer if an incorrect one was chosen. This logic usually works fine. But the problem is that if I go too fast, i.e. just keep banging away indiscriminately at the display as the lists are presented, before too long the ListView will return 0 children in onListItemClick, and the program will crash on the resulting empty view. My debug statements show that when this occurs, the array used to populate the ListView is correctly initialized, containing all four items as expected.
Additional info: When the user responds, I'm writing data to a DB inside of an AsyncTask. When I disable this, the problem seems to go away. I'm passing a subclass of the ApplicationContext to it, but no other data is common to the threads.
This is the array used to populate the ListView:
ArrayList<String> myArrayList = new ArrayList<String>();
That array list is populated in the code and is declared as follows:
String[] myAnswerArray = new String[4];
The ListView adapter is set in the follow code extract:
myArrayList.clear();
myArrayList.addAll(Arrays.asList(myAnswerArray));
// attach the adapter to the ListView
setListAdapter(myStringArrayAdapter);
The MyStringArrayAdapter extends ArrayAdapter, and shown here for completeness:
public class MyStringArrayAdapter extends ArrayAdapter<String> {
private Typeface font;
private static final String TAG = "MyStringArrayAdapter";
public MyStringArrayAdapter(Context context, int textViewResourceId,
ArrayList<String> answerArray, Typeface font) {
super(context, textViewResourceId, answerArray);
this.font = font;
}
#Override
public View getView(int position, View view, ViewGroup viewGroup) {
View v = super.getView(position, view, viewGroup);
((TextView) v).setTypeface(font);
return v;
}
}
Once the problem started showing up, I added code to check for zero children. Here's how I check for that:
#Override
protected void onListItemClick(ListView listView, View listItemView,
int position, long id) {
super.onListItemClick(listView, listItemView, position, id);
int childCount = listView.getChildCount();
// Workaround on bug raised by monkey exerciser
if (childCount == 0) {
Log.d(TAG, "No children found in ListView")
}
// etc...
Again, this problem only happens if I bang away rapidly on the touch screen, and also the exerciser monkey has occasionally but not always generated this I've tried waiting for it to be populated, using Thread.sleep in a loop and re-retrieving the child count, but it makes no difference.
Anyone have any ideas how to resolve this?
Try with ViewHolder... http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/view/List14.html