如何在RecyclerView GridLayout中使用notifyDataSetChanged

4

我是 Android 新手,正在尝试更新 Udacity 纳米学位项目中的热门电影应用程序中的 gridlayout

我正在使用 AsyncTask 下载数据并解析数据,在 onPostExecute 中更新 recyclerview 并设置适配器。

这是我的 AsyncTask 类

private class getJson extends AsyncTask<String, Void, String>{

    @Override
    protected String doInBackground(String... strings) {
        String result = "";
        URL url;
        HttpURLConnection httpURLConnection = null;


        try {
            url = new URL(strings[0]);

            httpURLConnection = (HttpURLConnection) url.openConnection();

            InputStream in = httpURLConnection.getInputStream();

            InputStreamReader reader = new InputStreamReader(in);

            int data = reader.read();

            while ( data != -1 ){
                  char current  = (char) data;

                  result += current;

                  data = reader.read();
            }

        } catch (java.io.IOException e) {
            e.printStackTrace();
        }

        return result;
    }

    @Override
    protected void onPostExecute(String s) {
        super.onPostExecute(s);

        jsonString = s;

        Log.i("result", jsonString);

        JSONObject jsonObject = null;

        if(!list.isEmpty())
          list.clear();
        try {

            jsonObject = new JSONObject(jsonString);

            JSONArray jsonArray = jsonObject.getJSONArray("results");

            for( int i = 0; i < jsonArray.length() && i < 10; i++ ){

                JSONObject currObject = jsonArray.getJSONObject(i);

                String posterurl = preUrl + currObject.getString("backdrop_path");

                Log.i("poster", posterurl);

                String url =  preUrl + currObject.getString("poster_path");

                String overview = currObject.getString("overview");

                String title = currObject.getString("original_title");

                String release_date = (currObject.getString("release_date"));

                String votes = (currObject.getString("vote_average"));

                list.add(new MovieDetails(url,overview, title, release_date, votes, posterurl));
            }

        } catch (JSONException e) {
            e.printStackTrace();
        }



    }
}

这是我的适配器类

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

    ArrayList<MovieDetails> data;
    Context context;

    public MainRecyclerViewAdapter(Context context, ArrayList<MovieDetails> data ){
         this.data = data;
         this.context = context;
    }
    @Override
    public MainRecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.grid_item, parent, false);

        MainRecyclerViewAdapter.ViewHolder viewHolder = new MainRecyclerViewAdapter.ViewHolder(v);

        return viewHolder;
    }

    @Override
    public void onBindViewHolder(MainRecyclerViewAdapter.ViewHolder holder, int position) {

        Picasso.with(context).load(data.get(position).getImageUrl()).into(holder.movie_thumbnail);
        //holder.movie_thumbnail.setImageResource(R.mipmap.ic_launcher);


    }

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

    public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{

        ImageView movie_thumbnail;

        private Context context;

        @Override
        public void onClick(View view) {
            Toast.makeText(context,data.get(getPosition()).getTitle(),Toast.LENGTH_LONG).show();
            Intent intent = new Intent(context, DetailsActivity.class);
            Bundle bundle = new Bundle();
            bundle.putSerializable("list", (Serializable) data);
            intent.putExtras(bundle);
            intent.putExtra("index", getAdapterPosition());

            context.startActivity(intent);
        }

        public ViewHolder(View itemView) {
            super(itemView);
            context = itemView.getContext();
            itemView.setOnClickListener(this);
            movie_thumbnail = (ImageView) itemView.findViewById(R.id.movies_item_thumbnail);
        }
    }
}

这是我处理基于投票或热度的排序的方式。
@Override
public boolean onOptionsItemSelected(MenuItem item) {

    getJson gJson = new getJson();
    switch (item.getItemId()){
        case R.id.rating :
            gJson.execute(top_rated);
            return true;
        case R.id.popularity : gJson.execute(popular);
            return true;
        default: return super.onOptionsItemSelected(item);

    }

}

