在RecyclerView.Adapter中从Internet加载数据

时间:2017-08-08 14:15:21

标签: android android-recyclerview recycler-adapter

我从互联网上获取电影数据,如名字,海报等,但是对于电影的类型,我需要从网上再次获取它。所以这是我解决这个问题的方法。

public class MoviesViewAllAdapter extends RecyclerView.Adapter<MoviesViewAllAdapter.MoviesViewHolder> {

private Context mContext;
private List<MovieBrief> mMovies;

public MoviesViewAllAdapter(Context context, List<MovieBrief> movies) {
    mContext = context;
    mMovies = movies;
}

@Override
public MoviesViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    return new MoviesViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item_movie_large,parent,false));
}

@Override
public void onBindViewHolder(MoviesViewHolder holder, int position) {

    holder.movieGenreTextView.setText("");
    setGenres(holder, mMovies.get(position).getId());

}

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

public class MoviesViewHolder extends RecyclerView.ViewHolder {

    public TextView movieGenreTextView;

    public MoviesViewHolder(View itemView) {
        super(itemView);
        movieGenreTextView = (TextView) itemView.findViewById(R.id.text_view_genre_movie_card);
    }
}

private void setGenres(final MoviesViewHolder holder, Integer movieId) {
    ApiInterface apiService = ApiClient.getClient().create(ApiInterface.class);
    Call<Movie> call = apiService.getMovieDetails(movieId,mContext.getResources().getString(R.string.MOVIE_DB_API_KEY));
    call.enqueue(new Callback<Movie>() {
        @Override
        public void onResponse(Call<Movie> call, Response<Movie> response) {
            if(response.code() != 200) return;
            List<Genre> genresList = response.body().getGenres();
            String genres = "";
            for (int i=0;i<genresList.size();i++) {
                if(i == genresList.size()-1) {
                    genres = genres.concat(genresList.get(i).getGenreName());
                }
                else {
                    genres = genres.concat(genresList.get(i).getGenreName()+", ");
                }
            }
            holder.movieGenreTextView.setText(genres);
        }

        @Override
        public void onFailure(Call<Movie> call, Throwable t) {

        }
    });
}

}

但是这里的问题是,当执行fling并向上回收者视图显示的类型与电影无关时,正在加载的类型是随机的。 可能是因为我在onBindViewHolder上加载数据,当持有者从屏幕上消失时,它会加载到随机持有者中。是这样吗?

2 个答案:

答案 0 :(得分:1)

我相信您的问题是,每次在列表中创建视图时,您都会获得整个电影列表。我不确定服务器是如何返回该数据的,但我的猜测是每次调用服务器时都无法保证数据的顺序相同。你每次都会得到一个随机的顺序,但是试图提取它的固定位置,这就是为什么类型不相关的原因。

有问题的行是onBindViewHolder,每次创建一个视图时都会调用该行,并且此函数正在调用setGenres,它正在获取一个随机顺序的新电影列表。

你可以做两件事来解决这个问题:

  1. 首先搜索电影,找到它的索引,然后使用它来获取该类型。但这仍然是一个非常糟糕的设计,因为对于N部电影的列表,你正在呼叫服务器N次。
  2. 首先获取列表,将其作为ArrayList存储在适配器中。现在迭代它,而不必每次都调用服务器

    public class MoviesViewAllAdapter extends RecyclerView.Adapter<MoviesViewAllAdapter.MoviesViewHolder> {
    .
    .
    .
    List<Movie> list = new ArrayList<>;
    
    public void setList(List movies){
        //get data from server before creating the adapter. call this on your adapter and store the data here
        this.list = movies;
    }
    
    private void setGenres(final MoviesViewHolder holder, Integer movieId){
        //iterate the field list instead of calling the server
    }
    .
    .
    .
    }
    

答案 1 :(得分:1)

显示错误类型的问题是由RecyclerView执行的视图/持有者的回收引起的。当您滚动MoviesViewHolder的实例并重复使用视图时,当您触发加载电影详细信息时ViewHolder与MovieRef相关联,但是当获取电影详细信息的调用结束时,持有者现在被分配给不同的电影。

我认为最好的办法是加载电影详细信息并将其缓存在地图中,例如{API},当调用API结束时,您可以将Movie对象存储在那里。

HashMap<Integer, Movie> mMoviesDetails;

然后在您的适配器中,您可以将onBindViewHolder更改为如下所示:

  public void onResponse(Call<Movie> call, Response<Movie> response) {
           if(response.code() != 200) return;
      mMoviesDetails.put(movieId, response.body());
      notifyDataSetChanged();
  }

这只是基于您当前实现的示例代码,一般来说我不会在Adapter中加载这些数据,而是将这种类型的任务委托给一个专用的API类,它可以将数据存储在像Realm这样的数据库中,而不是使用地图。