Обновить пользовательский интерфейс из потока - PullRequest
52 голосов
/ 06 декабря 2010

Я хочу обновить свой интерфейс из потока, который обновляет индикатор прогресса. К сожалению, при обновлении ничьей индикатора выполнения из «запускаемого» индикатор исчезает! Изменение рисования индикаторов в onCreate() на сторонних работах!

Есть предложения?

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

Ответы [ 7 ]

61 голосов
/ 06 декабря 2010

Вы должны сделать это с помощью AsyncTask (интеллектуальный фоновый поток) и ProgressDialog

AsyncTask позволяет правильно и легко использовать поток пользовательского интерфейса. Этот класс позволяет выполнять фоновые операции и публиковать результаты в потоке пользовательского интерфейса без необходимости манипулировать потоками и / или обработчиками.

Асинхронная задача определяется вычислением, которое выполняется в фоновом потоке и результат которого публикуется в потоке пользовательского интерфейса. Асинхронная задача определяется 3 универсальными типами, называемыми Params, Progress и Result, и 4 шагами, называемыми begin, doInBackground, processProgress и end.

4 шага

Когда выполняется асинхронная задача, задача проходит 4 шага:

onPreExecute(), вызывается в потоке пользовательского интерфейса сразу после выполнения задачи. Этот шаг обычно используется для настройки задачи, например, показывая индикатор выполнения в пользовательском интерфейсе.

doInBackground(Params...), вызывается в фоновом потоке сразу после завершения выполнения onPreExecute (). Этот шаг используется для выполнения фоновых вычислений, которые могут занять много времени. Параметры асинхронной задачи передаются на этот шаг. Результат вычисления должен быть возвращен этим шагом и будет возвращен к последнему шагу. Этот шаг также может использовать publishProgress (Progress ...) для публикации одной или нескольких единиц прогресса. Эти значения публикуются в потоке пользовательского интерфейса на шаге onProgressUpdate (Progress ...).

onProgressUpdate(Progress...), вызывается в потоке пользовательского интерфейса после вызова publishProgress (Progress ...). Время выполнения не определено. Этот метод используется для отображения любой формы прогресса в пользовательском интерфейсе, пока выполняется фоновое вычисление. Например, его можно использовать для анимации индикатора выполнения или отображения журналов в текстовом поле.

onPostExecute(Result), вызывается в потоке пользовательского интерфейса после завершения фоновых вычислений. Результат фонового вычисления передается на этот шаг в качестве параметра. Правила потоков

Для правильной работы этого класса необходимо соблюдать несколько правил потоков:

Экземпляр задачи должен быть создан в потоке пользовательского интерфейса. execute (Params ...) должен быть вызван в потоке пользовательского интерфейса. Не вызывайте onPreExecute (), onPostExecute (Result), doInBackground (Params ...), onProgressUpdate (Progress ...) вручную. Задача может быть выполнена только один раз (исключение будет выдано, если будет предпринята вторая попытка).

Пример кода
То, что делает адаптер в этом примере, не важно, важно понимать, что вам нужно использовать AsyncTask для отображения диалогового окна прогресса.

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();
    }
}
25 голосов
/ 23 января 2012

Самое простое решение, которое я когда-либо видел, выполнение в потоке пользовательского интерфейса осуществляется с помощью метода post () представления. Это необходимо, поскольку методы пользовательского интерфейса не являются повторными. Метод для этого:

package android.view;

public class View;

public boolean post(Runnable action);

Метод post () соответствует SwingUtilities.invokeLater (). К сожалению, я не нашел чего-то простого, что соответствует SwingUtilities.invokeAndWait (), но можно построить позже на основе первого с монитором и флагом.

Итак, вы экономите на этом, создавая обработчик. Вам просто нужно чтобы найти свое мнение, а затем опубликовать на нем. Вы можете найти свой взгляд через findViewById (), если вы склонны работать с идентифицированными ресурсами. Результирующий код очень прост:

/* inside your non-UI thread */

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

Примечание: по сравнению с SwingUtilities.invokeLater () метод View.post () возвращает логическое значение, указывающее, является ли представление имеет связанную очередь событий. Так как я использовал invokeLater () соотв. post () в любом случае только для огня и забыть, Я не проверял значение результата. В основном вы должны вызывать post () только после вызова onAttachedToWindow () на вид.

С наилучшими пожеланиями

11 голосов
/ 06 декабря 2010

Если вы используете Handler (я вижу, вы это делаете и, надеюсь, вы создали его экземпляр в потоке пользовательского интерфейса), то не используйте runOnUiThread() внутри вашего runnable. runOnUiThread() используется, когда вы что-то делаете из потока, не являющегося пользовательским интерфейсом, однако Handler уже выполнит ваш runnable в потоке пользовательского интерфейса.

Попробуйте сделать что-то вроде этого:

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 голосов
/ 06 декабря 2010

Используйте класс AsyncTask (вместо Runnable).У него есть метод onProgressUpdate, который может влиять на пользовательский интерфейс (он вызывается в потоке пользовательского интерфейса).

9 голосов
/ 06 декабря 2010

Вам необходимо создать Handler в потоке пользовательского интерфейса, а затем использовать его для публикации или отправки сообщения из другого потока для обновления пользовательского интерфейса

1 голос
/ 24 июня 2011

Если вам не нравится AsyncTask, вы можете использовать шаблон наблюдателя .В этом примере используйте ResponseHandler в качестве внутреннего класса в своей деятельности, а затем получите строковое сообщение, которое установит процент выполнения индикаторов ... Вам необходимо убедиться, что любые изменения пользовательского интерфейса выполняются в ResponseHandler, чтобы избежать зависанияПользовательский интерфейс, тогда ваш рабочий поток (в данном случае EventSource) может выполнять требуемые задачи.

Я бы использовал AsyncTask, хотя шаблон наблюдателя может быть полезен по причинам настройки, плюс его легче понять.Также я не уверен, если этот способ широко принят или будет работать на 100%.Я загружаю и плагин Android теперь, чтобы проверить его

0 голосов
/ 26 сентября 2017

В соответствии с официальной документацией , вы можете использовать AsyncTask для обработки рабочих элементов длиной менее 5 мс .Если ваша задача занимает больше времени, ищите другие альтернативы.

HandlerThread является одной альтернативой Thread или AsyncTask.Если вам нужно обновить пользовательский интерфейс с HandlerThread, разместите сообщение в потоке пользовательского интерфейса Looper, а в потоке пользовательского интерфейса Обработчик может обрабатывать обновления пользовательского интерфейса.

Пример кода:

Android: тост в теме

...