问题
I just about got this, but I have a small problem in the order of things going off. Specifically, in my thread() I am setting up an array that is used by a Spinner. Problem is the Spinner is all set and done basically before my thread() is finished, so it sets itself up with a null array.
How do I associate the spinners ArrayAdapter with an array that is being loaded by another thread?
I've cut the code down to what I think is necessary to understand the problem, but just let me know if more is needed. The problem occurs whether or not refreshData() is called.
Along the same lines, sometimes I want to call loadData() from the menu. Directly following loadData() if I try to fire a toast on the next line this causes a forceclose, which is also because of how I'm implementing ProgressDialog.
THANK YOU FOR LOOKING
public class CMSHome extends Activity {
private static List<String> pmList = new ArrayList<String>();
// Instantiate helpers
PMListHelper plh = new PMListHelper();
ProjectObjectHelper poc = new ProjectObjectHelper();
// These objects hold lists and methods for dealing with them
private Employees employees;
private Projects projects;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Loads data from filesystem, or webservice if necessary
loadData();
// Capture spinner and associate pmList with it through ArrayAdapter
spinner = (Spinner) findViewById(R.id.spinner);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
this, android.R.layout.simple_spinner_item,
pmList);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
//---the button is wired to an event handler---
Button btn1 = (Button)findViewById(R.id.btnGetProjects);
btn1.setOnClickListener(btnListAllProjectsListener);
spinner.setOnItemSelectedListener(new MyOnItemSelectedListener());
}
private void loadData()
{
final ProgressDialog pd = ProgressDialog.show(this,
"Please wait", "Loading Data...", true, false);
new Thread(new Runnable(){
public void run(){
employees = plh.deserializeEmployeeData();
projects = poc.deserializeProjectData();
// Check to see if data actually loaded, if not then refresh
if ((employees == null) || (projects == null)) {
refreshData();
}
// Load up pmList for spinner control
pmList = employees.getPMList();
pd.dismiss();
}
}).start();
}
private void refreshData()
{
// Refresh data for Projects
projects = poc.refreshData();
poc.saveProjectData(mCtx, projects);
// Refresh data for PMList
employees = plh.refreshData();
plh.savePMData(mCtx, employees);
}
}
<---- EDIT -----> I tried changing onCreate() to the following after Jims suggestion. Not sure if I did this right, still doesn't work:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mCtx = this;
// Loads data from filesystem, or webservice if necessary
// Would like to extend this to update if files are over x days old
final ProgressDialog pd = ProgressDialog.show(this,
"Please wait", "Loading Data...", true, false);
new Thread(new Runnable(){
public void run(){
employees = plh.deserializeEmployeeData();
projects = poc.deserializeProjectData();
// Check to see if data actually loaded, if not then refresh
if ((employees == null) || (projects == null)) {
refreshData();
}
pd.dismiss();
runOnUiThread(new Runnable() {
public void run(){
// Load up pmList for spinner control
pmList = employees.getPMList();
}
});
}
}).start();
spinner = (Spinner) findViewById(R.id.spinner);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
this, android.R.layout.simple_spinner_item, pmList);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
//---the button is wired to an event handler---
Button btn1 = (Button)findViewById(R.id.btnGetProjects);
btn1.setOnClickListener(btnListAllProjectsListener);
spinner.setOnItemSelectedListener(new MyOnItemSelectedListener());
}
Wow, this took forever for me to find a solution, but Im overjoyed to have finally gotten this to work.
Updating the Spinner with a background thread can be accomplished by utilizing a handler. The handler is called after the main work of the thread is completed.
mProgressDlg = ProgressDialog.show(this, "App_Name", "Loading data...",
true, false);
new Thread(new Runnable(){
public void run() {
/*Load Data, set pmList in my case*/
mProgressDlg.dismiss();
hRefresh.sendEmptyMessage(REFRESH);
}
}).start();
Handler hRefresh = new Handler(){
@Override
public void handleMessage(Message msg) {
switch(msg.what){
case REFRESH:
spinner = (Spinner) findViewById(R.id.spinner);
final ArrayAdapter<String> adapter = new ArrayAdapter<String>(
mCtx, android.R.layout.simple_spinner_item, pmList);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
spinner.setOnItemSelectedListener(new MyOnItemSelectedListener());
break;
}
}
};
Credit to bhatt4982 and his answer on this thread
回答1:
Don't know if you've ever solved this, but this works for me:
public class Start extends Activity {
private static final String TAG = "PriceList";
ArrayAdapter<ProductCategory> category_adapter;
ArrayAdapter<ProductGroup> group_adapter;
ArrayList<ProductCategory> categories;
ArrayList<ProductGroup> groups;
ArrayList<Price> prices;
Spinner group_spinner;
Spinner category_spinner;
ProgressDialog progressDialog;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
group_spinner = (Spinner) findViewById(R.id.group_spinner);
category_spinner = (Spinner) findViewById(R.id.category_spinner);
// product category spinner
categories = new ArrayList<ProductCategory>();
category_adapter = new CustomArrayAdapter<ProductCategory>(categories);
category_adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
// load category spinner from database
loadCategory(); // adapter MUST be setup before this is called
category_spinner.setAdapter(category_adapter);
category_spinner.setOnItemSelectedListener(new OnItemSelectedListener () {
..... other stuff ......
private final Handler handler = new Handler() {
@Override
public void handleMessage(final Message msg) {
Log.v(TAG, "worker thread done, setup adapter");
switch (msg.what) {
case Constants.CATEGORIES:
category_adapter.notifyDataSetChanged();
break;
case Constants.GROUPS:
group_adapter.notifyDataSetChanged();
break;
case Constants.PRICES:
startActivity(new Intent(Start.this, ShowPrices.class));
break;
default:
}
// dismiss dialog
progressDialog.dismiss();
}
};
// loadCategory() essentially the same....
private void loadGroup(final String cat) {
Log.v(TAG, "loadGroup");
progressDialog = new ProgressDialog(this);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setMessage("Retrieving Product Groups...");
progressDialog.setMax(100);
progressDialog.setProgress(0);
progressDialog.show();
new Thread() {
@Override
public void run() {
int count = 100;
int i = 0;
SQLiteDatabase db = DbUtils.getStaticDb();
Cursor c = db.rawQuery("select count(*) from productgroup where category = \'"
+ cat + "\';", null);
c.moveToFirst();
if (!c.isAfterLast()) {
count = c.getInt(0);
}
c.close();
progressDialog.setMax(count);
groups.clear();
groups.add(new ProductGroup("-1", "--- Select ---"));
StringBuilder sb = new StringBuilder("select _id,description from productgroup");
sb.append(" where category = \'");
sb.append(cat);
sb.append("\' order by description;");
Log.v(TAG, sb.toString());
c = db.rawQuery(sb.toString(), null);
c.moveToFirst();
while (!c.isAfterLast()) {
Log.v(TAG, c.getString(0));
groups.add(new ProductGroup(c.getString(0), c.getString(1)));
i++;
if (i % 5 == 0) {
progressDialog.setProgress(i);
}
c.moveToNext();
}
c.close();
// tell UI thread OK
handler.sendEmptyMessage(Constants.GROUPS);
}
}.start();
}
// custom ArrayAdapter allows us to have our own ArrayList<T>
class CustomArrayAdapter<T> extends ArrayAdapter<T> {
CustomArrayAdapter(ArrayList<T> list) {
super(Start.this, android.R.layout.simple_spinner_item, list);
}
}
回答2:
All the work after loadData() in onCreate() needs to be done after the work in run() in the thread triggered in loadData() has been completed. Try inlining loadData(), and adding this post-setup work to a runOnUiThread() after pd.dismiss().
回答3:
I would also suggest you to use https://github.com/commonsguy/cwac-task it has very nice AsyncTaskEx impl, which you can use to do heavy work without locking the ui.
Example class;
protected class DoHeavyWorkAsync extends AsyncTaskEx<Void, Integer, String> {
private static final String TAG = "DoHeavyWorkAsync";
@Override
protected String doInBackground(Void... arg0) {
// do heavy work here. e.g. loadDataFromSomewhere();
YourActivity.this.runOnUiThread(new Runnable() {
public void run() {
// you can do ui work on the main activity from here
}
});
return null;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
Log.d(TAG, "onPreExecute()");
//e.g. display "loading..."
}
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
Log.d(TAG, "onPostExecute()");
}
}
from your main activity you can call like this;
(new DoHeavyWorkAsync()).execute();
来源:https://stackoverflow.com/questions/2596390/android-progressdialog-progress-bar-doing-things-in-the-right-order