6

Edit: My issue seems like BaseAdapter just wont post more than 1 Spinner. If I change the array's size to 0, it wont put anything, but anything more than 1 is truncating it. It never passes position 0 from getView() and it never shwows anymore than 1. I have been at it for hours. Is there a reason for this?

I am having an issue with adding Spinners dynamically in a ListView using a BaseAdapter. I tried it before as a test to make sure it could be done correctly in a test class, and it iterates the positions correctly. But now I am doing it again and its failing. What I mean by failing is instead of getView() creating the new Spinner, it never leaves position 0. It still runs. Just never adds more Spinners. This is my code:

Main Adapter code


public class RemindersAdapter extends BaseAdapter{
Spinner[] shownReminders = new Spinner[1];
TextView[] removeReminders = new TextView[1];
String[] reminders = new String[1]; //this hlds the values of the coresponding spinner

RemindersAdapter mAdapter;


@Override
public int getCount() {
    return shownReminders.length;
}

@Override
public Object getItem(int position) {
    return shownReminders[position];
}

@Override
public long getItemId(int position) {
    return position;
}

@Override
public View getView(int position, View view, ViewGroup parent) {
    Log.d("TAG", "A NEW SPINNER AND TEXTVIEW IS CREATED HERE WITH POSITION"+position);
    if(view == null) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        view = inflater.inflate(R.layout.reminder_spinner, parent, false);
    }

    Spinner reminderSpinner = (Spinner)view.findViewById(R.id.reminder_spinner);
    reminderSpinner.setTag(String.valueOf(position));
    ArrayAdapter<CharSequence> reminderAdapter = ArrayAdapter.createFromResource(
            parent.getContext(), R.array.reminders_array, android.R.layout.simple_spinner_item);
    reminderAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    reminderSpinner.setAdapter(reminderAdapter);
    reminderSpinner.setOnItemSelectedListener(new MyOnReminderSelectedListener());
    shownReminders[position] = reminderSpinner;

    TextView remove = (TextView)view.findViewById(R.id.remove_reminder);
    remove.setTag(String.valueOf(position));
    removeReminders[position] = remove;
    remove.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Log.d("The Array positioning of this remove view is: ", ""+v.getTag());

        }
    });


    return view;
}


public void addReminder() {
    Log.d("addReminder METHOD", "The Add Reminder method is running");

    Spinner[] temp = new Spinner[shownReminders.length+1];
    TextView[] temp2 = new TextView[removeReminders.length+1];
    String [] temp3 = new String[reminders.length+1];
    for(int i = 0; i < shownReminders.length; i++) {
        temp[i] = shownReminders[i];
        temp2[i] = removeReminders[i];
        temp3[i] = reminders[i];
    }
    shownReminders = temp;
    removeReminders = temp2;
    reminders = temp3;
    mAdapter.notifyDataSetChanged();//this just makes the adapter refresh itself

}

public void giveYourself(RemindersAdapter adapter) {
    mAdapter = adapter;
}


public class MyOnReminderSelectedListener implements OnItemSelectedListener{

    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int pos,
            long id) {
        int position = Integer.parseInt(parent.getTag().toString()); //gets the position of the Spinner and puts it in the same index in the reminders array
        reminders[position] = parent.getItemAtPosition(pos).toString();
        for(int i =0; i < reminders.length; i++) Toast.makeText(parent.getContext(), i+": "+reminders[i], Toast.LENGTH_SHORT).show();
    }
    @Override
    public void onNothingSelected(AdapterView<?> arg0) {
        // Do nothing for now   
    }   
}//end of MyOnReminderSelectedListener innerclass   


}//end of Class

What runs in the Activity


reminderList = (ListView)findViewById(R.id.reminders_list);
    reminderAdapter = new RemindersAdapter();
    reminderAdapter.giveYourself(reminderAdapter);
    reminderList.setAdapter(reminderAdapter);

    TextView addReminder = (TextView)findViewById(R.id.add_reminder);
    addReminder.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Log.d("TAG", "The onClick is running !");
            reminderAdapter.addReminder();
        }
    });

