使用 IMDB 改造 API

Using retrofit with IMDB API

我正在构建一个简单的 IMDB 应用程序,我几乎完成了一个小细节。 API(http://www.omdbapi.com/) 一次只提供 10 部电影,用户可以指定他们想要哪 "page" 部电影。我想检索所有条目。我的代码看起来像这样:

//This populates the list
private void populateList(String title) {
    myAPI.getSearchResults(title, page).enqueue(new Callback<Movies>() {
        @Override
        public void onResponse(Call<Movies> call, Response<Movies> response) {
            movies = response.body().getSearch();
            recyclerView.setAdapter(new ItemAdapter(movies));
            recyclerView.addOnItemTouchListener(
                new ItemClickableListener(getActivity(), new   ItemClickableListener.OnItemClickListener() {
                    @Override
                    public void onItemClick(View view, int position) {
                        String id = movies.get(position).getImdbID();
                        showDetails(id, view);
                    }
                }));
        }

        @Override
        public void onFailure(Call<Movies> call, Throwable t) {
            Log.d(TAG, "Error: " + t);
        }
    });
}

在我的界面中:

//For populating the list
@GET("?")
Call<Movies> getSearchResults(@Query("s") String title, @Query("page") int pages);

有一种方法可以知道总共有多少条目,但查询必须至少 运行 一次才能检索该信息。我尝试用 "do...while" 循环修复它,并将每批连续的电影添加到列表中,然后才填充 RecyclerView 但它不起作用(它会离开循环而不显示任何东西)。也许我忽略了一些东西,这是正确的答案,但即便如此 - 难道没有更优雅的方法吗?

我认为您需要 EndlessRecyclerView 来十页十页地检索。使用以下代码:

mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
mAdapter = new MyAdapter(getActivity(), this);
scrollListener = new EndlessRecyclerOnScrollListener((LinearLayoutManager) mRecyclerView.getLayoutManager()) {
    @Override
    public void onLoadMore(int page) {
        callWebservice(page);
    }
};
mRecyclerView.addOnScrollListener(scrollListener);
mRecyclerView.setAdapter(mAdapter);

完成 callWebservice 后,将项目添加到您的列表中:

        @Override
        public void onResponse(Call<List<ShortVideoModel>> call, Response<List<ShortVideoModel>> response) {
             mAdapter.addItems(response.body());
        }

我最终检查了 EndlessRecyclerView,它几乎完美地工作,但我 运行 遇到了一些问题,所以我在此处发布代码。它一直在堆叠监听器和适配器,所以我交换了它们。每次插入数据时它也会不断向上滚动,所以我强迫它停留但它有点紧张。

public class SearchFragment extends Fragment {
final String TAG = "LOG.SearchFragment";
final String baseUrl = "http://www.omdbapi.com/";
Button searchButton;
EditText searchField;
RecyclerView recyclerView;
LinearLayoutManager llm;
String title = "";
int page = 1;
List<Search> movies;
Gson gson;
Retrofit retrofit;
MyAPI myAPI;
ItemClickableListener listener;
EndlessRecyclerOnScrollListener scrollListener;
int firstItem;
float topOffset;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    Log.d(TAG, "Starting SearchFragment...");
    return inflater.inflate(R.layout.search_fragment, container, false);
}

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);

    //Preparing RecyclerView
    recyclerView = (RecyclerView) getActivity().findViewById(R.id.recycler_view);
    llm = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false);
    recyclerView.setLayoutManager(llm);
    setOnScrollManager();

    //List for the movies
    movies = new ArrayList<>();

    //UI
    searchField = (EditText) getActivity().findViewById(R.id.search_field);
    searchButton = (Button) getActivity().findViewById(R.id.search_button);
    searchButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            if (!searchField.getText().toString().equals("")) {
                gson = new GsonBuilder().create();
                retrofit = new Retrofit.Builder()
                        .baseUrl(baseUrl)
                        .addConverterFactory(GsonConverterFactory.create(gson))
                        .build();
                myAPI = retrofit.create(MyAPI.class);
                title = searchField.getText().toString();
                movies.clear();
                page=1;
                setOnScrollManager();
                fetchMovies(title, page);
            }
        }
    });
}

private void setOnScrollManager() {
    if (scrollListener!=null) recyclerView.removeOnScrollListener(scrollListener);
    scrollListener = new EndlessRecyclerOnScrollListener((LinearLayoutManager) recyclerView.getLayoutManager()) {
        //This happens when user scrolls to bottom
        @Override
        public void onLoadMore(int newPage) {
            Log.d(TAG, "OnLoadMore "+newPage);
            //Preparing the scroll
            firstItem = llm.findFirstVisibleItemPosition();
            View firstItemView = llm.findViewByPosition(firstItem);
            topOffset = firstItemView.getTop();

            //Getting new page
            page=newPage;
            fetchMovies(title, page);
        }
    };
    recyclerView.addOnScrollListener(scrollListener);
}