onCreate

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Toolbar myToolbar = (Toolbar) findViewById(R.id.main_toolbar);
    setSupportActionBar(myToolbar);

    getJson getJson = new getJson();
    getJson.execute(popular);

    recyclerView = (RecyclerView)findViewById(R.id.main_recyclerview);

    recyclerView.setHasFixedSize(true);
    layoutManager = new GridLayoutManager(MainActivity.this,2);
    recyclerView.setLayoutManager(layoutManager);

    adapter = new MainRecyclerViewAdapter(MainActivity.this, list);
    recyclerView.setAdapter(adapter);

}

我已经想出了使用notifydatasetchanged()的方法,但是我不知道在哪里使用它。

请告诉我更好的处理排序的方法。

这是我做的:

  1. 我将适配器的实例化和设置移动到了onCreate中
  2. onPostExecute()中调用notfiydatasetchanged()

  3. 每次调用AsyncTask时,我的arraylist都会被新元素填充,因此适配器会填充所有图像。我在onPostExecute()中解析json之前list.clear()


我不太明白你的问题。你已经在代码中调用了 adapter.notifyDataSetChanged();,它在 onPostExecute 中。请问你能让我理解一下吗? - X09
但我正在使用recyclerview,这个答案讨论的是arrayadapter和arrayadapter的方法。 - potter13
@Ozuf 对不起,我在问题中没有提到这一点,我使用了notifydatasetchanged(),看到其他答案也是这样做的,但对我没有起作用。我会更新问题。 - potter13
@potter13,我可以给你一个答案,但这并不能帮助你提高自己的知识水平,因为当你自己阅读和学习时,会有不同的收获。看看这个视频,虽然它有点旧,但我真的很推荐给你 https://www.youtube.com/watch?v=wDBM6wVEO70 ,并且浏览一下已经存在的关于这个主题的答案,如果你想的话,我可以评论其中的几个。 - Charuක
@Charuka 谢谢,我会仔细阅读它。 - potter13
显示剩余2条评论
5个回答

1

对于 http-requests,我建议使用 retrofit

适配器描述了您想要的选项上的排序功能,调用它,最后调用notifydatasetchanged();

例如

void sort(String sortBy) {
    // your sort
    notifydatasetchanged();
}

当您想使用排序时,只需调用sorting adapter的方法,并将数据设置到其中即可。

谢谢,这个应用需要分两部分提交。我将在第二部分的提交中使用Retrofit,我想我可以在第一部分集中精力处理RecyclerView、Intents和所有基本的东西。 - potter13

1
在活动的onCreate方法中设置adapter。保留HTML标记不进行解释。
    adapter = new MainRecyclerViewAdapter(MainActivity.this, list);
    recyclerView.setAdapter(adapter);

使用一个字段来存储你的列表。
在AsyncTask的onPostExecute()中调用notifydatasetchanged()(就像你现在正在做的那样),但不要在那里重新分配adapter。

我在活动的onCreate中设置了适配器,并在PostExecute()中调用notifydatasetchanged(),但没有用,当我重新运行应用程序或更改排序时,以前的图像不会清除。 - potter13
你将适配器实例化移出了后执行区吗? - Cory Roy

1
就像Cory Roy建议的那样,试试这个:

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

        recyclerView = (RecyclerView)findViewById(R.id.main_recyclerview);
        layoutManager = new GridLayoutManager(MainActivity.this, 2);
        recyclerView.setLayoutManager(layoutManager);

        adapter = new MainRecyclerViewAdapter(MainActivity.this, list);
        recyclerView.setAdapter(adapter);
    }

   private class getJson extends AsyncTask<String, Void, String>{

    @Override
    protected String doInBackground(String... strings) {
        String result = "";
        URL url;
        HttpURLConnection httpURLConnection = null;

        try {
            url = new URL(strings[0]);

            httpURLConnection = (HttpURLConnection) url.openConnection();

            InputStream in = httpURLConnection.getInputStream();

            InputStreamReader reader = new InputStreamReader(in);

            int data = reader.read();

            while ( data != -1 ){
                  char current  = (char) data;

                  result += current;

                  data = reader.read();
            }

        } catch (java.io.IOException e) {
            e.printStackTrace();
        }

        return result;
    }

    @Override
    protected void onPostExecute(String s) {
        super.onPostExecute(s);

        jsonString = s;

        Log.i("result", jsonString);

        JSONObject jsonObject = null;
        try {

            jsonObject = new JSONObject(jsonString);

            JSONArray jsonArray = jsonObject.getJSONArray("results");

            for( int i = 0; i < jsonArray.length() && i < 10; i++ ){

                JSONObject currObject = jsonArray.getJSONObject(i);

                String posterurl = preUrl + currObject.getString("backdrop_path");

                Log.i("poster", posterurl);

                String url =  preUrl + currObject.getString("poster_path");

                String overview = currObject.getString("overview");

                String title = currObject.getString("original_title");

                String release_date = (currObject.getString("release_date"));

                String votes = (currObject.getString("vote_average"));

                list.add(new MovieDetails(url,overview, title, release_date, votes, posterurl));
            }

        } catch (JSONException e) {
            e.printStackTrace();
        }

        adapter.notifyDataSetChanged();

    }
}

