2010-12-06 20 views
49

Je souhaite mettre à jour mon interface utilisateur à partir d'un thread qui met à jour une barre de progression. Malheureusement, lors de la mise à jour du drawable de la barre de progression à partir du "runnable", la barre de progression disparaît! Changer le drawable de la barre de progression en onCreate() sur les autres travaux!Mise à jour de l'interface utilisateur du thread

Avez-vous des suggestions?

public void onCreate(Bundle savedInstanceState) { 
    res = getResources(); 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.gameone); 
    pB.setProgressDrawable(getResources().getDrawable(R.drawable.green)); //**Works**/ 
    handler.postDelayed(runnable, 1);  
} 

private Runnable runnable = new Runnable() { 
    public void run() { 
     runOnUiThread(new Runnable() { 
      public void run() 
      { 
       //* The Complete ProgressBar does not appear**/       
       pB.setProgressDrawable(getResources().getDrawable(R.drawable.green)); 
      } 
     }); 
    } 
} 

Répondre

9

Vous devez créer un Handler dans le thread d'interface utilisateur, puis l'utiliser pour afficher ou envoyer un message de votre autre thread de mettre à jour l'interface utilisateur

11

Utilisez la classe AsyncTask (au lieu de Runnable). Il a une méthode appelée onProgressUpdate qui peut affecter l'interface utilisateur (elle est appelée dans le thread d'interface utilisateur).

11

Si vous utilisez Handler (je vois que vous faites et nous espérons que vous avez créé son instance sur le thread d'interface utilisateur), alors ne pas utiliser runOnUiThread() à l'intérieur de votre runnable. runOnUiThread() est utilisé lorsque vous utilisez un thread non-interface utilisateur, mais Handler exécute déjà votre runnable sur le thread d'interface utilisateur.

Essayez de faire qch comme ceci:

private Handler mHandler = new Handler(); 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.gameone); 
    res = getResources(); 
    // pB.setProgressDrawable(getResources().getDrawable(R.drawable.green)); **//Works** 
    mHandler.postDelayed(runnable, 1); 
} 

private Runnable runnable = new Runnable() { 
    public void run() { 
     pB.setProgressDrawable(getResources().getDrawable(R.drawable.green)); 
     pB.invalidate(); // maybe this will even not needed - try to comment out 
    } 
}; 
60

Vous devriez le faire avec l'aide de AsyncTask (un fil backround intelligent) et ProgressDialog

AsyncTask permet une utilisation correcte et facile du thread d'interface utilisateur. Cette classe permet d'effectuer des opérations d'arrière-plan et de publier des résultats sur le thread d'interface utilisateur sans avoir à manipuler des threads et/ou des gestionnaires.

Une tâche asynchrone est définie par un calcul qui s'exécute sur un thread d'arrière-plan et dont le résultat est publié sur le thread de l'interface utilisateur. Une tâche asynchrone est définie par 3 types génériques, appelés Params, Progress et Result, et 4 étapes, appelées begin, doInBackground, processProgress et end.

Les 4 étapes

Lorsqu'une tâche asynchrone est exécutée, la tâche passe par 4 étapes:

onPreExecute(), sur le fil invoqué de l'interface utilisateur immédiatement après la tâche est exécutée. Cette étape est normalement utilisée pour configurer la tâche, par exemple en affichant une barre de progression dans l'interface utilisateur.

doInBackground(Params...), invoqué sur le thread d'arrière-plan immédiatement après l'exécution de onPreExecute(). Cette étape est utilisée pour effectuer un calcul en arrière-plan qui peut prendre beaucoup de temps. Les paramètres de la tâche asynchrone sont transmis à cette étape. Le résultat du calcul doit être retourné par cette étape et sera renvoyé à la dernière étape. Cette étape peut également utiliser publishProgress (Progress ...) pour publier une ou plusieurs unités de progression. Ces valeurs sont publiées sur le thread de l'interface utilisateur dans l'étape onProgressUpdate (Progress ...).

onProgressUpdate(Progress...), invoquée sur le thread d'interface utilisateur après un appel à publishProgress (Progress ...). Le moment de l'exécution est indéfini. Cette méthode est utilisée pour afficher toute forme de progression dans l'interface utilisateur alors que le calcul de l'arrière-plan est toujours en cours d'exécution. Par exemple, il peut être utilisé pour animer une barre de progression ou afficher des journaux dans un champ de texte.

onPostExecute(Result), invoquée sur le thread d'interface utilisateur après la fin du calcul de l'arrière-plan. Le résultat du calcul de l'arrière-plan est passé à cette étape en tant que paramètre. règles Threading

Il y a quelques règles de filetage qui doivent être suivies pour cette classe fonctionne correctement:

