I recently decided to convert my simple ListView into a Separted List
View in order to categorize by list items. After creating my custom
ArrayAdapter to hold each section within my List
I noticed that adding sections and items to my ListView containing
default data when my application loads works fine. However, when I
call my update function to remove default data and append data from
background server communications my application crashes with a
ArrayIndexOutOfBoundsException.
I've searched the net and found a few others with similar issues but
found no solutions. I'm thinking it has to do with my getView of the
Custom ArrayAdapter I created (PlayerArrayAdapter).
After logging, I noticed that the first section returns views
correctly, however after the first list item of the second category
(section) is created, I get the exception. My initial thoughts were
that I was attempting to access data that did not exists, however that
was not the case as you can see below in my log:
(Data contained in first section's arrayAdapter)
04-05 04:19:18.939: WARN/PlayerArrayAdapter.getView(15716): 0.
playerID=DE8574E5-603C-4E8B-94B4-36F9BD4FD546
04-05 04:19:18.939: WARN/PlayerArrayAdapter.getView(15716): 1.
playerID=DE7574E5-603C-4E8B-94B4-36F9BD4FD546
(Creating View for first List Item)
04-05 04:19:18.939: WARN/PlayerArrayAdapter.getView(15716): Getting
view for Pos=0 View=null parent=android.widget.ListView@44ea39f0
04-05 04:19:18.989: WARN/PlayerArrayAdapter.getView(15716): -- PARSED
A PLAYER: playerID=DE8574E5-603C-4E8B-94B4-36F9BD4FD546
(Returned a View for first List Item)
04-05 04:19:19.039: WARN/PlayerArrayAdapter.getView(15716): --
CREATED NEW VIEW: playerID=DE8574E5-603C-4E8B-94B4-36F9BD4FD546
(Data contained in first section's arrayAdapter)
04-05 04:19:19.099: WARN/PlayerArrayAdapter.getView(15716): 0.
playerID=DE8574E5-603C-4E8B-94B4-36F9BD4FD546
04-05 04:19:19.099: WARN/PlayerArrayAdapter.getView(15716): 1.
playerID=DE7574E5-603C-4E8B-94B4-36F9BD4FD546
(Reusing View (convertView) from first item for second List Item)
04-05 04:19:19.099: WARN/PlayerArrayAdapter.getView(15716): Getting
view for Pos=1 View=android.widget.LinearLayout@44f1ca30
parent=android.widget.ListView@44ea39f0
04-05 04:19:19.109: WARN/PlayerArrayAdapter.getView(15716): Convert
view from ViewHolder.pos=0 for pos=1
04-05 04:19:19.109: WARN/PlayerArrayAdapter.getView(15716): -- PARSED
A PLAYER: playerID=DE7574E5-603C-4E8B-94B4-36F9BD4FD546
(Returned a view reused view for second list item)
04-05 04:19:19.279: WARN/PlayerArrayAdapter.getView(15716): --
CREATED NEW VIEW: playerID=DE7574E5-603C-4E8B-94B4-36F9BD4FD546
(Data contained in second section/category)
04-05 04:19:19.309: WARN/PlayerArrayAdapter.getView(15716): 0.
playerID=2791CB11-6B61-48A6-94AB-E888D745E048
04-05 04:19:19.319: WARN/PlayerArrayAdapter.getView(15716): 1.
playerID=77EE8049-2909-4F17-BB9E-A1BDC576AEBF
04-05 04:19:19.329: WARN/PlayerArrayAdapter.getView(15716): 2.
playerID=9AC5F033-5014-4FB6-B5DD-E9087858DFFA
04-05 04:19:19.329: WARN/PlayerArrayAdapter.getView(15716): 3.
playerID=ED9CB89B-0252-4E72-B3F5-CC00978B49DF
(Parses, creates and returns a view for the first item since
convertView == null)
04-05 04:19:19.339: WARN/PlayerArrayAdapter.getView(15716): Getting
view for Pos=0 View=null parent=android.widget.ListView@44ea39f0
04-05 04:19:19.359: WARN/PlayerArrayAdapter.getView(15716): -- PARSED
A PLAYER: playerID=2791CB11-6B61-48A6-94AB-E888D745E048
04-05 04:19:19.389: WARN/PlayerArrayAdapter.getView(15716): --
CREATED NEW VIEW: playerID=2791CB11-6B61-48A6-94AB-E888D745E048
[[[Then I get this error ]]]
04-05 04:19:19.460: ERROR/AndroidRuntime(15716): FATAL EXCEPTION: main
04-05 04:19:19.460: ERROR/AndroidRuntime(15716):
java.lang.ArrayIndexOutOfBoundsException
04-05 04:19:19.460: ERROR/AndroidRuntime(15716): at
android.widget.AbsListView$RecycleBin.addScrapView(AbsListView.java:
4078)
04-05 04:19:19.460: ERROR/AndroidRuntime(15716): at
android.widget.ListView.measureHeightOfChildren(ListView.java:1210)
04-05 04:19:19.460: ERROR/AndroidRuntime(15716): at
android.widget.ListView.onMeasure(ListView.java:1109)
04-05 04:19:19.460: ERROR/AndroidRuntime(15716): at
android.view.View.measure(View.java:8171)
04-05 04:19:19.460: ERROR/AndroidRuntime(15716): at
android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:3132)
04-05 04:19:19.460: ERROR/AndroidRuntime(15716): at
android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:
1012)
04-05 04:19:19.460: ERROR/AndroidRuntime(15716): at
android.widget.LinearLayout.measureVertical(LinearLayout.java:381)
04-05 04:19:19.460: ERROR/AndroidRuntime(15716): at
android.widget.LinearLayout.onMeasure(LinearLayout.java:304)
04-05 04:19:19.460: ERROR/AndroidRuntime(15716): at
android.view.View.measure(View.java:8171)
04-05 04:19:19.460: ERROR/AndroidRuntime(15716): at
android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:3132)
04-05 04:19:19.460: ERROR/AndroidRuntime(15716): at
android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:
1012)
04-05 04:19:19.460: ERROR/AndroidRuntime(15716): at
android.widget.LinearLayout.measureVertical(LinearLayout.java:381)
04-05 04:19:19.460: ERROR/AndroidRuntime(15716): at
android.widget.LinearLayout.onMeasure(LinearLayout.java:304)
04-05 04:19:19.460: ERROR/AndroidRuntime(15716): at
android.view.View.measure(View.java:8171)
04-05 04:19:19.460: ERROR/AndroidRuntime(15716): at
android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:3132)
04-05 04:19:19.460: ERROR/AndroidRuntime(15716): at
android.widget.FrameLayout.onMeasure(FrameLayout.java:245)
04-05 04:19:19.460: ERROR/AndroidRuntime(15716): at
android.view.View.measure(View.java:8171)
04-05 04:19:19.460: ERROR/AndroidRuntime(15716): at
android.widget.LinearLayout.measureVertical(LinearLayout.java:526)
04-05 04:19:19.460: ERROR/AndroidRuntime(15716): at
android.widget.LinearLayout.onMeasure(LinearLayout.java:304)
Here is my update and clearData functions added to the Jeff's list
adapter:
/*
* Used to clear contents of this Instance before updating.
*/
public void clearData()
{
final String CONTEXT = CLASS_NAME+".clearData()";
Log.i(CONTEXT, "** Separated List Adapter Cleared **");
sections.clear();
headers.clear();
this.notifyDataSetInvalidated();
}
/**
* Used to update the data in our view
* @param response - GenereicInViewPlayerInfoResponse Instance
containing new player Data
*/
public void update(Map <String,PlayerInfo[]> m)
{
final String CONTEXT = CLASS_NAME+".update(PlayerInfo)";
int playerCount = 0;
if (m != null && !m.isEmpty())
{
this.clearData();
// Clear Adapter data
contents
PlayerInfo [] data;
for(Object title : m.keySet())
{
data = m.get(title);
Log.i(CONTEXT, "Adding New Category: "
+ (String)title + "
- contains " + data.length + " players.");
for(int x=0;x<data.length;x++)
{
Log.i(CONTEXT,
data[x].toString());
}
Log.i(CONTEXT, "Num of players added: "
+ data.length);
playerCount += data.length;
this.addSection((String)title,
new
PlayerArrayAdapter((Activity)context,
R.layout.playerinfoadapter,data)); // Add data to Separated ListView
}
notifyDataSetChanged();
if (AppMain.isLogi) Log.i(CONTEXT,"Updated view with "+
playerCount +" PlayerInfos");
}
else{
if (AppMain.isLogi) Log.i(CONTEXT,"No data
found to update");
}
}
/**
* Used to update the data in our view
* @param str - String Array contain data to update view
*/
public void update(String [] str) {
final String CONTEXT = CLASS_NAME+".update(String [])";
System.out.println("Array of Strings outputted, No
Content
Found");
int playerCount = str.length;
if(str != null && str.length > 0)
{
this.clearData();
// Clear Adapter data contents
// Add data to Separated ListView
this.addSection("Content",
new
PlayerArrayAdapter((Activity)context,R.layout.playerinfoadapter,str));
}
notifyDataSetChanged();
if (AppMain.isLogv) Log.v(CONTEXT,"Updated view with "+
playerCount +" PlayerInfos");
}
/**
* Used to update the data in our view
* @param s - String to be parsed into an Array that will contain
data to update view
*/
public void update(String s) {
final String CONTEXT = CLASS_NAME+".update(String [])";
System.out.println("Array of Strings outputted, No
Content
Found");
String [] str = new String[1];
str[0] = s;
if(str != null && str.length > 0)
{
this.clearData();
// Clear Adapter data contents
// Add data to Separated ListView
this.addSection("Content",new
PlayerArrayAdapter((Activity)context,R.layout.playerinfoadapter,str));
}
notifyDataSetChanged();
if (AppMain.isLogv) Log.v(CONTEXT,"Updated view with "+ 1 +"
PlayerInfos");
}
Custom ArrayAdapter:
public class PlayerArrayAdapter extends ArrayAdapter<Object>{
// Static CONSTANTS
private final static String CLASS_NAME =
"PlayerArrayAdapter";
/** Handles the reading the XML and building a view from it */
private static LayoutInflater inflater = null;
/** Common instance of the AppMain */
private static AppMain appMain
= null;
private Activity
activity;
private Object[] data;
private boolean
stringData;
private boolean
playerData;
private boolean
showLastViewed; // used for Favorites and
History view
public String
title;
public String
textline1;
public String
textline2;
/**
* Inner class to hold the instance of each view
*/
public static class ViewHolder {
public int position = -1;
//public String playerName;
//public String playerID;
public TextView text;
public ImageView image;
public PlayerInfo player;
//public String browserURL;
public boolean playerInfo;
public String toString() {
if (player != null) return "PlayerInfoAdapter for
"+player.playerName;
return "PlayerInfoAdapter";
}
}
private void initialize() {
if (inflater == null) inflater =
(LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (appMain == null) appMain = AppMain.getInstance();
}
////////////////////////////////////////////
Constructors /////////////////////////////////////////
/**
* Create an instance of this View adapter
* @param a Current Activity instance
*/
public PlayerArrayAdapter (Activity a, int viewResourceId) {
super(a, viewResourceId);
final String CONTEXT = CLASS_NAME+"()";
activity = a;
initialize();
}
/**
* Create an instance of this View adapter
* @param a - Current Activity instance
* @param viewResourceId - XML layout to use
* @param objects - Data to be used to build List View
*/
public PlayerArrayAdapter(Activity a, int viewResourceId, Object[]
objects)
{
super(a, viewResourceId, objects);
data = objects;
activity = a;
// TODO Auto-generated constructor stub
initialize();
}
/**
* Create an instance of this View adapter with the list of
Strings
* @param a Current Activity instance
* @param viewResourceId - XML layout to use
* @param s Strings to create this list
*/
public PlayerArrayAdapter (Activity a, int viewResourceId, String
s) {
super(a, viewResourceId);
final String CONTEXT = CLASS_NAME+"(String[])";
activity = a;
String [] str = new String[1];
str[0] = s;
data = str;
this.add(data);
stringData = true;
initialize();
}
/**
* Create an instance of this View adapter with the list of
Strings
* @param a Current Activity instance
* @param viewResourceId - XML layout to use
* @param d Array of Strings to create this list
*/
public PlayerArrayAdapter (Activity a, int viewResourceId,
String[] d) {
super(a, viewResourceId, d);
final String CONTEXT = CLASS_NAME+"(String[])";
if (AppMain.isLogv) Log.v(CONTEXT,"Loading "+d.length+"
Strings");
activity = a;
if(d!= null && d.length>0){
data = d;
this.add(data);
}
stringData = true;
initialize();
}
/**
* Create an instance of this View adapter with the list of our
Players
* @param a Current Activity instance
* @param viewResourceId - XML layout to use
* @param players Array of PlayerInfo to create this list
*
*/
public PlayerArrayAdapter (Activity a, int viewResourceId,
PlayerInfo[] players) {
super(a,viewResourceId,players);
final String CONTEXT = CLASS_NAME+"(PlayerInfo[])";
if (AppMain.isLogv) Log.v(CONTEXT,"Loading "+players.length+"
Players");
activity = a;
data = players;
//this.clear(); // Clear Adapter data contents
//this.add(data);
playerData = true;
initialize();
}
/**
* Get a View that displays the data at the specified position in
the data set
* You can either create a View manually or inflate it from an
XML
layout file.
* When the View is inflated, the parent View (GridView,
ListView...)
will
* apply default layout parameters unless you use
* inflate(int, android.view.ViewGroup, boolean) to specify a
root
view and
* to prevent attachment to the root.
*
* @param position The position of the item within the adapter's
data set
* of the item whose view we want.
* @param convertView The old view to reuse, if possible.
* Note: You should check that this view is non-null and of an
appropriate
* type before using. If it is not possible to convert this view
to
display
* the correct data, this method can create a new view.
* @param parent The parent that this view will eventually be
attached to
* @return A View corresponding to the data at the specified
position.
*/
@Override
public View getView(int position, View convertView, ViewGroup
parent) {
final String CONTEXT = CLASS_NAME+".getView";
for(int x=0;x<data.length;x++)
{
Log.w(CONTEXT, x + ". " + data[x].toString());
}
View vi = convertView;
try {
Log.w(CONTEXT,"Getting view for
Pos="+position+" View="+convertView
+" parent="+parent);
ViewHolder holder = null;
if (convertView == null) {
if (AppMain.isLogv) Log.v(CONTEXT,"Building
view for pos="+
(position));
vi = inflater.inflate(R.layout.playerinfoadapter,
null);
holder = null;
holder = new ViewHolder();
holder.position = position;
holder.text = (TextView)vi.findViewById(R.id.text);
holder.image =
(ImageView)vi.findViewById(R.id.image);
vi.setTag(holder);
}
else {
// Getting existing view
holder = (ViewHolder) vi.getTag();
Log.w(CONTEXT,"Convert view from
ViewHolder.pos="+holder.position+" for pos="+position);
}
// Build the view
if (data != null) {
if (holder != null) {
if (data instanceof PlayerInfo[]) {
String result =
"Pos:"+(position);
StringBuilder sbuf = new
StringBuilder(256);
PlayerInfo player =
(PlayerInfo) data[position];
Log.w(CONTEXT, " -- PARSED A
PLAYER: " +
data[position].toString());
String iconURL = null;
if (player != null) {
holder.player = null;
holder.player = player;
holder.playerInfo =
true;
title =
player.playerName;
textline1 =
player.locationDescription;
// The next line varies
depending on if we display the
distance or last viewed time
StringBuilder line2 =
new StringBuilder(24);
if (showLastViewed) {
// if over 7
days display the date, otherwise display
the time
String
lastViewedTime = player.lastViewed;
if
(lastViewedTime != null) {
//
line2.append(activity.getString(R.string.lastviewed));
line2.append(lastViewedTime);
} else {
//
never viewed
}
} else {
if
(player.onSite > 0 || player.distance < 16093.44d) {
line2.append("<b><font color='green'>");
line2.append(player.getDistance(false));
line2.append("</font></b>");
} else {
line2.append(player.getDistance(false));;
}
}
textline2=
line2.toString();
line2 = null;
sbuf.append("<b>").append(title).append("</b><br/>");
sbuf.append("<small>").append(textline1).append("</
small><br/>");
sbuf.append("<small>").append(textline2).append("</
small>");
//holder.playerName =
player.playerName;
//holder.browserURL =
player.playerAddress;
//holder.playerID =
player.playerID;
iconURL =
player.iconURL;
result += " Set
Text="+title;
} else {
sbuf.append(activity.getString(R.string.noplayerdatafound));
}
holder.text.setText(Html.fromHtml(sbuf.toString()));
if (holder.image != null &&
iconURL != null) {
holder.image.setTag(iconURL);
// load the image from
our cache
appMain.getImageLoader().getImage (iconURL, activity,
holder.image);
result += " Set
Image="+iconURL;
}
if (AppMain.isLogd)
Log.d(CONTEXT,result);
}
else if (data instanceof String[])
{
String result =
"Pos:"+(position);
Log.w(CONTEXT,
(String)data[position].toString());
String stringData = (String)
data[position];
holder.text.setText(Html.fromHtml(stringData));
holder.playerInfo = false;
holder.player = null;
result += " Set
Text="+stringData;
Log.w(CONTEXT, " -- PARSED A
PLAYER: " + result);
appMain.getImageLoader().displayDefaultIcon
(holder.image);
if (AppMain.isLogd)
Log.d(CONTEXT,result);
}
else {
Log.w(CONTEXT,"Incorrect data
for view.
"+data.toString());
}
} else {
Log.w(CONTEXT,"Null view");
}
} else {// else no data defined yet
if (AppMain.isLogd) Log.d(CONTEXT,"Data not
defined");
}
}
catch (Exception e) {
Log.e(CONTEXT,e.getMessage()+" Pos="+(position)
+"
View="+convertView+" parent="+parent);
}
Log.w(CONTEXT, " -- CREATED NEW VIEW: " +
data[position].toString());
return vi;
}
/**
* Get the row id associated with the specified position in the
list.
* <p>
* Called when we have focused or clicked on,
* @param position The position of the item within the adapter's
data set whose row id we want.
* @return The id of the item at the specified position.
*
*/
@Override
public long getItemId(int position) {
final String CONTEXT = CLASS_NAME+".getItemId";
if (AppMain.isLogv) Log.v(CONTEXT,"position="+position);
return position;
}
/**
* Returns a count of how many elements we have in the view List
* @return 0 if no elements or the number of elements
*/
@Override
public int getCount() {
final String CONTEXT = CLASS_NAME+".getItemId";
int count = data.length;
//Log.w(CONTEXT, "GOT TOTAL COUNT: " + count);
return count;
}
@Override
public int getViewTypeCount() {
return 1;
}
@Override
public int getItemViewType(int position) {
return 0;
}
/**
* Get the data item associated with the specified position in the
data set
* @param position Position of the item whose data we want within
the adapter's data set.
* @return The data at the specified position.
*
*
*/
@Override
public Object getItem(int position) {
final String CONTEXT = CLASS_NAME+".getItem";
Object itemData = null;
if (data != null) {
if (AppMain.isLogv) Log.v(CONTEXT,"position="+position);
if (playerData) {
itemData = (PlayerInfo)data[position];
if (AppMain.isLogv) Log.v(CONTEXT,"Player "+
((PlayerInfo)data[position]).playerName);
} if (stringData) {
itemData = (String)data[position];
if (AppMain.isLogv) Log.v(CONTEXT,"String "+
((String)data[position]));
} else {
itemData = data[position];
if (AppMain.isLogv)
Log.v(CONTEXT,"position="+position);
}
} else {
Log.w(CONTEXT,"Data set is empty");
}
return itemData;
}
public void showLastViewed(boolean value) {
showLastViewed = value;
}
}
I've been at this for days, and my deadline is slowly tightening
around my neck! Anyone can help? Thx in advanced
--
You received this message because you are subscribed to the Google
Groups "Android Developers" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en