如何从 Loader 向 TextView 添加文本
How to add text to TextView from Loader
现在我正在研究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);
可能我不明白为什么会这样,但确实有效
像这样使 textview public 静态 public static TextView counter;
您正在 Activity
class 中使用内部 AsyncTaskLoaderClass
。内部 class 持有外部 class 的引用。这意味着在某些情况下,您的 AsyncTaskLoaderClass
可能包含 Activity
引用。让你的内心 class 静止。
您有 2 个解决方案。将 AsyncTaskLoaderClass
设为单独的 class 文件或将 AsyncTaskLoaderClass
设为静态 class.
使 Activity 实现 LoaderCallbacks。此外,加载程序在其 onLoadFinished
回调中检索 一个特定值 ,并且它应该 return 检索(加载)的项目结果。
要更改加载程序正在加载的值,您应该使用新的参数包重新启动加载程序,并传入参数以便它知道它在做什么。
然后,您又试图在 AsyncTask 中创建类似于 "publishProgress" 的内容;加载程序不能开箱即用,需要 "sending an event" 的一些变体(处理线程,如果你喜欢冒险,但很可能是 event bus,请参阅 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;
}
}
}
现在我正在研究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);
可能我不明白为什么会这样,但确实有效
像这样使 textview public 静态 public static TextView counter;
您正在 Activity
class 中使用内部 AsyncTaskLoaderClass
。内部 class 持有外部 class 的引用。这意味着在某些情况下,您的 AsyncTaskLoaderClass
可能包含 Activity
引用。让你的内心 class 静止。
您有 2 个解决方案。将 AsyncTaskLoaderClass
设为单独的 class 文件或将 AsyncTaskLoaderClass
设为静态 class.
使 Activity 实现 LoaderCallbacks。此外,加载程序在其 onLoadFinished
回调中检索 一个特定值 ,并且它应该 return 检索(加载)的项目结果。
要更改加载程序正在加载的值,您应该使用新的参数包重新启动加载程序,并传入参数以便它知道它在做什么。
然后,您又试图在 AsyncTask 中创建类似于 "publishProgress" 的内容;加载程序不能开箱即用,需要 "sending an event" 的一些变体(处理线程,如果你喜欢冒险,但很可能是 event bus,请参阅 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;
}
}
}