2010-04-07 9 views
1

J'ai à peu près cela, mais j'ai un petit problème dans l'ordre des choses qui se passe. Plus précisément, dans mon thread() je configure un tableau qui est utilisé par un Spinner. Le problème est que le Spinner est tout réglé et fait avant que mon thread() soit fini, donc il s'installe avec un tableau nul. Comment associer les spinner ArrayAdapter à un tableau chargé par un autre thread?Android ProgressDialog barre de progression de faire les choses dans le bon ordre

J'ai réduit le code à ce que je pense est nécessaire pour comprendre le problème, mais laissez-moi savoir si plus est nécessaire. Le problème se produit si refreshData() est appelé ou non. Dans le même ordre d'idées, parfois je souhaite appeler loadData() depuis le menu. Directement après loadData() si j'essaie de lancer un toast sur la ligne suivante cela provoque un forceclose, qui est aussi à cause de la façon dont j'applique ProgressDialog.

MERCI POUR LE REGARD

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> Je essayé de changer onCreate() à ce qui suit après la suggestion JIM. Je ne sais pas si je l'ai fait ce droit, ne fonctionne toujours pas:

@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, cela a pris une éternité pour moi de trouver une solution, mais je suis fou de joie d'avoir enfin obtenu cela fonctionne.

La mise à jour du Spinner avec un fil d'arrière-plan peut être effectuée en utilisant un gestionnaire. Le gestionnaire est appelé après que le travail principal du thread est terminé.

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

Crédit bhatt4982 et sa réponse sur this thread

Répondre

0

Tous les travaux après loadData() dans onCreate() qui doit être fait après les travaux en run() dans le fil déclenché dans loadData() a été complété. Essayez d'intégrer loadData(), et d'ajouter ce travail de post-installation à un runOnUiThread() après pd.dismiss().

+0

Merci Jim, Im pas programmeur expert, donc je crains que je ne sais pas exactement ce que vous voulez dire, en particulier la partie sur « inline » J'ai essayé de modifier loadData() (inclus dans un EDIT à la question) mais en vain. Même problème. Je n'ai probablement pas mis en œuvre ce que vous avez suggéré. Toute autre aide grandement appréciée.Merci – FauxReal

+0

Mes excuses pour le jargon, «inlining» signifie copier le code d'une méthode à l'endroit où elle est appelée et supprimer la méthode. Vous pouvez le faire vous-même ou automatiquement dans Eclipse avec le menu Refactor. Quoi qu'il en soit, l'essentiel de ma suggestion originale était que la bonne façon de configurer les contrôles où une opération longue est impliquée est 1. l'installation initiale, qui établit un nouveau thread pour 2. obtenir les données, qui quand elle est complète, runOnUiThread() avec 3. remplir les contrôles avec les données. –

+0

Merci encore Jim, J'ai changé la méthode onCreate comme vous l'avez suggéré (je pense). Vous pouvez le voir ci-dessus dans la 2ème fenêtre de code. Le runOnUiThread devait-il aller dans le Thread()? Cela ne fonctionne toujours pas. Lorsque je débogue, il semble configurer l'ensemble de l'interface utilisateur avant même qu'il ne commence à exécuter le (s) thread (s). – FauxReal

3

Je ne sais pas si vous avez déjà résolu ce problème, mais cela fonctionne pour moi:

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

} 
0

je suggère également d'utiliser https://github.com/commonsguy/cwac-task il a de très belles impl AsyncTaskEx, que vous pouvez utiliser pour faire du gros travail sans bloquer l'interface utilisateur.

Exemple de classe;

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()"); 
     } 
    } 

de votre activité principale, vous pouvez appeler comme ceci;

(new DoHeavyWorkAsync()).execute();