web-dev-qa-db-fra.com

Mettre à jour l'interface utilisateur à partir du fil

Je souhaite mettre à jour mon interface utilisateur à partir d'un thread qui met à jour une barre de progression. Malheureusement, lors de la mise à jour de la "progressable" de la barre de progression, celle-ci disparaît! Changer les barres de progression dessinées dans onCreate() de l’autre côté fonctionne!

Aucune suggestion?

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

Vous devez le faire à l’aide de AsyncTask (un fil de travail intelligent) et ProgressDialog

AsyncTask permet d'utiliser correctement et facilement le thread d'interface utilisateur. Cette classe permet d'effectuer des opérations en 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 en arrière-plan et dont le résultat est publié sur le thread d'interface utilisateur. Une tâche asynchrone est définie par 3 types génériques, appelés Params, Progression et Résultat, 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(), appelée sur le thread d'interface utilisateur immédiatement après l'exécution de la tâche. 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 durer longtemps. Les paramètres de la tâche asynchrone sont passés à cette étape. Le résultat du calcul doit être renvoyé 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 d'interface utilisateur, à l'étape onProgressUpdate (Progress ...).

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

onPostExecute(Result), invoqué sur le thread d'interface utilisateur une fois le calcul d'arrière-plan terminé. Le résultat du calcul en arrière-plan est transmis à cette étape sous la forme d'un paramètre .

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

L'instance de tâche doit être créée sur le thread d'interface utilisateur . Execute (Params ...) doit être appelée sur le thread d'interface utilisateur . N'appelez pas onPreExecute (), onPostExecute (Result), doInBackground (Param ... ), onProgressUpdate (Progression ...) manuellement ... La tâche ne peut être exécutée qu’une seule fois (une exception sera levée si une deuxième exécution est tentée.)

Exemple de code
Ce que fait l'adaptateur dans cet exemple n'est pas important, il est plus important de comprendre que vous devez utiliser AsyncTask pour afficher une boîte de dialogue indiquant 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();
    }
}
61
Pentium10

La solution la plus simple que j'ai vue pour fournir une exécution courte au thread d'interface utilisateur est via la méthode post () d'une vue ..__, nécessaire car les méthodes d'interface utilisateur ne sont pas ré-entrantes. La méthode pour cela est la suivante:

package Android.view;

public class View;

public boolean post(Runnable action);

La méthode post () correspond à SwingUtilities.invokeLater () . Malheureusement, je n’ai pas trouvé quelque chose de simple qui correspond à SwingUtilities.invokeAndWait (), mais il est possible de construire ce dernier ancien avec un moniteur et un drapeau.

Donc, ce que vous économisez en créant un gestionnaire. Il vous suffit simplement de trouver votre point de vue, puis de le publier. Vous pouvez trouver votre vue via FindViewById () si vous avez tendance à travailler avec des ressources id-ed. Le code Résultant est très simple:

/* inside your non-UI thread */

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

Remarque: par rapport à SwingUtilities.invokeLater (), la méthode View.post () renvoie un booléen indiquant si la vue Est associée à une file d'attente d'événements. Depuis que j'ai utilisé le InvokeLater () resp. post () de toute façon que pour feu et oublier, Je n'ai pas vérifié la valeur du résultat. En principe, vous devriez Appeler post () seulement après que onAttachedToWindow () a été appelé Dans la vue. 

Meilleures salutations

23
j4n bur53

Si vous utilisez Handler (je le vois et espérons que vous avez créé son instance sur le thread d'interface utilisateur), n'utilisez pas runOnUiThread() dans votre runnable. runOnUiThread() est utilisé lorsque vous faites un thread non-UI, cependant Handler exécutera déjà votre runnable sur le thread UI.

Essayez de faire comme ça:

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
    }
};
11
Vit Khudenko

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

11
pablochan

Vous devez créer une variable Handler dans le fil de l'interface utilisateur, puis l'utiliser pour publier ou envoyer un message de votre autre fil afin de mettre à jour l'interface utilisateur.

9
schwiz

Si vous n'aimez pas la tâche Async, vous pouvez utiliser le modèle observer . Dans cet exemple, utilisez ResponseHandler en tant que classe interne de votre activité, puis créez un message indiquant le pourcentage de barres de progression ... Vous devez vous assurer que toute modification apportée à l'interface utilisateur est effectuée dans ResponseHandler afin d'éviter de geler le L'interface utilisateur, votre thread de travail (EventSource dans l'exemple) peut effectuer les tâches requises.

Je voudrais utiliser AsyncTask, cependant, le motif de l'observateur peut être bon pour des raisons de personnalisation, plus il est plus facile à comprendre. Aussi je ne suis pas sûr si cette façon est largement acceptée ou fonctionnera à 100%. Je télécharge et le plugin Android maintenant pour le tester

1
tmho

Comme recommandé par documentation , vous pouvez utiliser AsyncTaskpour gérer les éléments de travail de moins de 5 ms}. Si votre tâche prend plus de temps, cherchez d'autres solutions. 

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

Exemple de code:

Android: Toast dans un fil de discussion

0
Ravindra babu