3

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.

almightyGOSU
  • 3,731
  • 6
  • 31
  • 41
user1282637
  • 1,827
  • 5
  • 27
  • 56
  • I would use AsynchTask for this. – ngunha02 Jun 02 '14 at 16:47
  • I am using AsyncTask to load each ListView item. How do you propose to use AsyncTask to check whether or not the list is full? – user1282637 Jun 02 '14 at 16:50
  • You will need a Progress Dialog, and a CallBack in your AsyncTask. Then your AsyncTask, make sure you pass Context as an argument for your constructor, and CallBack as well. Override onPreExecute() to show the dialog. Override onPostExecute() to pass the value return from doInBackground() to your CallBack and dimiss your Progress Dialog. I hope that makes sense to you. – ngunha02 Jun 02 '14 at 16:58
  • I have never used a CallBack but it does kind of make sense. The thing I don't know how to do is how will I know when the ListView is done loading? – user1282637 Jun 02 '14 at 17:03
  • When you call your AsyncTask in fragment or activity by using: asyncTask.execute(whatever argument you pass in here), the asynctask will do all the work in doInBackground() such as loading data,... As long as doInBackground() finish, it will pass the data to onPostExecute(). In onPostExecute(), you can check whether the data is valid, if the data is valid, you dismiss the dialog. Inside a fragment or an activity where you call your async task class, you will need to implements your callback where you will update your list. – ngunha02 Jun 02 '14 at 17:10

2 Answers2

1

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.

John D.
  • 2,521
  • 3
  • 24
  • 45
0

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

agmezr
  • 350
  • 2
  • 15
  • thank you for the answer. The thing with mine is I would like to display a spinning circle, not update the user on the progress. All I want them to know is that the data is loading. But this example still helps a lot – user1282637 Jun 02 '14 at 17:44
  • Also, what is `context` in progressBar = new ProgressDialog(context); ? – user1282637 Jun 02 '14 at 17:46
  • If you only want to show a spinning circle then comment the progressBar.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); in the onPreExecute method. That way you only will see a circle – agmezr Jun 02 '14 at 17:48
  • About the context: sorry i based in an old code and forgot to include the context. I edited the code. – agmezr Jun 02 '14 at 17:53
  • When I implement this I get android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application – user1282637 Jun 02 '14 at 18:51
  • I have tried the code myself and got no error. Remember that in the main activity is `new ExampleAsync(this).execute();` Maybe this can help you http://stackoverflow.com/a/7933358/3307382 – agmezr Jun 02 '14 at 19:53
  • this doesn't really apply to me, I have a working progress bar. I have no errors, it just isn't working the way I intend – user1282637 Jun 02 '14 at 19:54