I am at a loss because my code looks exactly like my test code, with some modifications in order for it to work with my app. But the information used by the Adapter is pretty much the same. I am going to post the test code as well so you guys can see the code that works.

Test Code


public class RemindersAdapter extends BaseAdapter{
Spinner[] shownReminders = new Spinner[1];
ArrayList<TextView> removeSpinner = new ArrayList<TextView>();
Context mContext;

public RemindersAdapter(Context context) {
    mContext = context;
}

@Override
public int getCount() {
    // TODO Auto-generated method stub
    return shownReminders.length;
}

@Override
public Object getItem(int position) {
    // TODO Auto-generated method stub
    return shownReminders[position];
}

@Override
public long getItemId(int position) {
    // TODO Auto-generated method stub
    return position;
}

@Override
public View getView(int position, View view, ViewGroup parent) {
    Log.d("TAG", "Number of times this is running"+position);
    Log.d("TAG", "Address of the Spinner Object"+shownReminders[position]);
    if(view == null) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        view = inflater.inflate(R.layout.reminder_spinner, parent, false);
    }

    Spinner reminderSpinner = (Spinner)view.findViewById(R.id.reminders_spinner);
    reminderSpinner.setTag("1"); 
    ArrayAdapter<CharSequence> reminderAdapter = ArrayAdapter.createFromResource(
            parent.getContext(), R.array.reminders_array, android.R.layout.simple_spinner_item);
    reminderAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    reminderSpinner.setAdapter(reminderAdapter);
    reminderSpinner.setOnItemSelectedListener(new MyOnReminderSelectedListener());
    shownReminders[position] = reminderSpinner;

    TextView remove = (TextView)view.findViewById(R.id.remove_reminder);
    remove.setTag(position);
    removeSpinner.add(remove);
    remove.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            int pos = Integer.parseInt(v.getTag().toString());
            removeSpinner.remove(pos);
            Spinner[] temp = new Spinner[shownReminders.length-1];
            for(int i =0; i < shownReminders.length; i++) {
                if(i == pos || i > pos) {
                    temp[i-1] = shownReminders[i];
                } else {
                    temp[i] = shownReminders[i];
                }
            }
            //Here i should refresh somewhow

        }
    });

    return view;
}

public void addReminder() {
    Spinner[] temp = new Spinner[shownReminders.length+1];
    for(int i = 0; i < shownReminders.length; i++) {
        temp[i] = shownReminders[i];
    }
    shownReminders = temp;
}


/*
 * Listener for when the reminder spinner gets a value the user entered
 * */
public class MyOnReminderSelectedListener implements OnItemSelectedListener{

    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int pos,
            long id) {
        //does nothing for now

    }
    @Override
    public void onNothingSelected(AdapterView<?> arg0) {
        // Do nothing for now   
    }   
}//end of MyOnReminderSelectedListener innerclass

I also have a question as to why the Adapter runs itself so much. For examples, using the Log, I noticed it calls getView() twice for no apparent reason. Its weird that it has this behavior. I guess I don't understand BaseAdapter so well.

MMiroslav
  • 1,672
  • 20
  • 32
