【问题标题】:How do I update a view out of an AsyncTask?如何从 AsyncTask 中更新视图?
【发布时间】:2019-05-25 13:40:24
【问题描述】:

我必须创建一个RecyclerView,每次我的AsyncTask 创建一个新项目时都会更新它。所以RecyclerView 正在逐渐建立起来。 每个 Item 都会生成,然后 Thread 会休眠一段时间以查看进度较慢。

我尝试获取适配器并使用notifyDataSetChanged() 更新它,但它不会像这样工作。我得到的错误是:

只有创建视图层次结构的原始线程才能触及它的 意见。

另一个想法是使用接口更新我的MainActivity 中的适配器。但我不完全知道该怎么做。首先,我必须知道它是否是使用界面的正确方法,或者是否有更好的方法,或者是否有一个非常简单的解决方案来解决我的问题。

public class MainActivity extends AppCompatActivity implements ListAdapter.Listener{

    static RecyclerView recyclerView;

    static ListAdapter adapter;

    @Override
    public void click(String name) {

        if(getResources().getConfiguration().orientation== Configuration.ORIENTATION_LANDSCAPE){
            DetailsFragment detailsFragment = (DetailsFragment)getSupportFragmentManager().findFragmentById(R.id.fragment_details);
            detailsFragment.setData(name);

        }
        else{
            Toast.makeText(this, "clicked", Toast.LENGTH_SHORT).show();
            Intent intent = new Intent(this, DetailsActivity.class);
            intent.putExtra("sorte_name", name);
            startActivity(intent);
        }
    }


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

        new DataContainer().execute();

    }

    private void initRecyclerView(){
        recyclerView = findViewById(R.id.meinRecyclerView);
        adapter = new ListAdapter(this, DataContainer.meineSortenListe, this);
        recyclerView.setAdapter(adapter);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
    }

}

public class DataContainer extends AsyncTask<Void, Void, Void> {

    public static ArrayList<String> meineSortenListe = new ArrayList<String>();

    ListAdapter myAdapter;

    @Override
    protected void onPreExecute() {
        myAdapter =(ListAdapter)MainActivity.recyclerView.getAdapter();
        super.onPreExecute();
    }

    @Override
    protected Void doInBackground(Void... voids) {
        for(int i= 0; i<50; i++){
            meineSortenListe.add("Sorte "+i);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            onProgressUpdate();
        }
        Log.i("info", "array befüllt");
        return null;
    }

    @Override
    protected void onProgressUpdate(Void... values) {
        myAdapter.notifyDataSetChanged();
        super.onProgressUpdate(values);
    }

    @Override
    protected void onPostExecute(Void aVoid) {
        super.onPostExecute(aVoid);
    }
}

public class ListAdapter extends RecyclerView.Adapter<ListAdapter.ViewHolder>{

    private ArrayList<String> mSorten;
    private Context mContext;
    private Listener listener;

    public interface Listener{
        void click(String name);
    }


    //Constructor
    public ListAdapter(Context mContext, ArrayList<String> sortenListe, Listener listener) {
        this.mContext = mContext;
        this.mSorten = sortenListe;
        this.listener=listener;
    }

    ///////////////////////

    public class ViewHolder extends RecyclerView.ViewHolder {

        TextView sortenName;
        LinearLayout sorteLayout;

        public ViewHolder(View itemView) {
            super(itemView);
            sortenName = itemView.findViewById(R.id.nameSorte);
            sorteLayout = itemView.findViewById(R.id.sorteLayout);
        }

    }


    ////////////////////////////////

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item, parent, false);
        ViewHolder holder = new ViewHolder(view);
        return holder;
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, final int position) {
        holder.sortenName.setText(mSorten.get(position));

        holder.sorteLayout.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                listener.click(mSorten.get(position));
            }
        });
    }

    @Override
    public int getItemCount() {
        return mSorten.size();
    }

}

【问题讨论】:

  • 下面的答案是不必要的。如果正确触发,onProgressUpdate() 方法将在 UI 线程上运行。不要直接调用onProgressUpdate(),而是调用publishProgress(),它会为你处理线程切换。为了完整起见,我只是提到这一点。没有人真的应该再使用AsyncTask了。
  • 感谢您的提及 :) 绝对是更好的方法

标签: java android android-recyclerview android-asynctask android-adapter


【解决方案1】:

在 Android 中,只有 UI 线程可以更新视图。 (onPostExecute 可以触发 UI 更新,因为onPostExecute 正在切换到 UI 线程。)

由于您仍在 doInBackground() 函数中,因此您需要一个 Handler 将带有您的代码的 Runnable 发送到 UI 线程的 MessageQueue。

因此,在您的 DataContainer 中,将 onProgressUpdate 更改为

protected void onProgressUpdate(Void... values) {
    Handler handler = new Handler(Looper.getMainLooper());
    handler.post(new Runnable(){
        public void run() {
            myAdapter.notifyDataSetChanged();            
        }
    });

    super.onProgressUpdate(values);
}

【讨论】:

  • 解决了 :) 非常感谢!我得看看 Handlers 是如何工作的
猜你喜欢
  • 2019-05-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-01
  • 1970-01-01
  • 2015-03-30
  • 1970-01-01
相关资源
最近更新 更多