如何在Android应用中实现“加载”指示器

Ok, so right now I have a ListView which is being populated by information via a PHP script. Right now, the list loads one at a time. By this, I mean the user can see when each listing is loaded (e.g. they see one item, one second later they see the second item, etc.) What I want to do is to WAIT until all the items are retrieved and then display them all at once. And while this is happening, to have some type of "loading" indicator (maybe a spinning circle type deal). Is there any way I can implement this? Here is my code:

public class MainActivity extends ActionBarActivity {

    ArrayList<Location> arrayOfLocations;
    LocationAdapter adapter;
    Button refresh;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        setContentView(R.layout.activity_main);

        // Construct the data source
        arrayOfLocations = new ArrayList<Location>();

        // Create the adapter to convert the array to views
        adapter = new LocationAdapter(this, arrayOfLocations);

        getData();

        // Attach the adapter to a ListView
        ListView listView = (ListView) findViewById(R.id.listView1);
        listView.setAdapter(adapter);
    }
}

So, my getData() method adds each Location into the adapter and then my Adapter class is what put the data into the ListView:

public class LocationAdapter extends ArrayAdapter<Location> {
    public LocationAdapter(Context context, ArrayList<Location> locations) {
        super(context, R.layout.item_location, locations);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // Get the data item for this position
        Location location = getItem(position);
        // Check if an existing view is being reused, otherwise inflate the view
        if (convertView == null) {
            convertView = LayoutInflater.from(getContext()).inflate(
                    R.layout.item_location, parent, false);
        }

        // Lookup view for data population
        TextView tvName = (TextView) convertView.findViewById(R.id.tvName);
        TextView tvDetails = (TextView) convertView
                .findViewById(R.id.tvDetails);
        TextView tvDistance = (TextView) convertView
                .findViewById(R.id.tvDistance);
        TextView tvHours = (TextView) convertView.findViewById(R.id.tvHours);
        ImageView ivIcon = (ImageView) convertView.findViewById(R.id.imgIcon);

        // Populate the data into the template view using the data object
        tvName.setText(location.name);
        tvDetails.setText(location.details);
        tvDistance.setText(location.distance);
        tvHours.setText(location.hours);
        ivIcon.setImageBitmap(location.icon);
        // Return the completed view to render on screen
        return convertView;
    }
}

Also, I have code for a simple loading indicator:

public class MainActivity extends Activity {

    private ProgressDialog progress;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        progress = new ProgressDialog(this);
    }

    public void open(View view) {
        progress.setMessage("Loading...Please Wait");
        progress.setProgressStyle(ProgressDialog.STYLE_SPINNER);
        progress.setIndeterminate(true);
        progress.show();

        final int totalProgressTime = 100;

        final Thread t = new Thread() {

            @Override
            public void run() {

                int jumpTime = 0;
                while (jumpTime < totalProgressTime) {
                    try {
                        sleep(200);
                        jumpTime += 5;
                        progress.setProgress(jumpTime);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }

                }

            }
        };
        t.start();

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
}

So basically, what I am trying to do is figure out how to do this: 1. When the activity starts, display "loading" indicator 2. Load ALL items into ListView 3. When all items are listed, remove "loading" indicator and display ListView

Any ideas? Thanks.

To do this you need a class that extends AsyncTask. In this class in the method doInBackground you need to do all the "heavy" stuff. In your case populate your ListView. In case you want to show your progress you can call the publishProgress method at the end of each iteration. Finally in the method onPostExecute you can inform the user that the process finished. Here's a simple example

 public class ExampleAsync extends AsyncTask <Void, Integer, Void> {

     private ProgressDialog progressBar; //to show a little modal with a progress Bar
 private Context context;  //needed to create the progress bar

     public ExampleAsync(Context context){
           this.context = context;
     }

     //this is called BEFORE you start doing anything 
     @Override 
     protected void onPreExecute(){
         progressBar = new ProgressDialog(context);
         progressBar.setCancelable(false);
         progressBar.setMessage("I am starting to look for stuff");
         progressBar.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
         progressBar.setIndeterminate(true);
         progressBar.show();
     }

     //every time you call publishProgress this method is executed, in this case receives an Integer
     @Override
 protected void onProgressUpdate(Integer ... option){
         progressBar.setMessage("I have found :" + option[0]);      
}  

     @Override
 protected void onPostExecute(Void unused){     
      progressBar.dismiss(); //hides the progress bar
          //do whatever you want now
}



     //in here is where you execute your php script or whatever "heavy" stuff you need
     @Override
 protected Void doInBackground(Void... unused) {          
        for (int i = 0; i < someLimit; i++){
             //do something
             publishProgress(i); //to show the progress
        }
     } 




 }

And in your main activity:

//code
new ExampleAsync(this).execute();

Obviously this is a simple example. You can do lots of stuff in the onProgressUpdate method, and it's this method that you need to update the progress bar.

Hope it helps

Another simple way to achieve this is to have the ProgressBar in your view already visible, and hide it when your processing is complete:

<RelativeLayout
    android:id="@+id/app_container"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1">

    <FrameLayout
        android:id="@+id/loading_progress_container"
        android:layout_width="45dp"
        android:layout_height="45dp"
        android:layout_centerInParent="true">
        <ProgressBar
            android:id="@+id/list_progress_indicator"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    </FrameLayout>

    <com.example.MyListView
        android:id="@+id/my_list_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="gone"/>

</RelativeLayout>

Then in a callback for your request which does the work do something like:

final View progressView = containerView.findViewById(R.id.loading_progress_container);
final View myListView = containerView.findViewById(R.id.my_list_view);
activity.runOnUiThread(new Runnable() {
    progressView.setVisibility(View.GONE);
    myListView.setVisibility(View.VISIBLE);
});

Obviously you would need to have a reference to your container view and activity for the above code.