L'instance de tâche doit être créé sur le thread d'interface utilisateur. execute (Params ...) doit être appelée sur le thread d'interface utilisateur. Ne pas appeler onPreExecute(), onPostExecute (résultat), doInBackground (paramètres ...), onProgressUpdate (progression ...) manuellement. La tâche peut être exécutée qu'une seule fois (une exception sera levée si une seconde exécution est tentée.)

code Exemple
Qu'est-ce que l'adaptateur fait dans cet exemple est pas important, plus important de comprendre que vous avez besoin utiliser AsyncTask pour afficher une boîte de dialogue pour la progression.

private class PrepareAdapter1 extends AsyncTask<Void,Void,ContactsListCursorAdapter > { 
    ProgressDialog dialog; 
    @Override 
    protected void onPreExecute() { 
     dialog = new ProgressDialog(viewContacts.this); 
     dialog.setMessage(getString(R.string.please_wait_while_loading)); 
     dialog.setIndeterminate(true); 
     dialog.setCancelable(false); 
     dialog.show(); 
    } 
    /* (non-Javadoc) 
    * @see android.os.AsyncTask#doInBackground(Params[]) 
    */ 
    @Override 
    protected ContactsListCursorAdapter doInBackground(Void... params) { 
     cur1 = objItem.getContacts(); 
     startManagingCursor(cur1); 

     adapter1 = new ContactsListCursorAdapter (viewContacts.this, 
       R.layout.contact_for_listitem, cur1, new String[] {}, new int[] {}); 

     return adapter1; 
    } 

    protected void onPostExecute(ContactsListCursorAdapter result) { 
     list.setAdapter(result); 
     dialog.dismiss(); 
    } 
} 
+5

"AsyncTasks devrait idéalement être utilisé pour les opérations courtes (quelques secondes au plus.) [...]" - http://developer.android.com/ reference/android/os/AsyncTask.html –

1

Si vous ne souhaitez pas le AsyncTask vous pouvez utiliser le observer pattern. Dans cet exemple, utilisez le ResponseHandler en tant que classe interne dans votre activité puis ayez un message de chaîne qui définira le pourcentage de barre de progression ... Vous devez vous assurer que toute modification de l'interface utilisateur est effectuée dans le ResponseHandler pour éviter de geler le L'interface utilisateur, puis votre thread de travail (EventSource dans l'exemple) peut effectuer les tâches requises.

Je voudrais utiliser l'AsyncTask tho, mais le modèle d'observateur peut être bon pour des raisons de personnalisation, plus facile à comprendre. Aussi je ne sais pas si cette façon est largement acceptée ou fonctionnera à 100%. Im le téléchargement et le plugin android maintenant pour le tester

23

La solution la plus simple que j'ai vu pour fournir une courte exécution à la thread UI est via la méthode post() d'une vue. Ceci est nécessaire car les méthodes d'interface utilisateur ne sont pas réentrantes. Le procédé est constitué par:

package android.view; 

public class View; 

public boolean post(Runnable action); 

La méthode post() correspond à la SwingUtilities.invokeLater(). Malheureusement, je n'ai pas trouvé quelque chose de simple qui correspond à le SwingUtilities.invokeAndWait(), mais on peut construire le plus tard basé sur le premier avec un moniteur et un drapeau.

Donc, ce que vous économisez en créant un gestionnaire. Vous avez simplement besoin de pour trouver votre point de vue et ensuite poster dessus. Vous pouvez trouver votre vue via findViewById() si vous avez tendance à travailler avec des ressources id-ed. Le résultat du code est très simple:

/* inside your non-UI thread */ 

view.post(new Runnable() { 
     public void run() { 
      /* the desired UI update */ 
     } 
    }); 
} 

Note: Par rapport à SwingUtilities.invokeLater() la méthode View.post() ne renvoie une valeur booléenne indiquant si l'affichage a une file d'attente d'événement associé. Depuis que j'ai utilisé le invokeLater() resp. post() de toute façon seulement pour le feu et oublier, Je n'ai pas vérifié la valeur du résultat. Fondamentalement, vous devriez appel post() seulement après onAttachedToWindow() a été appelé sur la vue.

Cordialement

+0

Si 'view' est une référence à un objet réel comme' progress bar', laisser une activité ou un fragment qui crée la vue entière recycle la ressource 'view', provoquant' null' .post (~); – Stallman

+0

Pour savoir comment tuer le looper via un post, voir ici: http://stackoverflow.com/a/30562809/502187 –

0

Comme l'a recommandé officielle documentation, vous pouvez utiliser AsyncTaskpour gérer les éléments de travail plus courtes que 5ms durée. Si votre tâche prend plus de temps, cherchez d'autres alternatives.

HandlerThread est une alternative à Thread ou AsyncTask. Si vous devez mettre à jour l'interface utilisateur à partir de HandlerThread, publiez un message sur le thread d'interface utilisateur et le thread d'interface utilisateur Handler peut gérer les mises à jour de l'interface utilisateur.

code Exemple:

Android: Toast in a thread