I got a trivial code involving a ListView and a ArrayAdapter which throws a IndexOutOfBoundsException on some devices. The problem is: I do not know how this Exception occurs, I only get the stacktraces from the Developer Console of Android.
The reduced code example is shown below. How can the getItem operation of the ArrayAdapter fail for the position element? The ArrayAdapter is never changed, there is no other method in the Activity.
I know what a IndexOutOfBoundsException is, I know that I can prevent it by checking the length first. But I'm curious: How can this Exception happen here? How can someone click on a event which does not exist in the datastructure?
Reduced code:
public class EventListActivity extends Activity {
public void onStart() {
final ListView listview = new ListView(this);
final Event[] events = [Retrieve a Array from somewhere]
final ArrayAdapter<Event> a = new ArrayAdapter<Event>(this, R.layout.eventlistitem, events);
listview.setAdapter(a);
listview.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Event event = a.getItem(position);
^^^^^^^ throws Exception
}
});
Exception:
java.lang.IndexOutOfBoundsException
at java.util.Arrays$ArrayList.get(Arrays.java:75)
at android.widget.ArrayAdapter.getItem(ArrayAdapter.java:298)
at xxx.EventListActivity$3.onItemClick(EventListActivity.java:130)
Okay, I debugged it, in case someone has the same error, here is the solution:
If you use ListView.addHeader, the position argument of the onItemClick is shifted by 1. Seems quite weird because the position then has nothing to do with the position in the array-adapter. This SO Question contains the same conclusion, you have to pay attention what you add as headers.
My question above was in fact not very helpfull because the whole addHeader part was missing, it was impossible to answer the question by others, sorry. Nevertheless, this answer might be helpfull for others.
Related
I'm working on a note taking app. I add a note, and it get's added to the bottom of the list. As the last assertion in the espresso test, I want to make sure that the ListView displays a listItem that has just been added. This would mean grabbing the last item in the listView. I guess you might be able to do it in other ways? (e.g. get the size of adapted data, and go to THAT position? maybe?), but the last position of the list seems easy, but I haven't been able to do it. Any ideas?
I've tried this solution, but Espresso seems to hang. http://www.gilvegliach.it/?id=1
1. Find the number of elements in listView's adapter and save it in some variable. We assume the adapter has been fully loaded till now.:
final int[] numberOfAdapterItems = new int[1];
onView(withId(R.id.some_list_view)).check(matches(new TypeSafeMatcher<View>() {
#Override
public boolean matchesSafely(View view) {
ListView listView = (ListView) view;
//here we assume the adapter has been fully loaded already
numberOfAdapterItems[0] = listView.getAdapter().getCount();
return true;
}
#Override
public void describeTo(Description description) {
}
}));
2. Then, knowing the total number of elements in listView's adapter you can scroll to the last element:
onData(anything()).inAdapterView(withId(R.id.some_list_view)).getPosition(numberOfAdapterItems[0] - 1).perform(scrollTo())
I have two listview, like listview_1 and listview_2. I wanna refresh the listview_2 while listview_1 is refreshed.
My code like this:
public void updateTwoListView() {
listview_1.getAdapter().notifyDataSetChanged();
listview_2.getAdapter().notifyDataSetChanged();
}
But it don't work, listview_1 can refresh but the listview_2 can't.
And at that moment what I found is that listview_1 was on focus.
And then I tried to set focus to other views before ran the method, both of them didn't refresh. It likes to refresh a listview only if the listview has focus.
What's more I found that when I called the method to refresh, listview_2 didn't, and then I set focus to listview_2, refreshed itself!
So, What all I want to ask is:
How to refresh two listview at one moment in Android?
What's more code:
//init two listview there
public void init() {
listview_1 = (ListView)findViewById(R.id.listView1);
listview_2 = (ListView)findViewById(R.id.listView2);
adapter1 = new MyListViewAdapter(mContext);
adapter2 = new MyListViewAdapter(mContext); //I have tried use different adapter, that also didn't work.
listview_1.setAdapter(adapter1);
listView_2.setAdapter(adapter2);
}
the real code of upside snippet is:
public void updateTwoListView(int currentPosition) {
adapter1.updateCurPos(currentPosition);
adapter2.updateCurPos(currentPosition);
}
and in MyListViewAdapter.java:
public void updateCurPos(int currentPosition) {
mCurrentPos = currentPosition;
notifyDataSetChanged();
}
And I will call method like listViewManager.updateTwoListView(1) outside to refresh.
Any reply is appreciated.
You have called listview_1 twice. Just change one of them to listview_2 as below:
public void updateTwoListView() {
listview_1.getAdapter().notifyDataSetChanged();
listview_2.getAdapter().notifyDataSetChanged();
}
It seems the problem of your code is that you call getAdapter().
Sets the data behind this ListView. The adapter passed to this method may be wrapped by a WrapperListAdapter, depending on the ListView features currently in use. For instance, adding headers and/or footers will cause the adapter to be wrapped.
Source: http://developer.android.com/reference/android/widget/ListView.html#setAdapter(android.widget.ListAdapter)
The solution is save your Adapter as member variable in your activity and call the notifyDataSetChanged()from there.
See more on this question's answer: https://stackoverflow.com/a/31893525/2742759
I have this ListView that is using an array adapter:
//global
ArrayList<Location> locations=new ArrayList<Location>(); //array list of location objects
ArrayAdapter<Location> theLocations;
ListView listView;
then in onCreateView of my Fragment:
theLocations = new ArrayAdapter<Location>(mContext,
R.layout.location_row, locations);
listView.setAdapter(theLocations);
This works pretty well I think, though if I try to update this adapter it freaks out...
//in my fragment, another dialog makes this call here to update the list.
public void onUIUpdate(Location l) { //listener call back when dialog "ok" clicked
locations.add(l); //it is okay with this.
theLocations.notifyDataSetChanged(); //dies
}
It dies with the error:
07-23 08:03:28.655: ERROR/AndroidRuntime(509): java.lang.IllegalStateException: ArrayAdapter requires the resource ID to be a TextView
does that mean it doesn't like the Location in the ArrayAdapter ??? does this mean I have to make my own custom listadapter? If I don't call theLocations.notifyDataSetChanged() everything is okay but then nothing updates... :(
I should note that the toString() in the Location class is override to return a String name;
after a comment I tried this:
public void onUIUpdate(Location l) {
locations.add(l);
//theLocations.notifyDataSetChanged();
WrapperListAdapter wr = (WrapperListAdapter)listView.getAdapter();
ArrayAdapter aa=(ArrayAdapter)wr.getWrappedAdapter();
aa.notifyDataSetChanged();
}
LOL no luck here, was a stab in the dark. Also wandering if its the addHeaderView I used on the ListView? I am not sure if it makes it immutable in the future using addHeaderView?
I won't comment about ListView design, it is very ugly, but I do listView.getAdapter().getWrappedAdapter().notifyDataSetChabged(); and it works.
While attempting to populate the items in a ListView following the outcome of a switch statement, I am encountering some sort of error. The application force closes inside the emulator, and when I ran it through the Eclipse debugger it shows the main thread has hung due to an IllegalStateException.
Aside from it meaning the obvious that it has entered some sort of wrong state, how do I fix it? I'm trying to do all of this from inside an OnItemClickListener, so that when the item is clicked, a switch statement evaluates which item was clicked, and then assigns an according ListAdapter to the ListView depending on the outcome of the switch. Is this the correct way to go about it? And if so, what in my code below is throwing the error?
final ListView lv = (ListView) findViewById(R.id.main_list);
final String[] autos = getResources().getStringArray(R.array.auto_array);
final ListAdapter la_auto = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_2, autos);
And then further down in the portion dealing with the onclicklistener
gallery.setOnItemClickListener(new OnItemClickListener()
{
public void onItemClick(AdapterView parent, View v, int position, long id)
{
switch(gallery.getSelectedItemPosition())
{
case 0:
lv.setAdapter(la_auto);
break;
EDIT: The LogCat stack trace is stopping at this error, "You must supply a Resource ID for a TextView, and the stack is hung at this point: ArrayAdapter.createViewFromResource(int, View, ViewGroup, int) line: 347
Any suggestions? I imagine it has something to do with the parameters I'm passing to the onItemClicked method.
Try android.R.layout.simple_list_item_1.
The answer is in simple_list_item_2.xml:
<TwoLineListItem xmlns:android="http://schemas.android.com/apk/res/android"
...
You need to use a resource that contains only a TextView. simple_list_item_1.xml fits the bill:
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
...
Try browsing other possibilities here or making your own if you don't see any you like.
It would probably be better to start a new Activity with a new ListView and adapter.
You can pass the selected item as a parameter in your Intent:
Intent = new Intent(MyActivity.this, ActivityToStart.class);
intent.putExtra("selected", position);
startActivity(intent);
and retrieve it later in your other activity's onCreate method with:
int position = getIntent().getIntExtra("selected");
You may want to think about what information you would like to share between the activities. Using multiple activities will allow your user to press the back button and return to the original list as well.
I'm having a problem with my ListView (using CursorAdapter). When I call getListView().getLastVisiblePosition() I am receiving -1. This is a problem since my list is populated with items. Additionally, getListView().getFirstVisiblePosition() always returns 0, no matter where I am scrolled on the list. Any ideas?
It has something to do with startManagingCursor
#Override
public void changeCursor(Cursor cursor) {
super.changeCursor(cursor);
MyActivity.this.mCursor = cursor;
//startManagingCursor(MyActivity.this.mCursor);
}
If I comment out startManagingCursor, everything works fine. I've also tried adding stopManagingCursor() before changing the Cursor and still have the same problem.
This is because the moment you call getListView().getLastVisiblePosition() the listview is not rendered. You can add the call to the view's message queue like this:
listview.post(new Runnable() {
public void run() {
listview.getLastVisiblePosition();
}
});
Even I faced the same problem. The thing is that, getLastVisiblePosition() works only when you are in the first element of your listview and for the rest you will get null being returned. So what I did is that, I made a small calculation to find out the exact view position which I have mentioned below,
int LastPos=(mylist.getLastVisiblePosition()-mylist.getFirstVisiblePosition());
This returns the exact last position without a doubt.
My guess is that your getItem(int position) and getItemId(int position) methods aren't defined correctly in your adapter.