请尝试一下,然后告诉我它是否有效。

是的,我尝试过这个,但当我使用按评分排序时,之前的图像不会清除,两组图像都会加载。 - potter13

1
你可以使用AsyncTask从远程服务器下载电影。一旦下载和解析完成,您就可以创建适配器并将其设置到RecyclerView中。
根据您的实现,不需要在postExecute()中调用notifyDataSetChanged()
上述方法有一个缺点,即每次执行AsyncTask时都会创建一个新的适配器实例。
尝试下面的方法:
 Activity {

    ArrayList<MovieDetails> data;
    MainRecyclerViewAdapter adapter

    onCreate() {

        recyclerView = (RecyclerView)findViewById(R.id.main_recyclerview);

        recyclerView.setHasFixedSize(true);
        layoutManager = new GridLayoutManager(MainActivity.this,2);
        recyclerView.setLayoutManager(layoutManager);


        adapter = new MainRecyclerViewAdapter(MainActivity.this);
        recyclerView.setAdapter(adapter);
    }


    AsyncTask {
        /.....

        onPostExecute{

            // Download completed
            adapter.notifyDataSetChanged()
        }
    }

    MainRecyclerViewAdapter {

        //...
        ViewHolder {
            //...
        }
    }
}

我按照建议进行了更改,但没有用。当按评分排序时,以前的图像并没有清除。 - potter13

1

以下是你需要做的全部内容,

看我的示例即可。

这些是全局变量

    private List<Movie> movieList = new ArrayList<>();
    private RecyclerView recyclerView;
    private MoviesAdapter mAdapter; 

让我们去到onCreate

1.初始化回收视图

recyclerView = (RecyclerView) findViewById(R.id.recycler_view);

2.创建我的List的新实例

mAdapter = new MoviesAdapter(movieList);

3.设置我的回收视图。
        RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
        recyclerView.setLayoutManager(mLayoutManager);
        recyclerView.setItemAnimator(new DefaultItemAnimator());

4.将适配器设置到我的recycleView

recyclerView.setAdapter(mAdapter);

5. 调用方法加载数据。

setMyData();

让我们来到 setMyData 方法

1.从某个地方获取数据.. 可能是服务器

2.将它们添加到我的列表中

movieList.add(movie); // i have not added the lines where i add data to my movie list think it has 10 elements now 

3. 在添加完所有数据后,我执行了

mAdapter.notifyDataSetChanged();

每次更改时,我都可以调用setMyData();来设置所需的数据。
这个逻辑怎么样?
如果您需要Adapter类,请告诉我。
提示:您的异步任务应该执行setMyData()方法的操作。现在看看。好的,您需要第二次更新数据!浏览您的异步任务, layoutManager = new GridLayoutManager(MainActivity.this,2); 啊哈,当您调用异步任务时,您反复调用此行……人们建议您在onPostExecute中进行notifyDataSet(它有效,但是……)请思考,新对象也会创建tan ta tan!!调用您的asyncTask 10次..现在比较一下我创建对象的位置,是否不断增加..现在您会看到出了什么问题! p.s我的LayoutManager实例不在我的setMyData()方法中,而是在我的onCreate中。

感谢所有的帮助,我已经更新了解决方案。唯一的问题是我还没有创建 setMyData() 方法,我按照建议将代码移动到了 onCreate 中,并在 onPostExecute() 中调用 notifydatasetchanged(),但主要问题是 ArrayList 每次都被填充数据。所以在解析和添加数据之前,我会调用 list.clear() - potter13

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接