我目前正在开发一个项目,其中我们必须开发一个从电影数据库 API ( https://www.themoviedb.org/ ) 检索电影的 Android 应用程序。该应用程序的功能之一是从 API 检索热门电影并将其显示在 RecyclerView 中,如下所示:
从此 URL 检索电影:https://api.themoviedb.org/3/movie/popular?api_key=4c422ac80f2c83f42b8f905d4303959d&language=en&page=1
如您所见,有 500 页。在 RecyclerView 中显示数据之前加载 Asynctask 中的所有 500 个页面并不是一个解决方案,因为加载所有内容需要很长时间。相反,我希望 RecyclerView 能够检测何时到达列表末尾,然后从 API 加载下一页。我不知道如何解决这个问题。
我非常接近一个解决方案,即向 RecyclerView 添加 OnScrollListener 并在 RecyclerView 无法垂直滚动 2 个项目时检索下一页,但我还没有得到正确的逻辑。使用我当前的代码,一旦接近第一页的末尾,它将立即加载其他 499 页,这使得新加载的电影无法单击,因为任务尚未完成。我需要让它工作,以便在加载第二个页面后,它再次检查 RecyclerView 是否无法垂直滚动 2 个项目。
在我的 UrlBuilder 类中构建 URL 的方法:
public static URL buildPopularMovieListUrl(int page) {
Log.d(TAG, "Method called: buildPopularMovieListUrl");
// Paths and parameters are appended to the base URL.
Uri builtUri = Uri.parse(BASE_URL_TMDB).buildUpon()
.appendPath(MOVIE_PATH)
.appendPath(POPULAR_PATH)
.appendQueryParameter(PARAM_API_KEY, API_KEY)
.appendQueryParameter(PARAM_LANGUAGE, LANGUAGE)
.appendQueryParameter(PARAM_PAGE, String.valueOf(page))
.build();
URL url = null;
try {
url = new URL(builtUri.toString());
} catch (MalformedURLException e) {
e.printStackTrace();
}
Log.d(TAG, "Built URL: " + url);
return url;
}
MainActivityFragment.java
package nl.avans.cinetopia.presentation;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import nl.avans.cinetopia.R;
import nl.avans.cinetopia.adapters.PopularMoviesRecyclerViewAdapter;
import nl.avans.cinetopia.data_access.UrlBuilder;
import nl.avans.cinetopia.data_access.get_requests.GenresGetRequest;
import nl.avans.cinetopia.data_access.get_requests.PopularMovieGetRequest;
import nl.avans.cinetopia.data_access.utilities.JsonUtils;
import nl.avans.cinetopia.domain.Movie;
public class MainActivityFragment extends Fragment implements PopularMoviesRecyclerViewAdapter.OnItemClickListener {
private static final String TAG = MainActivityFragment.class.getSimpleName();
// RecyclerView attributes
private RecyclerView mRecyclerView;
private PopularMoviesRecyclerViewAdapter mAdapter;
private RecyclerView.LayoutManager mLayoutManager;
private ArrayList<Movie> mMovies = new ArrayList<>();
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.activity_main_fragment, container, false);
retrieveLatestGenresFromApi();
retrievePopularMoviesFromApi(1);
// Obtain a handle to the object.
mRecyclerView = view.findViewById(R.id.activity_main_recyclerView);
// Use a linear layout manager.
mLayoutManager = new LinearLayoutManager(getActivity());
// Connect the RecyclerView to the layout manager.
mRecyclerView.setLayoutManager(mLayoutManager);
// Specify an adapter.
mAdapter = new PopularMoviesRecyclerViewAdapter(getActivity(), mMovies);
// Connect the RecyclerView to the adapter.
mRecyclerView.setAdapter(mAdapter);
// Set OnItemClickListener.
mAdapter.setOnItemClickListener(this);
/* Add a divider to the RecyclerView. */
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL);
dividerItemDecoration.setDrawable(getResources().getDrawable(R.drawable.rv_divider));
mRecyclerView.addItemDecoration(dividerItemDecoration);
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
for (int i = 2; i < 500; i++) {
if (!recyclerView.canScrollVertically(2)) {
retrievePopularMoviesFromApi(i);
mAdapter.notifyDataSetChanged();
}
}
}
});
return view;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
private void retrievePopularMoviesFromApi(int page) {
PopularMovieGetRequest task = new PopularMovieGetRequest(new PopularMovieApiListener());
task.execute(UrlBuilder.buildPopularMovieListUrl(page));
}
private void retrieveLatestGenresFromApi() {
GenresGetRequest task = new GenresGetRequest(new JsonUtils.GenresApiListener());
task.execute(UrlBuilder.buildGenreUrl());
}
/**
* Listener class for the PopularMovieGetRequest.
*/
class PopularMovieApiListener implements PopularMovieGetRequest.PopularMovieApiListener {
/**
* Fills our global ArrayList with the retrieved movies and notifies the adapter that the
* dataset has changed.
*
* @param movies The list of movies retrieved by our PopularMovieGetRequest.
*/
@Override
public void handleMovieResult(ArrayList<Movie> movies) {
Log.d(TAG, "Method called: handleMovieResult");
// Add all movies to our ArrayList and notify the adapter that the dataset has changed.
mMovies.addAll(movies);
mAdapter.notifyDataSetChanged();
}
}
@Override
public void onItemClick(int position) {
getActivity().getSupportFragmentManager().beginTransaction()
.replace(R.id.activity_main_frameLayout, new MovieDetailsActivity(mMovies.get(position).getId()))
.addToBackStack(null).commit();
}
}
最佳答案
为什么使用原始代码而不是 Retrofit 库从服务器获取数据? Retrofit 非常容易从服务器获取数据。只需使用对象进行 API 调用即可。您可以轻松地使用库进行分页。请参阅此处如何实现它 https://github.com/square/retrofit
关于java - 当到达 RecyclerView 底部时从 API 接收下一页电影?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60934614/