列表视图中的不确定进度条

时间:2011-05-23 12:33:54

标签: android listview scroll progress-bar

我正在尝试创建一个列表,显示一个不确定的进度条作为最后一个条目,同时获取更多数据。我可以显示条形图并获取/添加数据,但在加载时向上和向下滚动会导致显示多个进度条。

我有一个使用ArrayAdapter的ListActivity。每行的布局如下。

<?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>

ListView有一个带有以下onScroll方法的OnScrollListener。

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调用AsyncTask,它可以获取更多数据以添加到适配器。在onPreExecute中,我调用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;
}

并在其onPostExecute / onCancelled中调用hideSpinner(),它是相同的代码,除了以另一种方式检查isSpinVisible标志,并且GONE和VISIBLE交换。据我所知,交换代码只会被调用一次,但如果你上下滚动,最后会显示多个条目并显示进度条。

我尝试这样做而不是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;
}

但vRow为null,某些进度条仍然显示。我该如何解决?或者,有更好的方法吗? (我想我可以用我的ArrayAdapter的getView()方法做一些事情,但我无法解决它。)


ETA :这answer似乎解释了我遇到的问题,但知道这并没有帮助我找到解决方法。


ETA2 :我试过这样做:

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);

但我显然仍然犯了一个逻辑错误,因为有时上下滚动会让我感到空白。 (这是因为计数已经改变了吗?)

2 个答案:

答案 0 :(得分:2)

一般来说,你不应该直接触摸ListView的孩子。事实上,在他们离开你的适配器的getView()巢之后,你应该认为它们是完全独立的并且完全脱离你的直接控制。您可以控制它们的唯一方法是调用notifyDataSetChanged()并让ListView创建新的。

要解决您的问题,请执行以下操作:

public class MyAdapter extends ArrayAdapter[…]{
    //[…]

    private boolean mIsLoading = false;

    public void setIsLoading(boolean isLoading){
       if (mIsLoading != isLoading){
          mIsLoading = isLoading;
          notifyDataSetChanged();
       }
    }

    @Override 
    public int getCount(){
       return super.getCount() + (isLoading ? 1 : 0);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if (mIsLoading && position == (getCount() - 1)){
            //return your progress view goes here. Ensure that it has the ID R.id.progress;

        }else{
            if (convertView != null && convertView.getId() == R.id.progress){
                convertView = null;
            }
            return super.getView(position, convertView, parent);
        }
    }
}

答案 1 :(得分:0)

我建议你改用BaseAdapter。当显示列表中的最后一个元素时,它会自动加载更多元素。