I'm trying to create a list that shows an indeterminate progress bar as the last entry while it's fetching more data. I can show the bar and get/add the data, but scrolling up and down while it's loading causes multiple progress bars to show up.
I have a ListActivity that uses an ArrayAdapter. Each row has a layout as follows.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:background="@drawable/textlines" android:padding="2dip">
<LinearLayout android:layout_width="fill_parent"
android:layout_height="wrap_content" android:id="@+id/rowContent">
<TextView android:height="20sp" android:text=""
android:id="@+id/search_display" android:layout_width="fill_parent"
android:textSize="16sp" android:layout_height="20sp" android:gravity="left" />
</LinearLayout>
<LinearLayout android:layout_height="wrap_content"
android:layout_width="fill_parent" android:id="@+id/rowSpinner"
android:padding="3px" android:gravity="center" android:visibility="gone">
<ProgressBar android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/progress"
android:indeterminate="true" />
</LinearLayout>
</RelativeLayout>
The ListView has an OnScrollListener with the following onScroll method.
public void onScroll(final AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
{
// detect if last item is visible
if ((visibleItemCount < totalItemCount)
&& (firstVisibleItem + visibleItemCount == totalItemCount))
{
if (false == scrollTaskRunning)
{
scrollTaskRunning = true;
getMoreData(totalItemCount);
}
}
}
getMoreData invokes an AsyncTask that gets some more data to add to adapter. In its onPreExecute I call showSpinner() --
private void showSpinner()
{
// nothing to do if there's already a spinner visible
if (isSpinVisible == true) return;
// hide the progress spinner
if (0 < lvList.getChildCount())
{
View vRow = lvList.getChildAt(lvList.getChildCount() - 1);
vRow.findViewById(R.id.rowContent).setVisibility(View.GONE);
vRow.findViewById(R.id.rowSpinner).setVisibility(View.VISIBLE);
}
isSpinVisible = true;
}
and in its onPostExecute/onCancelled I call hideSpinner() which is the same code, except checking the isSpinVisible flag the other way, and GONE and VISIBLE swapped. The swapping code, so far as I can tell, only gets called once, but multiple entries in the last show up with the progress bar visible if you scroll up and down.
I tried doing this instead for hideSpinner() --
private void hideSpinner()
{
// nothing to do if there's no spinner visible
if (isSpinVisible == false) return;
// show the progress spinner
int iChildCount = lvList.getChildCount();
if (0 < lvList.getChildCount())
{
for (int i = 0; i < iChildCount; i++)
{
View vRow = lvList.getChildAt(iChildCount);
if (null != vRow)
{
vRow.findViewById(R.id.rowContent).setVisibility(View.VISIBLE);
vRow.findViewById(R.id.rowSpinner).setVisibility(View.GONE);
}
}
}
else if (null != pbSearch)
{
pbSearch.setVisibility(View.GONE);
}
isSpinVisible = false;
}
but vRow is null, and some of the progress bars still show up. How do I fix this? Alternatively, is there a better way to do this? (I thought I might be able to do something with my ArrayAdapter's getView()
method, but I couldn't work it out.)
ETA: this answer seems to explain the problem I'm having, but knowing that hasn't helped me find a way around it.
ETA2: I tried doing this:
final LayoutInflater mInflater = (LayoutInflater)
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
aapSearchResults = new ArrayAdapter<ParsedXML>(this, R.layout.search_row, saData)
{
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
View row;
// get the view
if (null == convertView)
{
row = mInflater.inflate(R.layout.search_row, null);
}
else
{
row = convertView;
}
// bind the data to the view
TextView tv = (TextView) row.findViewById(R.id.search_display);
tv.setText(getItem(position).name);
// show data, hide spinner
row.findViewById(R.id.rowContent).setVisibility(View.VISIBLE);
row.findViewById(R.id.rowSpinner).setVisibility(View.GONE);
// if the current position is the last, and a task is running,
// show the progress bar
if (taskRunning && (position == this.getCount()-1))
{
row.findViewById(R.id.rowContent).setVisibility(View.GONE);
row.findViewById(R.id.rowSpinner).setVisibility(View.VISIBLE);
}
return row;
}
};
lvList.setAdapter(aapSearchResults);
but I'm clearly making a logic error still, because sometimes scrolling up and down now gets me blank entries. (Is it because the count has changed?)