2017-08-08 11 views
0

名前、ポスターなどのようにインターネットから映画データを取得していますが、映画のジャンルについてはネットから再度取得する必要があります。だから私はこの問題の解決策です。RecyclerView.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) { 

     } 
    }); 
} 

} 

しかし、ここで問題が浮気を実行し、表示されているジャンルは、映画に関連していないrecyclerviewを上がっていく時に、ロードされているジャンルがランダムであるということです。 onBindViewHolderでデータをロードしていて、ホルダーがスクリーンから消えるとランダムホルダーにロードされている可能性があります。そうですか?

+0

ここでは電話をかけてはいけません。呼び出された回数を制御することはできません。一度にいくつかのリクエストをすることができない限り、ムービーリストを1つずつ取得した後に行います。 –

+0

また、コードの代わりに 'if(!response.isSuccessful())return;'を使いたいとします。 –

+0

@MatiasOloccoそのコードを200でチェックしました。 はresponse.isSuccessful()と同じですか?この方法については知りませんでした。 –

答えて

1

リストにビューを作成するたびに、ムービーリスト全体が表示されてしまうことが問題だと思います。サーバーがそのデータを返す方法はわかりませんが、サーバーを呼び出すたびにデータが同じ順序であるという保証はありません。毎回無作為に注文していますが、その固定位置を抽出しようとしているので、ジャンルは関連していません。

問題のある行は、ビューが作成されるたびに呼び出されるonBindViewHolderです。この関数は、ランダムな順序で新しいムービーリストを取得しているsetGenresを呼び出しています。 、最初の映画のための

  1. 検索インデックスを検索し、ジャンルを取得するためにそれを使用:

    あなたはこの問題を解決するために2つのことを行うことができます。しかし、これはまだ非常に悪いデザインです。なぜなら、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 
    } 
    . 
    . 
    . 
    } 
    
+0

同じリストからジャンルを取得してみませんか?そのリストがジャンルを持っていない場合は、それに似た別のフィールドを追加して、毎回ジャンルを取得する必要はありません – SoroushA

+0

大丈夫ですので、私は2つのリストをアダプタに渡すべきです。 MovieBriefの1つと映画のためのその他。それは私が推測してうまくいくでしょう。ありがとう! :) –

+0

正確に。それらのリストは同じ順序でなければなりません。理想的には、すべてのデータを持つリストが1つありますが、もちろん設計制約に依存しています – SoroushA

1

を反復表示されている間違ったジャンルに問題がRecyclerViewにより、予備成形ビュー/ホルダーのリサイクルによって引き起こされます。 MoviesViewHolderのインスタンスをスクロールしてビューを再利用するので、ムービーの詳細の読み込みをトリガするときにViewHolderがMovieRefに関連付けられますが、ムービーの詳細を取得する呼び出しが終了するまでに、ホルダが別のムービーに割り当てられます。

私の意見では、ムービーの詳細を読み込んで、マップ内にキャッシュすることをお勧めします。たとえば、HashMap<Integer, Movie> mMoviesDetails;です.APIの呼び出しによってMovieオブジェクトがそこに格納されます。

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

次に、あなたのアダプタであなたは以下のようなものにonBindViewHolderを変更することができます。

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

    holder.movieGenreTextView.setText(""); 
    Movie movie = mMoviesDetails.get(mMovies.get(position).getId()); 
    if(movie != null){ 
     /// set data to holder 
    }else { 
     //load data from network 
     loadGenres(mMovies.get(position).getId()); 
    } 


} 

これは、一般的に、私は内部でこのデータをロードしないだろうに言えば、あなたの現在の実装に基づいてちょうどサンプルコードですこのタイプのタスクは、マップを使用する代わりにRealmなどのデータベースにデータを格納できる専用のAPIクラスに委譲します。

関連する問題