//This populates the list
private void fetchMovies(String title, int page) {
    Log.d(TAG, "Getting "+title+", page "+page);
    myAPI.getSearchResults(title, page).enqueue(new Callback<Movies>() {
        @Override
        public void onResponse(Call<Movies> call, Response<Movies> response) {
            if (movies.size()==0) Toast.makeText(getActivity(), "No movies found", Toast.LENGTH_SHORT).show();
            movies.addAll(response.body().getSearch());
            //We swap the adatper's content when user scrolls down and loads more data
            recyclerView.setRecycledViewPool(new RecyclerView.RecycledViewPool());
            recyclerView.swapAdapter(new ItemAdapter(movies), true);

            //Scrolling
            Log.d(TAG, "Scrolling to "+firstItem);
            llm.scrollToPositionWithOffset(firstItem, (int) topOffset);

            //We avoid stacking up listeners
            if (listener!=null) recyclerView.removeOnItemTouchListener(listener);
            listener = new ItemClickableListener(getActivity(), new   ItemClickableListener.OnItemClickListener() {
                @Override
                public void onItemClick(View view, int position) {
                    String id = movies.get(position).getImdbID();
                    showDetails(id, view);
                }
            });
            recyclerView.addOnItemTouchListener(listener);
        }

        @Override
        public void onFailure(Call<Movies> call, Throwable t) {
            Log.d(TAG, "Error: " + t);
        }
    });
}

//This gets the movie details
private void showDetails(String id, final View view){
    myAPI.getDetails(id).enqueue(new Callback<MovieDetails>() {
        @Override
        public void onResponse(Call<MovieDetails> call, Response<MovieDetails> response) {
            showPopup(response.body(), view);
        }

        @Override
        public void onFailure(Call<MovieDetails> call, Throwable t) {
            Log.d(TAG, "Error: " + t);
        }
    });
}

//This displays the movie details
private void showPopup(MovieDetails details, View anchorView) {

    View popupView = getActivity().getLayoutInflater().inflate(R.layout.popup_layout, null);

    PopupWindow popupWindow = new PopupWindow(popupView,
            RecyclerView.LayoutParams.WRAP_CONTENT, RecyclerView.LayoutParams.WRAP_CONTENT);

    TextView title = (TextView) popupView.findViewById(R.id.movie_detail_title);
    TextView year = (TextView) popupView.findViewById(R.id.movie_detail_year);
    TextView rating = (TextView) popupView.findViewById(R.id.movie_detail_rating);
    TextView director = (TextView) popupView.findViewById(R.id.movie_detail_director);
    TextView stars = (TextView) popupView.findViewById(R.id.movie_detail_stars);
    TextView desc = (TextView) popupView.findViewById(R.id.movie_detail_desc);

    title.setText(details.getTitle());
    title.setTextColor(Color.parseColor("#ffffff"));
    year.setText(details.getYear());
    year.setTextColor(Color.parseColor("#ffffff"));
    rating.setText(details.getImdbRating()+"/10");
    rating.setTextColor(Color.parseColor("#ffffff"));
    director.setText("Dir: "+details.getDirector());
    director.setTextColor(Color.parseColor("#ffffff"));
    stars.setText("Stars: "+details.getActors());
    stars.setTextColor(Color.parseColor("#ffffff"));
    desc.setText(details.getPlot());
    desc.setTextColor(Color.parseColor("#ffffff"));

    UrlValidator urlValidator = new UrlValidator();
    if (urlValidator.isValid(details.getPoster())) {
        ImageView poster = (ImageView) popupView.findViewById(R.id.movie_detail_poster);
        ImageLoader imageLoader = ImageLoader.getInstance();
        imageLoader.displayImage(details.getPoster(), poster);
    }

    // If the PopupWindow should be focusable
    popupWindow.setFocusable(true);

    // If you need the PopupWindow to dismiss when when touched outside
    popupWindow.setBackgroundDrawable(new ColorDrawable(Color.parseColor("#CC000000")));

    int location[] = new int[2];

    // Get the View's(the one that was clicked in the Fragment) location
    anchorView.getLocationOnScreen(location);

    // Using location, the PopupWindow will be displayed right under anchorView
    popupWindow.showAtLocation(anchorView, Gravity.NO_GRAVITY,
            location[0], location[1] + anchorView.getHeight());

}
}