Как добавить текст в TextView из Loader - PullRequest
1 голос
/ 16 марта 2019

Сейчас я изучаю Threads, и моя задача - создать счетчик, который с помощью Loader добавит число от 0 до 9 к TextView.Конечно, я знаю, что это не лучший вариант использовать Loader для таких задач, но я хотел бы понять, как это работает.

Итак, у меня есть следующий код:

package asus.example.com.exercise4;

import android.annotation.SuppressLint;
import android.content.Context;
import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.AsyncTaskLoader;
import android.support.v4.content.Loader;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class LoaderActivity extends AppCompatActivity {

    private TextView counter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_threads);
        Button startButton = findViewById(R.id.start_button);
        Button cancelButton = findViewById(R.id.cancel_button);
        startButton.setOnClickListener(listener);
        cancelButton.setOnClickListener(listener);
        counter = findViewById(R.id.counter);
    }

    private View.OnClickListener listener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            switch (v.getId()){
                case R.id.start_button:
                    getSupportLoaderManager().initLoader(0, null, new LoaderClass());
                    break;
                case R.id.cancel_button:
                    break;
            }
        }
    };



    @SuppressLint("StaticFieldLeak")
    class AsyncTaskLoaderClass extends AsyncTaskLoader<Void>{

        AsyncTaskLoaderClass(@NonNull Context context) {
            super(context);
        }

        @Nullable
        @Override
        public Void loadInBackground() {
            for (int i = 0; i<10;i++){
                counter.setText(i);
                SystemClock.sleep(500);
            }
            return null;
        }
    }

    private class LoaderClass implements LoaderManager.LoaderCallbacks<Void>{

        @NonNull
        @Override
        public Loader<Void> onCreateLoader(int i, @Nullable Bundle bundle) {
            return new LoaderActivity.AsyncTaskLoaderClass(LoaderActivity.this);
        }

        @SuppressLint("SetTextI18n")
        @Override
        public void onLoadFinished(@NonNull Loader<Void> loader, Void aVoid) {
            counter.setText("Done!");
        }

        @Override
        public void onLoaderReset(@NonNull Loader<Void> loader) {

        }
    }



}

Когда я запускаю проект, у меня появляется ошибка времени выполнения:

java.lang.IllegalArgumentException: Object returned from onCreateLoader must not be a non-static inner member class: AsyncTaskLoaderClass{eed39bf id=0}

Да, я понимаю, что этоозначает, что AsyncTaskLoaderClass должен быть в другом файле или в статическом файле, но в этом случае у меня не будет возможности добавить текст к textview.Итак, как я могу решить эту проблему?

UPD

Я изменил код при нажатии кнопки запуска таким образом:

case R.id.start_button:
                Loader loader = getSupportLoaderManager().initLoader(0, null, LoaderActivity.this);
                loader.forceLoad();
                Log.i(TAG, "Button start clicked");
                break;

Итеперь каждый раз в цикле у меня появляется следующая ошибка:

E/e.com.exercise: Invalid ID 0x00000009.
E/EventBus: Could not dispatch event: class asus.example.com.exercise4.LoaderActivity$MyAsyncTaskLoader$ProgressEvent to subscribing class class asus.example.com.exercise4.LoaderActivity
    android.content.res.Resources$NotFoundException: String resource ID #0x9

UPD 2

Наконец исправлена ​​проблема следующим образом:

Был

counter.setText(i);

Сейчас

counter.setText(""+i);

Возможно, я не понимаю, почему это работает, но работает

Ответы [ 3 ]

1 голос
/ 16 марта 2019

Заставьте Activity реализовать LoaderCallbacks. Также Loader извлекает одно конкретное значение в своем обратном вызове onLoadFinished, и в результате он должен вернуть полученный (загруженный) элемент.

Чтобы изменить значение, загружаемое загрузчиком, вы должны перезапустить загрузчик с новым набором аргументов и передать параметры, чтобы он знал, что он делает.

Опять же, вы пытаетесь создать что-то вроде "publishProgress" в AsyncTask; Загрузчики не могут сделать это «из коробки», и им нужен какой-то вариант «отправки события» (потоки обработчика, если вы любите приключения, но, скорее всего, шина событий , см. implementation 'org.greenrobot:eventbus:3.1.1').

TL; DR: используйте для этого EventBus .

public class LoaderActivity extends AppCompatActivity  implements LoaderManager.LoaderCallbacks<Void> {
    private TextView counter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_threads);

        Button startButton = findViewById(R.id.start_button);
        Button cancelButton = findViewById(R.id.cancel_button);
        counter = findViewById(R.id.counter);
        startButton.setOnClickListener((view) -> {
            getSupportLoaderManager().initLoader(0, null, LoaderActivity.this);
        });
        cancelButton.setOnClickListener((view) -> {
            // do nothing, apparently
        });

        EventBus.getDefault().register(this);
    }

    @Override
    protected void onDestroy() {
        EventBus.getDefault().unregister(this);
        super.onDestroy();
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onLoaderProgressEvent(MyAsyncTaskLoader.ProgressEvent event) {
        counter.setText("" + event.getNumber());
    }

    @NonNull
    @Override
    public Loader<Void> onCreateLoader(int i, @Nullable Bundle bundle) {
        return new MyAsyncTaskLoader(LoaderActivity.this);
    }

    @SuppressLint("SetTextI18n")
    @Override
    public void onLoadFinished(@NonNull Loader<Void> loader, Void aVoid) {
        counter.setText("Done!");
    }

    @Override
    public void onLoaderReset(@NonNull Loader<Void> loader) {

    }

    public static class MyAsyncTaskLoader extends AsyncTaskLoader<Void> {
        public static class ProgressEvent {
            private final int number;

            public ProgressEvent(int number) {
                this.number = number;
            }

            public int getNumber() { return number; }
        }

        public MyAsyncTaskLoader(@NonNull Context context) {
            super(context);
        }

        @Nullable
        @Override
        public Void loadInBackground() {
            for (int i = 0; i<10;i++){
                EventBus.getDefault().post(new ProgressEvent(i));
                SystemClock.sleep(500);
            }
            return null;
        }
    }
}
0 голосов
/ 16 марта 2019

Вы используете внутренний AsyncTaskLoaderClass в Activity классе. Внутренний класс содержит ссылку Внешнего класса. Это означает, что в некоторых случаях ваша AsyncTaskLoaderClass может содержать ссылку Activity. Сделайте ваш внутренний класс статичным.

У вас есть 2 решения. Сделайте AsyncTaskLoaderClass отдельным файлом класса или сделайте AsyncTaskLoaderClass статическим классом.

0 голосов
/ 16 марта 2019

сделать текстовое представление общедоступным статическим, как это public static TextView counter;

...