Andy
  • 10,553
  • 21
  • 75
  • 125
  • I don't understand why you are internally using another ReminderAdapter whose notifyDataSetChanged method you are calling. If you put an instance of your ReminderAdapter into a ListView, ListView registers a DataSetObserver with your ReminderAdapter. So where does mAdapter come from? Is it the same instance? Then just get rid of that member and call notifyDataSetChanged directly. If it is not the same instance, then ListView cannot be informed about the changed data set. By the way, I would use an ArrayList instead of re-inventing the wheel with growing arrays – tiguchi Jul 09 '12 at 21:10
  • Lol, well its a pointer to itself so I can refresh it there instead of in the `Activity`. I am doing that because (not evident here), but I stumbled upon as issue where I needed to refresh it in the `Adapter` itself. As for the ArrayList, It actually became more of a nuisance to use it. I was getting more errors. Since using `size()` tells me how many elements in the `ArrayList`, I had to use other means to count things correctly. In either case, I tried and it literally was about the same amount of work. Besides, its only like 3 extra lines of code. No biggie :) thanks for the advice though. – Andy Jul 09 '12 at 21:15
  • When you tap your textview, are you seeing all of the proper logging output that you have inserted? – trumpetlicks Jul 09 '12 at 21:20
  • Yes, I believe so. But the issue lies in why the `getView()` method is not incrementing past zero. The size of shownReminders is increasing :/ but its not through getView. – Andy Jul 09 '12 at 21:22

2 Answers2

37

This error will happen if you put a ListView inside a ScrollView. ListView itself does vertical scroll so it must not be put into a ScrollView.

szcoder
  • 1,396
  • 11
  • 9
  • 3
    Ugh, I wish I would have found this post sooner. I was going crazy for about 30 minutes. – Mike Apr 14 '14 at 20:32
6

An adapter will call getView when listView needs a new item to show. So, if your listView has no scroll, no new item will be created, and no call to getView will be made.
But you should not store all spinner objects, or create new objects in getView. That`s because it will be slow and maybe waste of memory.

Lupu Silviu
  • 1,145
  • 11
  • 23
xtr
  • 5,870
  • 1
  • 21
  • 23
  • If my ListView not scroll? From my experience using it, that has not been the case. In fact I have to refresh it myself after the intitial time. I think you may be talking about `CursorAdapter`. In any case, `getView()` is ran multiple times from what I see using `Log`, but I will actually try what you just mentioned, which is to inflate my own view seperate from `getView()`, though to be honest thats not how things have been working. Like if you look at my TestCode, it works! So I am confused. – Andy Jul 10 '12 at 02:27
  • Oh, your view should be inflate in getView, but another object you create in getView should be constructed outside of getView, cause getView is called many times. – xtr Jul 10 '12 at 02:37
  • Huh, let me ask you something. Hopefully you have a good way. How can you get a list of spinners created? Like add them dynamically and remove them as well? This is my attempt. But I am obviously failing. I will take anything! Doing this single problem all day! – Andy Jul 10 '12 at 02:43
  • Because a view store many layout related object, and have quite large size of memory if you want store many of them. You can store only data of a spinner, and when you need, you can construct a spinner with that data. Remove a spinner by remove its data. – xtr Jul 10 '12 at 02:48
  • The issue I am having here is that even if I hardcode a number, say 2, in `getCount()`, the position in `getView()` will always be 0 meaning only size 1. Its stuck on that and I have no idea why. – Andy Jul 10 '12 at 02:49
  • Hmm, good point. I will change my implementation to not carry them. Thanks. But now how do I solve my issue. I still don't really know how to really fix it unless I fugure out why it gets stuck at index 0 – Andy Jul 10 '12 at 02:50
  • Is your listView height long enough to display more than 1 item? And did getView called multiple times with only position 0? – xtr Jul 10 '12 at 03:06
  • My ListView height attribute is wrap_content. Wouldn't it just lengthen to fit it? At least thats what its been doing thus far. As for `getView`, thats exactly whats been happening. It does not go past position 0. So it outputs the first spinner just fine. But anything larger than 1 never appears. When I make the length 0, no spinner appears. So the issue is only with #'s greater than 1 – Andy Jul 10 '12 at 03:09
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/13643/discussion-between-xtr-and-andy) – xtr Jul 10 '12 at 03:13
  • if Your design scrollview inside listview in this case getview not allowed next position so will return 0 we need to remove scrollview suppose you using expandable listview full page scroll we can use nested litview custom class – Pandiyan Muthu Jul 04 '15 at 17:39