Comment implémenter setOnScrollListener dans RecyclerView
Comment afficher la barre de progression en bas lorsque l'utilisateur atteint les éléments visibles dans une liste.
J'ai écrit un code dans lequel je reçois des données en utilisant le service web, maintenant je voudrais remplir des enregistrements partiels, car j'ai environ 630 enregistrements dans mon JSON.
Voici tout mon code que j'utilise pour obtenir des données de JSON et pour les remplir dans RecyclerView.
Voici à quoi ressemble mon JSON, mais le vrai JSON contient plus de 600 enregistrements:
http://walldroidhd.com/api.php
Quelqu'un peut-il me guider où je dois apporter des modifications à mon code ?
Je veux remplir plus d'enregistrements chaque fois que l'utilisateur fait défiler vers le bas en utilisant progressbar, je montre toujours tous les enregistrements.
RecyclerViewFragment.java:
public class RecyclerViewFragment extends Fragment {
RecyclerView mRecyclerView;
LinearLayoutManager mLayoutManager;
RecyclerView.Adapter mAdapter;
ArrayList<NatureItem> actorsList;
private int previousTotal = 0;
private boolean loading = true;
private int visibleThreshold = 5;
int firstVisibleItem, visibleItemCount, totalItemCount;
public static RecyclerViewFragment newInstance() {
return new RecyclerViewFragment();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_recyclerview_advance, container, false);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
actorsList = new ArrayList<NatureItem>();
new JSONAsyncTask().execute("my JSON url");
mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
mRecyclerView.setHasFixedSize(true);
mLayoutManager = new GridLayoutManager(getActivity(), 2);
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
visibleItemCount = mRecyclerView.getChildCount();
totalItemCount = mLayoutManager.getItemCount();
firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition();
if (loading) {
if (totalItemCount > previousTotal) {
loading = false;
previousTotal = totalItemCount;
}
}
if (!loading && (totalItemCount - visibleItemCount)
<= (firstVisibleItem + visibleThreshold)) {
// End has been reached
Log.i("...", "end called");
// Do something
loading = true;
}
}
});
// mAdapter = new CardAdapter();
mAdapter = new RecyclerViewMaterialAdapter(new CardAdapter(getActivity(), actorsList), 2);
mRecyclerView.setAdapter(mAdapter);
MaterialViewPagerHelper.registerRecyclerView(getActivity(), mRecyclerView, null);
mRecyclerView.addOnItemTouchListener(
new RecyclerItemClickListener(getActivity(), new RecyclerItemClickListener.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
Toast.makeText(getActivity(), String.valueOf(position), Toast.LENGTH_LONG).show();
}
})
);
}
class JSONAsyncTask extends AsyncTask<String, Void, Boolean> {
ProgressDialog dialog;
@Override
protected void onPreExecute() {
super.onPreExecute();
dialog = new ProgressDialog(getActivity());
dialog.setMessage("Loading, please wait");
dialog.setTitle("Connecting server");
dialog.show();
dialog.setCancelable(false);
}
@Override
protected Boolean doInBackground(String... urls) {
try {
//------------------>>
HttpGet httppost = new HttpGet(urls[0]);
HttpClient httpclient = new DefaultHttpClient();
HttpResponse response = httpclient.execute(httppost);
// StatusLine stat = response.getStatusLine();
int status = response.getStatusLine().getStatusCode();
if (status == 200) {
HttpEntity entity = response.getEntity();
String data = null;
try {
data = EntityUtils.toString(entity);
} catch (IOException e) {
e.printStackTrace();
}
JSONObject jsono = null;
try {
jsono = new JSONObject(data);
} catch (JSONException e) {
e.printStackTrace();
}
JSONArray jarray = jsono.getJSONArray("wallpapers");
for (int i = 0; i < jarray.length(); i++) {
JSONObject object = jarray.getJSONObject(i);
NatureItem actor = new NatureItem();
actor.setName(object.getString("id"));
actor.setThumbnail(object.getString("thumb_url"));
actorsList.add(actor);
}
return true;
}
//------------------>>
} catch (ParseException e1) {
e1.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return false;
}
protected void onPostExecute(Boolean result) {
dialog.cancel();
mAdapter.notifyDataSetChanged();
if (result == false) {
Toast.makeText(getActivity(), "Unable to fetch data from server", Toast.LENGTH_LONG).show();
}
else {
}
}
}
}
CardAdapter.java:
public class CardAdapter extends RecyclerView.Adapter<CardAdapter.ViewHolder> {
private ArrayList<NatureItem> mItems;
private Context mContext;
public CardAdapter(Context context, ArrayList<NatureItem> feedItemList) {
this.mItems = feedItemList;
this.mContext = context;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View v = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.recycler_view_card_item, viewGroup, false);
ViewHolder viewHolder = new ViewHolder(v);
return viewHolder;
}
@Override
public void onBindViewHolder(ViewHolder viewHolder, int i) {
NatureItem nature = mItems.get(i);
viewHolder.tvNature.setText(nature.getName());
Picasso.with(mContext).load(nature.getThumbnail())
.error(R.mipmap.ic_launcher)
.placeholder(R.mipmap.ic_launcher)
.into(viewHolder.imgThumbnail);
}
@Override
public int getItemCount() {
return mItems.size();
}
class ViewHolder extends RecyclerView.ViewHolder{
public ImageView imgThumbnail;
public TextView tvNature;
public ViewHolder(View itemView) {
super(itemView);
imgThumbnail = (ImageView)itemView.findViewById(R.id.img_thumbnail);
tvNature = (TextView)itemView.findViewById(R.id.tv_nature);
}
}
}
Voici comment J'ai implémenté Endless aussi, en utilisant this :
mRecyclerView.setOnScrollListener(new EndlessRecyclerOnScrollListener(linearLayoutManager) {
@Override
public void onLoadMore(int current_page) {
// do something...
}
});
Note: je préférerais personnellement la fonctionnalité RecyclerView onScroll pour faire mon travail.
5 réponses
Classe D'activité avec recylcerview dans le fichier de mise en page xml
public class WallpaperActivity extends AppCompatActivity implements OnTaskCompleted {
private static final String TAG = "WallpaperActivity";
private Toolbar toolbar;
private RecyclerView mRecyclerView;
private WallPaperDataAdapter mAdapter;
private LinearLayoutManager mLayoutManager;
// to keep track which pages loaded and next pages to load
public static int pageNumber;
private List<WallPaper> wallpaperImagesList;
protected Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.wallpaper_main);
toolbar = (Toolbar) findViewById(R.id.toolbar);
mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
pageNumber = 1;
wallpaperImagesList = new ArrayList<WallPaper>();
handler = new Handler();
if (toolbar != null) {
setSupportActionBar(toolbar);
getSupportActionBar().setTitle("WallPapers");
}
// use this setting to improve performance if you know that changes
// in content do not change the layout size of the RecyclerView
mRecyclerView.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(this);
// use a linear layout manager
mRecyclerView.setLayoutManager(mLayoutManager);
// create an Object for Adapter
mAdapter = new WallPaperDataAdapter(wallpaperImagesList, mRecyclerView);
// set the adapter object to the Recyclerview
mRecyclerView.setAdapter(mAdapter);
getWebServiceData();
mAdapter.setOnLoadMoreListener(new OnLoadMoreListener() {
@Override
public void onLoadMore() {
//add null , so the adapter will check view_type and show progress bar at bottom
wallpaperImagesList.add(null);
mAdapter.notifyItemInserted(wallpaperImagesList.size() - 1);
++pageNumber;
getWebServiceData();
}
});
}
public void getWebServiceData() {
BackGroundTask backGroundTask = new BackGroundTask(this, this, pageNumber);
backGroundTask.execute();
}
@Override
public void onTaskCompleted(String response) {
parsejosnData(response);
}
public void parsejosnData(String response) {
try {
JSONObject jsonObject = new JSONObject(response);
// String json = jsonObject.toString();
JSONArray jsonArray = jsonObject.getJSONArray("wallpapers");
if (jsonArray != null) {
// looping through All albums
if (pageNumber > 1) {
wallpaperImagesList.remove(wallpaperImagesList.size() - 1);
mAdapter.notifyItemRemoved(wallpaperImagesList.size());
}
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject c = jsonArray.getJSONObject(i);
// Storing each json item values in variable
String id = c.getString("id");
String orig_url = c.getString("orig_url");
String thumb_url = c.getString("thumb_url");
String downloads = c.getString("downloads");
String fav = c.getString("fav");
// Creating object for each product
WallPaper singleWall = new WallPaper(id, orig_url, thumb_url, downloads, fav);
// adding HashList to ArrayList
wallpaperImagesList.add(singleWall);
handler.post(new Runnable() {
@Override
public void run() {
mAdapter.notifyItemInserted(wallpaperImagesList.size());
}
});
}
mAdapter.setLoaded();
} else {
Log.d("Wallpapers: ", "null");
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
Classe D'Adaptateur
public class WallPaperDataAdapter extends RecyclerView.Adapter {
private final int VIEW_ITEM = 1;
private final int VIEW_PROG = 0;
private List<WallPaper> imagesList;
// The minimum amount of items to have below your current scroll position
// before loading more.
private int visibleThreshold = 5;
private int lastVisibleItem, totalItemCount;
private boolean loading;
private OnLoadMoreListener onLoadMoreListener;
public WallPaperDataAdapter(List<WallPaper> imagesList1, RecyclerView recyclerView) {
imagesList = imagesList1;
if (recyclerView.getLayoutManager() instanceof LinearLayoutManager) {
final LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView
.getLayoutManager();
recyclerView
.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView,
int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
totalItemCount = linearLayoutManager.getItemCount();
lastVisibleItem = linearLayoutManager
.findLastVisibleItemPosition();
if (!loading
&& totalItemCount <= (lastVisibleItem + visibleThreshold)) {
// End has been reached
// Do something
if (onLoadMoreListener != null) {
onLoadMoreListener.onLoadMore();
}
loading = true;
}
}
});
}
}
@Override
public int getItemViewType(int position) {
return imagesList.get(position) != null ? VIEW_ITEM : VIEW_PROG;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
RecyclerView.ViewHolder vh;
if (viewType == VIEW_ITEM) {
View v = LayoutInflater.from(parent.getContext()).inflate(
R.layout.wallpaper_row, parent, false);
vh = new WallPaperViewHolder(v);
} else {
View v = LayoutInflater.from(parent.getContext()).inflate(
R.layout.progress_item, parent, false);
vh = new ProgressViewHolder(v);
}
return vh;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof WallPaperViewHolder) {
WallPaper singleWallPaper = (WallPaper) imagesList.get(position);
Glide.with(((WallPaperViewHolder) holder).thumbIcon.getContext())
.load(singleWallPaper.getThumbUrl())
.centerCrop()
.placeholder(R.drawable.bg)
.crossFade()
.into(((WallPaperViewHolder) holder).thumbIcon);
} else {
((ProgressViewHolder) holder).progressBar.setIndeterminate(true);
}
}
public void setLoaded() {
loading = false;
}
@Override
public int getItemCount() {
return imagesList.size();
}
public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
this.onLoadMoreListener = onLoadMoreListener;
}
//
public static class WallPaperViewHolder extends RecyclerView.ViewHolder {
public ImageView thumbIcon;
public WallPaperViewHolder(View v) {
super(v);
thumbIcon = (ImageView) v.findViewById(R.id.thumbIcon);
}
}
public static class ProgressViewHolder extends RecyclerView.ViewHolder {
public ProgressBar progressBar;
public ProgressViewHolder(View v) {
super(v);
progressBar = (ProgressBar) v.findViewById(R.id.progressBar1);
}
}
}
Wallpaper_row.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/thumbIcon"
android:layout_width="160dp"
android:layout_height="160dp"
android:layout_centerInParent="true"
android:layout_margin="2dp"
android:gravity="center" />
</RelativeLayout>
Progress_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<ProgressBar
android:id="@+id/progressBar1"
android:layout_width="wrap_content"
android:layout_gravity="center"
android:layout_height="wrap_content" />
</LinearLayout>
Tâche D'Arrière-Plan Séparée.java
public class BackGroundTask extends AsyncTask<Object, Void, String> {
private ProgressDialog pDialog;
public OnTaskCompleted listener = null;//Call back interface
Context context;
int pageNumber;
public BackGroundTask(Context context1, OnTaskCompleted listener1, int pageNumber) {
context = context1;
listener = listener1; //Assigning call back interface through constructor
this.pageNumber = pageNumber;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected String doInBackground(Object... params) {
//My Background tasks are written here
synchronized (this) {
String url = Const.URL_WALLPAPERS_HD + pageNumber;
String jsonStr = ServiceHandler.makeServiceCall(url, ServiceHandler.GET);
Log.i("Url: ", "> " + url);
Log.i("Response: ", "> " + jsonStr);
return jsonStr;
}
}
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
listener.onTaskCompleted(result);
}
}
ServiceHanlder.java
public class ServiceHandler {
static String response = null;
public final static int GET = 1;
public final static int POST = 2;
public ServiceHandler() {
}
/**
* Making service call
*
* @url - url to make request
* @method - http request method
*/
public static String makeServiceCall(String url, int method) {
return makeServiceCall(url, method, null);
}
/**
* Making service call
*
* @url - url to make request
* @method - http request method
* @params - http request params
*/
public static String makeServiceCall(String url, int method,
List<NameValuePair> params) {
try {
// http client
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpEntity httpEntity = null;
HttpResponse httpResponse = null;
// Checking http request method type
if (method == POST) {
HttpPost httpPost = new HttpPost(url);
// adding post params
if (params != null) {
httpPost.setEntity(new UrlEncodedFormEntity(params));
}
Log.e("Selltis Request URL", url);
httpResponse = httpClient.execute(httpPost);
} else if (method == GET) {
// appending params to url
if (params != null) {
String paramString = URLEncodedUtils
.format(params, "utf-8");
url += paramString;
Log.i("Request URL", url);
}
HttpGet httpGet = new HttpGet(url);
httpResponse = httpClient.execute(httpGet);
}
httpEntity = httpResponse.getEntity();
response = EntityUtils.toString(httpEntity);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return "Fail";
} catch (ClientProtocolException e) {
e.printStackTrace();
return "Fail";
} catch (IOException e) {
e.printStackTrace();
return "Fail";
}
return response;
}
}
Interface pour charger plus
public interface OnLoadMoreListener {
void onLoadMore();
}
Interface pour connaître les données de service web chargées depuis asynctask
public interface OnTaskCompleted{
void onTaskCompleted(String response);
}
Faites-moi savoir si cela fonctionne ou si vous rencontrez des problèmes. Mieux vaut utiliser les bibliothèques Volley ou okHttp pour Réseau.
Pour ImageLoading j'ai utilisé Glide Bibliothèque.
C'est ainsi que je détecte si RecyclerView
doit être actualisé par OnScrollListener
, jetez-y un oeil:
recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
int ydy = 0;
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int offset = dy - ydy;
ydy = dy;
boolean shouldRefresh = (linearLayoutManager.findFirstCompletelyVisibleItemPosition() == 0)
&& (recyclerView.getScrollState() == RecyclerView.SCROLL_STATE_DRAGGING) && offset > 30;
if (shouldRefresh) {
//swipeRefreshLayout.setRefreshing(true);
//Refresh to load data here.
return;
}
boolean shouldPullUpRefresh = linearLayoutManager.findLastCompletelyVisibleItemPosition() == linearLayoutManager.getChildCount() - 1
&& recyclerView.getScrollState() == RecyclerView.SCROLL_STATE_DRAGGING && offset < -30;
if (shouldPullUpRefresh) {
//swipeRefreshLayout.setRefreshing(true);
//refresh to load data here.
return;
}
swipeRefreshLayout.setRefreshing(false);
}
});
J'espère que vous serez inspiré. Bonne chance~
un Autre exemple. Définissez la barre de progression en bas et modifiez sa visibilité en fonction du défilement / chargement et de vos enregistrements. REMARQUE: Vous devez appeler notifyDataSetChanged (); méthode pour ajouter/actualiser des données à l'adaptateur
recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int total = linearLayoutManager.getItemCount();
int firstVisibleItemCount = linearLayoutManager.findFirstVisibleItemPosition();
int lastVisibleItemCount = linearLayoutManager.findLastVisibleItemPosition();
//to avoid multiple calls to loadMore() method
//maintain a boolean value (isLoading). if loadMore() task started set to true and completes set to false
if (!isLoading) {
if (total > 0)
if ((total - 1) == lastVisibleItemCount){
loadMore();//your HTTP stuff goes in this method
loadingProgress.setVisibility(View.VISIBLE);
}else
loadingProgress.setVisibility(View.GONE);
}
}
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
});
L'idée d'implémenter load-more est:
Récupère tous les enregistrements dans une collection. Prenez une autre collection qui remplira les enregistrements partiels disons 10 enregistrements. Maintenant, lorsque vous atteignez en bas, remplissez les 10 enregistrements suivants dans la collection et notifiez la liste.
Donc, votre code sera quelque chose comme ceci:
ArrayList<NatureItem> tempList; // which holds partial records
tempList = getList(limit, 10);
mAdapter = new RecyclerViewMaterialAdapter(new CardAdapter(getActivity(), tempList), 2);
mRecyclerView.setAdapter(mAdapter);
Maintenant, code pour charger les enregistrements suivants:
limit += 10;
tempList.addAll(getList(limit, 10));
mAdapter.notifyDataSetChanged();
Pour montrer qu'il Loader / progress, mieux vaut utiliser load-plus de bibliothèque
Dans la méthode onScroll
if (!loading && (totalItemCount - visibleItemCount)
<= (firstVisibleItem + visibleThreshold)) {
// End has been reached
Log.i("...", "end called");
// Do something
new JSONAsyncTask().execute("http://walldroidhd.com/api.php");
loading = true;
}