I want to code endless scrolling with Adapters & RecyclerViews and I'm fetching my items with Volley from JSON. Though I have read these guides (Codepath and github), I'm finding it difficult to apply it to my own scenario.
The url of my json has these format: https://example.com/json/items?page=1, https://example.com/json/items?page=2, https://example.com/json/items?page=3 etc.
Below are the codes I'm using:
MainActivity
public class MainActivity extends AppCompatActivity {
private final String TAG = "MainActivity";
private int number = 1;
//Creating a list of videos
private List<VideoItems> mVideoItemsList;
//Creating Views
private RecyclerView recyclerView;
private RecyclerView.Adapter adapter;
private RecyclerView.LayoutManager layoutManager;
private ProgressDialog mProgressDialog;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG, "onCreate called");
//Initializing Views
recyclerView = (RecyclerView) findViewById(R.id.video_recycler);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
// This method was copied from codepath (above url)
// Add the scroll listener
recyclerView.addOnScrollListener(new EndlessRecyclerViewScrollListener(layoutManager) {
#Override
public void onLoadMore(int page, int totalItemsCount) {
// Triggered only when new data needs to be appended to the list
// Add whatever code needed to append new items of the list
customLoadMoreDataFromApi(page);
}
});
//Initializing the videolist
mVideoItemsList = new ArrayList<>();
adapter = new VideoAdapter(mVideoItemsList, this);
recyclerView.setAdapter(adapter);
if (NetworkCheck.isAvailableAndConnected(this)) {
//Calling method to get data
getData();
} else {
//Codes for building Alert Dialog
alertDialogBuilder.setPositiveButton(R.string.alert_retry, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
if (!NetworkCheck.isAvailableAndConnected(mContext)) {
alertDialogBuilder.show();
} else {
getData();
}
}
});
alertDialogBuilder.setNegativeButton(R.string.alert_cancel, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
});
alertDialogBuilder.show();
}
}
public int getNumber() {
return number++;
}
public void setNumber(int number) {
this.number = number;
}
// This method was copied from codepath (above is url)
// Append more data into the adapter
// This method probably sends out a network request and appends new data items to your adapter;
public void customLoadMoreDataFromApi (int offset) {
// Send an API request to retrieve appropriate data using the offset value as a parameter.
// Deserialize API request and then construct new objects to append to the adapter.
// Add the new objects to the data source for adapter
mVideoItemsList.addAll(moreVideos);
// For efficiency purpose, notify the adapter of the only elements inserted that got changed
// curSize will be equal to the index of the first element inserted because the list is 0-indexed
int curSize = adapter.getItemCount();
adapter.notifyItemRangeChanged(curSize, mVideoItemsList.size() - 1);
}
//This method will get data from the web api
private void getData(){
Log.d(TAG, "getData called");
//Codes for Showing progress dialog
//Creating a json request
JsonArrayRequest jsonArrayRequest = new JsonArrayRequest(ConfigVideo.GET_URL + getNumber(),
new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
Log.d(TAG, "onResponse called");
//Dismissing the progress dialog
if (mProgressDialog != null) {
mProgressDialog.hide();
}
//calling method to parse json array
parseData(response);
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
});
//Creating request queue
RequestQueue requestQueue = Volley.newRequestQueue(this);
//Adding request to the queue
requestQueue.add(jsonArrayRequest);
}
//This method will parse json data
private void parseData(JSONArray array){
Log.d(TAG, "Parsing array");
for(int i = 0; i<array.length(); i++) {
VideoItems videoItem = new VideoItems();
JSONObject jsonObject = null;
try {
jsonObject = array.getJSONObject(i);
videoItem.setVideo_title(jsonObject.getString(ConfigVideo.TAG_VIDEO_TITLE));
videoItem.setVideo_body(jsonObject.getString(ConfigVideo.TAG_VIDEO_BODY));
} catch (JSONException w) {
w.printStackTrace();
}
mVideoItemsList.add(videoItem);
}
adapter.notifyItemRangeChanged(0, adapter.getItemCount());
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy called");
if (mProgressDialog != null){
mProgressDialog.dismiss();
Log.d(TAG, "mProgress dialog dismissed");
}
}
}
EndlessRecyclerViewScrollListener
The link to the code is EndlessRecyclerViewScrollListener.
In the one in my project, I have deleted all the codes that concerns GridLayoutManager and StaggeredGridLayoutManager since I won't be needing them.
VideoAdapter
public class VideoAdapter extends RecyclerView.Adapter<VideoAdapter.ViewHolder>{
private ImageLoader imageLoader;
private Context mContext;
//List of videos
private List<VideoItems> mVideoItems;
public VideoAdapter(List<VideoItems> videoItems, Context context) {
super();
// Getting all videos
this.mVideoItems = videoItems;
this.mContext = context;
}
#Override
public ViewHolder onCreateViewHolder (ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.video_summ, parent, false);
ViewHolder viewHolder = new ViewHolder(v);
return viewHolder;
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
VideoItems videoList = mVideoItems.get(position);
holder.videoTitle.setText(videoList.getVideo_title());
holder.videoBody.setText(videoList.getVideo_body());
}
#Override
public int getItemCount(){
//Return the number of items in the data set
return mVideoItems.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
public NetworkImageView videoImage;
public TextView videoTitle, videoBody;
public ViewHolder (View videoView) {
super(videoView);
videoTitle = (TextView) videoView.findViewById(R.id.video_title);
videoBody = (TextView) videoView.findViewById(R.id.video_body);
}
}
}
VideoItems
public class VideoItems {
private String video_title;
private String video_body;
public String getVideo_title() {
return video_title;
}
public void setVideo_title(String video_title) {
this.video_title = video_title;
}
public String getVideo_body() {
return video_body;
}
public void setVideo_body(String video_body) {
this.video_body = video_body;
}
}
I don't know if you could give me tips on how I could implement it to my RecyclerView. Thanks in advance.
You have to implement own listener to the adapter you are binding to recyclerview.
First create a interface like this,
public interface OnLoadMoreListener {
void onLoadMore();
}
In your recyclerview's adapter set this,
adapter.setOnLoadMoreListener(new OnLoadMoreListener() {
#Override
public void onLoadMore() {
//get your items here based on the pagination count
}
});
Your modified video adapter
public class VideoAdapter extends RecyclerView.Adapter<VideoAdapter.ViewHolder>{
private ImageLoader imageLoader;
private Context mContext;
//List of videos
private List<VideoItems> mVideoItems;
private int totalItemCount = 0,lastVisibleItem = 0,int visibleThreshold = 5;
private boolean loading;
private OnLoadMoreListener onLoadMoreListener;
public VideoAdapter(List<VideoItems> videoItems, Context context, RecyclerView recyclerview) {
super();
// Getting all videos
this.mVideoItems = videoItems;
this.mContext = context;
LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView
.getLayoutManager();
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView,
int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
totalItemCount = layoutManager.getItemCount();
lastVisibleItem = layoutManager.findLastVisibleItemPosition();
if (!loading
&& totalItemCount <= (lastVisibleItem + visibleThreshold)) {
if (onLoadMoreListener != null) {
onLoadMoreListener.onLoadMore();
}
loading = true;
}
}
});
}
public void setLoaded() {
loading = false;
}
public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
this.onLoadMoreListener = onLoadMoreListener;
}
#Override
public ViewHolder onCreateViewHolder (ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.video_summ, parent, false);
ViewHolder viewHolder = new ViewHolder(v);
return viewHolder;
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
VideoItems videoList = mVideoItems.get(position);
holder.videoTitle.setText(videoList.getVideo_title());
holder.videoBody.setText(videoList.getVideo_body());
}
#Override
public int getItemCount(){
//Return the number of items in the data set
return mVideoItems.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
public NetworkImageView videoImage;
public TextView videoTitle, videoBody;
public ViewHolder (View videoView) {
super(videoView);
videoTitle = (TextView) videoView.findViewById(R.id.video_title);
videoBody = (TextView) videoView.findViewById(R.id.video_body);
}
}
}
While initializing the adapter send recyclerview object
adapter = new VideoAdapter(mVideoItemsList, this, recyclerView );
Once you get your next set of items, increment your pagecount, notifyDataSetChanged() and call this
adapter.setLoaded();
Hope this will help you!
Related
I have a problem with Recyclerview load more items. First fetch data work perfectly. Added onScrolled method, added offset to request. try to display 10 records at a time (increase offset by 10 in each request and set limit on 10 by starting with offset 0 and increasing it each time when bottom is reached. Every request should return next 10 items). Please check my below code.
PersonUtils.java
public class PersonUtils {
private String personFirstName;
private String jobProfile;
public String getPersonFirstName() {
return personFirstName;
}
public void setPersonFirstName(String personFirstName) {
this.personFirstName = personFirstName;
}
public String getJobProfile() {
return jobProfile;
}
public void setJobProfile(String jobProfile) {
this.jobProfile = jobProfile;
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
RecyclerView recyclerView;
RecyclerView.Adapter mAdapter;
RecyclerView.LayoutManager layoutManager;
List<PersonUtils> personUtilsList;
RequestQueue rq;
String request_url = "https://next.json-generator.com/api/json/get/Vkg6e6Vcu";
String url = "https://.../api/json/offset=";
static int total = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rq = Volley.newRequestQueue(this);
recyclerView = (RecyclerView) findViewById(R.id.recycleViewContainer);
recyclerView.setHasFixedSize(true);
layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
personUtilsList = new ArrayList<>();
sendRequest(request_url);
// sendRequest(url, 0);
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(#NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
System.out.print("test");
if(dx>0){
}
if(dy>0){
total = total+10;
System.out.print("testing");
// sendRequest(url, total);
sendRequest(request_url);
}
}
});
}
// public void sendRequest(String request_url, int offsetIndex){
public void sendRequest(String request_url){
// request_url = request_url+offsetIndex;
JsonArrayRequest jsonArrayRequest = new JsonArrayRequest(Request.Method.GET, request_url, null, new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
for(int i = 0; i < response.length(); i++){
PersonUtils personUtils = new PersonUtils();
try {
JSONObject jsonObject = response.getJSONObject(i);
personUtils.setPersonFirstName(jsonObject.getString("name"));
personUtils.setJobProfile(jsonObject.getString("id"));
} catch (JSONException e) {
e.printStackTrace();
}
personUtilsList.add(personUtils);
}
mAdapter = new CustomRecyclerAdapter(MainActivity.this, personUtilsList);
recyclerView.setAdapter(mAdapter);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.i("Volley Error: ", error.getMessage());
}
});
rq.add(jsonArrayRequest);
}
}
CustomRecyclerAdapter.java
public class CustomRecyclerAdapter extends RecyclerView.Adapter<CustomRecyclerAdapter.ViewHolder> {
private Context context;
private List<PersonUtils> personUtils;
public CustomRecyclerAdapter(Context context, List personUtils) {
this.context = context;
this.personUtils = personUtils;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.single_list_item, parent, false);
ViewHolder viewHolder = new ViewHolder(v);
return viewHolder;
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.itemView.setTag(personUtils.get(position));
PersonUtils pu = personUtils.get(position);
holder.pName.setText(pu.getPersonFirstName());
holder.pJobProfile.setText(pu.getJobProfile());
}
#Override
public int getItemCount() {
return personUtils.size();
}
public class ViewHolder extends RecyclerView.ViewHolder{
public TextView pName;
public TextView pJobProfile;
public ViewHolder(View itemView) {
super(itemView);
pName = (TextView) itemView.findViewById(R.id.pNametxt);
pJobProfile = (TextView) itemView.findViewById(R.id.pJobProfiletxt);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
PersonUtils cpu = (PersonUtils) view.getTag();
Toast.makeText(view.getContext(), cpu.getPersonFirstName()+" "+" is "+ cpu.getJobProfile(), Toast.LENGTH_SHORT).show();
}
});
}
}
}
Required Output:
on loading call sendRequest(String request_url, int offsetIndex)
if user scroll then call sendRequest(String request_url, int offsetIndex)
Every request should return next 10 items
I am creating an app which can retrieve data from my own database. I'm using Retrofit to connect with my API. I'm creating a Bottom Navigation Activity so i could add some fragments to it. I'm using a Recycler View to show the list but it won't let me attach the adapter when i retrieve the data, it always shows this error in the log :
04-11 14:29:19.461 1912-1912/com.siscaproject.sisca E/RecyclerView: No adapter attached; skipping layout
Usually i use a single activity to create a list activity with a recycler view and it ran perfectly, but now that i use a fragment to do the same, it always gave me the error. Here is the fragment code :
public class SearchAssetFragment extends Fragment {
private static final String TAG = "SearchAssetFragment";
#BindView(R.id.search_view) SearchView searchView;
#BindView(R.id.swprefresh) SwipeRefreshLayout refresh;
#BindView(R.id.rv_list_asset) RecyclerView recyclerView;
#BindView(R.id.progress_bar) ProgressBar progressBar;
private MaterialDialog createDialog;
private AssetsAdapter adapter;
private ArrayList<Asset> listData = new ArrayList<>();
private UserService userService;
private OnFragmentInteractionListener mListener;
private AssetsAdapter.OnButtonClickListener listener = new AssetsAdapter.OnButtonClickListener() {
#Override
public void showDeleteDialog(int id) {
// TODO: Complete showDeleteDialog()
}
#Override
public void showEditDialog(Asset asset) {
// TODO: Complete showEditDialog()
}
};
public SearchAssetFragment() {
// Required empty public constructor
}
public static SearchAssetFragment newInstance() {
SearchAssetFragment fragment = new SearchAssetFragment();
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_search_asset, container, false);
ButterKnife.bind(this, view);
userService = APIProperties.getUserService();
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
// The method to retrieve the data from the DB and set the recycler view adapter
getAsset();
refresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
getAsset();
}
});
return view;
}
private void showCreateDialog(){
// creating a dialog
}
private void showProgressBar(){
// showing a progress bar
}
private void hideProgressBar(){
// hiding a progress bar
}
// The method to retrieve the data from the DB and set the recycler view adapter
private void getAsset(){
showProgressBar();
Call<ResponseIndex<Asset>> call = userService.indexFixed(Header.auth, Header.accept);
call.enqueue(new Callback<ResponseIndex<Asset>>() {
#Override
public void onResponse(Call<ResponseIndex<Asset>> call, Response<ResponseIndex<Asset>> response) {
if(response.isSuccessful()){
int total = response.body().getTotal();
Log.i(TAG, "onResponse: total " + total);
// Initializing the adapter and set it to the recycler view
ArrayList<Asset> rows = response.body().getRows();
adapter = new AssetsAdapter(rows, getActivity(), userService, listener);
recyclerView.setAdapter(adapter);
}
else{
Log.i(TAG, "onResponse: else");
}
hideProgressBar();
refresh.setRefreshing(false);
}
#Override
public void onFailure(Call<ResponseIndex<Asset>> call, Throwable t) {
Log.e(TAG, "onFailure: " + t.getMessage() );
}
});
}
}
I'm not sure what i did wrong nor how to fix it, any answer would be appreciated. Thank you.
--Edit-- AssetsAdapter class :
public class AssetsAdapter extends RecyclerView.Adapter<AssetsAdapter.ItemHolder> implements Filterable{
private static final String TAG = "AssetsAdapter";
private OnButtonClickListener listener;
private ArrayList<Asset> listData;
private ArrayList<Asset> listDataFull;
private Context activityContext;
private UserService userService;
private MaterialDialog dialog;
public AssetsAdapter(ArrayList<Asset> listData, Context activityContext, UserService userService, OnButtonClickListener listener) {
this.listData = listData;
listDataFull = new ArrayList<>(listData);
this.activityContext = activityContext;
this.userService = userService;
this.listener = listener;
}
public AssetsAdapter(Context activityContext) {
this.activityContext = activityContext;
}
public void setListData(ArrayList<Asset> listData) {
this.listData = listData;
notifyDataSetChanged();
}
#NonNull
#Override
public ItemHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.layout_item_assets, parent, false);
return new ItemHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ItemHolder holder, final int position) {
// holder.iv_asset.setImageDrawable(); On progress
holder.tv_name.setText(listData.get(position).getName());
holder.tv_manufacturer.setText(listData.get(position).getAsset_id());
holder.tv_quantity.setText(listData.get(position).getPurchase_cost());
//holder.tv_status.setText(listData.get(position).getModel_no()); // Still on Progress
holder.btn_delete.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
listener.showDeleteDialog(listData.get(position).getId());
}
});
holder.btn_edit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//Toast.makeText(activityContext, "Edit", Toast.LENGTH_SHORT).show();
listener.showEditDialog(listData.get(position));
}
});
}
#Override
public int getItemCount() {
if(listData.isEmpty()) return 0;
else return listData.size();
}
#Override
public Filter getFilter() {
return dataFilter;
}
private Filter dataFilter = new Filter() {
#Override
protected FilterResults performFiltering(CharSequence charSequence) {
ArrayList<Asset> filteredList = new ArrayList<>();
if(charSequence == null || charSequence.length() == 0){
filteredList.addAll(listDataFull);
}
else{
String filterPattern = charSequence.toString().toLowerCase().trim();
for(Asset item : listDataFull){
if(item.getName().toLowerCase().contains(filterPattern)){
filteredList.add(item);
}
}
}
FilterResults results = new FilterResults();
results.values = filteredList;
return results;
}
#Override
protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
listData.clear();
listData.addAll((List) filterResults.values);
notifyDataSetChanged();
}
};
public class ItemHolder extends RecyclerView.ViewHolder{
private ImageView iv_asset;
private TextView tv_name;
private TextView tv_manufacturer;
private TextView tv_quantity;
private TextView tv_status;
private ImageButton btn_edit;
private ImageButton btn_delete;
public ItemHolder(View itemView) {
super(itemView);
iv_asset = itemView.findViewById(R.id.iv_asset);
tv_name = itemView.findViewById(R.id.tv_name);
tv_manufacturer = itemView.findViewById(R.id.tv_manufacturer);
tv_quantity = itemView.findViewById(R.id.tv_quantity);
tv_status = itemView.findViewById(R.id.tv_status);
btn_edit = itemView.findViewById(R.id.btn_edit);
btn_delete = itemView.findViewById(R.id.btn_delete);
}
}
public interface OnButtonClickListener {
void showDeleteDialog(final int id);
void showEditDialog(Asset asset);
}
}
First, initialize the model class and the adapter and attach to recyclerview and than get the data after retrieving data put notifydatasetchanged() or notifydatainserted() method, I think it should work,
please initialize adapter in fragment oncreateview method and set adapter to recycleview
adapter = new AssetsAdapter(rows, getActivity(), userService, listener);
recyclerView.setAdapter(adapter);
recyclerView.setAdapter(adapter);
after get response from api you can update adapter. please refer
this question
From JSON file I get almost (200-300 image URL), I'm trying to load 10 images by 10 images to make the images load quickly instead of loading all the images (200-300) at once .but I have a problem RecyclerView Stop scrolling after the first 10 items, here's what I have tried:
InfiniteScrollListener.java
private void initInfiniteScrollListener(LinearLayoutManager mLayoutManager) {
infiniteScrollListener = new InfiniteScrollListener(mLayoutManager) {
#Override
public void onLoadMore(int page) {
pb.setVisibility(View.VISIBLE);
refreshContent();
}
};
recyclerView.addOnScrollListener(infiniteScrollListener);
}
Method to set adapter:
private void refreshContent() {
fetchRemoteData(new DataStatus() {
#Override
public void onSuccess(List<Post> posts) {
loading.setVisibility(View.GONE);
pb.setVisibility(View.GONE);
if (adapter == null) {
adapter = new MyAdapter(posts, getActivity(), new RecyclerViewClickListener() {
#Override
public void onClick(View view, Post post) {
}
});
recyclerView.setAdapter(adapter);
} else {
int position = adapter.getItemCount();
adapter.getItems().addAll(posts);
adapter.notifyItemRangeInserted(position, position + 10);
}
}
#Override
public void onError(Exception e) {
slowinternetconnection();
}
});
}
Fetching data:
private void fetchRemoteData(final DataStatus callback) {
StringRequest stringRequest = new StringRequest(Request.Method.GET,
URL_DATA,
new Response.Listener<String>() {
#Override
public void onResponse(String s) {
List<Post> listItems = new ArrayList<>();
List<Post> PagintationList = new ArrayList<>();
try {
JSONObject jsonObject = new JSONObject(s);
JSONArray array = jsonObject.getJSONArray("bgs");
for (int i = 0; i < array.length(); i++) {
JSONObject o = array.getJSONObject(i);
Post item = new Post(
o.optString("img"),
o.optString("name")
);
listItems.add(item);
}
for (int count = resetNumber; count < 10; count++) {
PagintationList.add(listItems.get(count));
}
resetNumber = PagintationList.size() + 1;
callback.onSuccess(PagintationList);
} catch (JSONException e) {
slowinternetconnection();
}
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
callback.onError(error);
}
});
RequestQueue requestQueue = Volley.newRequestQueue(getActivity().getApplicationContext());
stringRequest.setShouldCache(false);
requestQueue.getCache().clear();
requestQueue.add(stringRequest);
}
What I want is:
From 250 images for example, I want to load 10 images each time user scroll recyclerview, until 250 image to have been completed
private void populateData() {
int i = 0;
while (i < 10) {
rowsArrayList.add("Item " + i);
i++;
}
}
private void initAdapter() {
recyclerViewAdapter = new RecyclerViewAdapter(rowsArrayList);
recyclerView.setAdapter(recyclerViewAdapter);
}
private void initScrollListener() {
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(#NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
#Override
public void onScrolled(#NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
if (!isLoading) {
if (linearLayoutManager != null && linearLayoutManager.findLastCompletelyVisibleItemPosition() == rowsArrayList.size() - 1) {
//bottom of list!
loadMore();
isLoading = true;
}
}
}
});
}
private void loadMore() {
rowsArrayList.add(null);
recyclerViewAdapter.notifyItemInserted(rowsArrayList.size() - 1);
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
rowsArrayList.remove(rowsArrayList.size() - 1);
int scrollPosition = rowsArrayList.size();
recyclerViewAdapter.notifyItemRemoved(scrollPosition);
int currentSize = scrollPosition;
int nextLimit = currentSize + 10;
while (currentSize - 1 < nextLimit) {
rowsArrayList.add("Item " + currentSize);
currentSize++;
}
recyclerViewAdapter.notifyDataSetChanged();
isLoading = false;
}
}, 2000);
}
In Adapter Class
public class RecyclerViewAdapter extends RecyclerView.Adapter {
private final int VIEW_TYPE_ITEM = 0;
private final int VIEW_TYPE_LOADING = 1;
public List<String> mItemList;
public RecyclerViewAdapter(List<String> itemList) {
mItemList = itemList;
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_ITEM) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_row, parent, false);
return new ItemViewHolder(view);
} else {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_loading, parent, false);
return new LoadingViewHolder(view);
}
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder viewHolder, int position) {
if (viewHolder instanceof ItemViewHolder) {
populateItemRows((ItemViewHolder) viewHolder, position);
} else if (viewHolder instanceof LoadingViewHolder) {
showLoadingView((LoadingViewHolder) viewHolder, position);
}
}
#Override
public int getItemCount() {
return mItemList == null ? 0 : mItemList.size();
}
/**
* The following method decides the type of ViewHolder to display in the RecyclerView
*
* #param position
* #return
*/
#Override
public int getItemViewType(int position) {
return mItemList.get(position) == null ? VIEW_TYPE_LOADING : VIEW_TYPE_ITEM;
}
private class ItemViewHolder extends RecyclerView.ViewHolder {
TextView tvItem;
public ItemViewHolder(#NonNull View itemView) {
super(itemView);
tvItem = itemView.findViewById(R.id.tvItem);
}
}
private class LoadingViewHolder extends RecyclerView.ViewHolder {
ProgressBar progressBar;
public LoadingViewHolder(#NonNull View itemView) {
super(itemView);
progressBar = itemView.findViewById(R.id.progressBar);
}
}
private void showLoadingView(LoadingViewHolder viewHolder, int position) {
//ProgressBar would be displayed
}
private void populateItemRows(ItemViewHolder viewHolder, int position) {
String item = mItemList.get(position);
viewHolder.tvItem.setText(item);
}
Check out this link
https://www.journaldev.com/24041/android-recyclerview-load-more-endless-scrolling
you can load 10 items first then refreshing and load next 10.
I have an adapter which won't get notified after fetching new data by Retrofit however, I'm using notifyDataSetChanged and get success in the logs but nothing works either :(
Also I tried to use local array with this adapter and it works great.
public class alberrClassNews extends Fragment {
private RecyclerView recyclerView;
private List<alberrNewsListContent> alberrNews = new ArrayList<alberrNewsListContent>();
private final String TAG = "Get JSONArray";
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.alberrnewsclass, container, false);
getActivity().setTitle("nnnn");
recyclerView = (RecyclerView) view.findViewById(R.id.alberrNewsList);
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
layoutManager.setReverseLayout(true);
layoutManager.setStackFromEnd(true);
final alberrNewsAdapter aNA = new alberrNewsAdapter(getActivity(), alberrNews);
recyclerView.setAdapter(aNA);
final ProgressDialog progressDialog = new ProgressDialog(getActivity());
progressDialog.setMessage("loading");
progressDialog.show();
final Retrofit alberrRetrofit = new Retrofit.Builder()
.baseUrl("http://nn.com/abbn/")
.addConverterFactory(GsonConverterFactory.create()).build();
alberrService service = alberrRetrofit.create(alberrService.class);
Call<JSONArray> call = service.alberrNews();
call.enqueue(new Callback<JSONArray>() {
#Override
public void onResponse(Call<JSONArray> call, Response<JSONArray> response) {
progressDialog.dismiss();
Log.i(TAG, "success");
try{
JSONArray jsonArray = response.body().getJSONArray(0);
for (int i = 0 ; i < jsonArray.length() ; i++){
JSONObject jsonObject = jsonArray.getJSONObject(i);
alberrNewsListContent newsContent = new alberrNewsListContent();
newsContent.setAlberrNewsTitle(jsonObject.getString("title"));
newsContent.setAlberrNewsSubject(jsonObject.getString("subject"));
newsContent.setAlberrNewsImage(jsonObject.getString("image"));
alberrNews.add(newsContent);
}
} catch (JSONException e){
e.printStackTrace();
}
aNA.notifyDataSetChanged();
}
#Override
public void onFailure(Call<JSONArray> call, Throwable t) {
Log.i(TAG, "Failed");
progressDialog.dismiss();
Toast.makeText(getActivity().getApplicationContext(),
"no connection", Toast.LENGTH_SHORT).show();
}
});
return view;
}
Adapter Class
public class alberrNewsAdapter extends RecyclerView.Adapter<alberrNewsAdapter.ViewHolder> {
private List<alberrNewsListContent> newsListContent;
private Context context;
public alberrNewsAdapter(Context ncontext , List<alberrNewsListContent> nnewsListContent ){
this.context = ncontext;
this.newsListContent = nnewsListContent;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater
.from(parent.getContext())
.inflate(R.layout.alberrnewscontent, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(alberrNewsAdapter.ViewHolder holder, int position) {
final alberrNewsListContent newsContent = newsListContent.get(position);
holder.alberrNewsTitle.setText(newsContent.getAlberrNewsTitle());
holder.alberrNewsSubject.setText(newsContent.getAlberrNewsSubject());
Glide.with(context)
.load(newsContent.getAlberrNewsImage())
.override(300, 260)
.into(holder.alberrNewsImage);
}
#Override
public int getItemCount() {
return newsListContent.size();
}
public void replaceDataSet(List<alberrNewsListContent> alberrNews){
this.newsListContent = alberrNews;
notifyDataSetChanged();
}
public static class ViewHolder extends RecyclerView.ViewHolder{
TextView alberrNewsTitle;
TextView alberrNewsSubject;
ImageView alberrNewsImage;
CardView alberrNewsCardView;
public ViewHolder(View itemView) {
super(itemView);
alberrNewsTitle = (TextView)itemView.findViewById(R.id.alberrNewsTitle);
alberrNewsSubject = (TextView)itemView.findViewById(R.id.alberrNewsSubject);
alberrNewsImage = (ImageView)itemView.findViewById(R.id.alberrNewsImage);
alberrNewsCardView = (CardView)itemView.findViewById(R.id.alberrNewsCardView);
}
}
}
In your alberrNewsAdapter, you can create a new method called replaceDataSet() which will replace your dataset in the adapter and call notifyDataSetChanged().
private List<alberrNewsListContent> alberrNews alberrNews;
public void replaceDataSet(List<alberrNewsListContent> alberrNews){
this.alberrNews = alberrNews;
notifyDataSetChanged();
}
And in onResponse() instead of calling aNA.notifyDataSetChanged(); call aNA.replaceDataSet();
Pass new list content to the adapter via public method and call notifyDataSetChanged(); inside that method. Currently, you're not updating the list inside the adapter.
I'm fairly new to Android and I'm building and app with a group of friends from college . The problem that we have run into is that when filling the list of elements to display by getting data from a server the data loads and gets added to a list but the recyclerView never displays it (even after calling NotifyDataSetChanged from my custom Adapter) . Back when we used hardcoded data to test the app , this never happened.
The strange part is that the list of elements get displayed on the recyclerView after I turn off the display and then turn it back on (onBindViewHolder of my custom Adapter gets called when I turn the screen back on).Moreover , if I replace the fragment that contains the recyclerView with another and then return to it the data gets loaded to the list but not shown.
What I want to know is why is this happening and what can I do to solve it (and by solve it I mean showing the recyclerView populated with the correct items right as I launch the fragment and updating the recyclerView when I add more elements to the list)
We are using a custom adpater and Retrofit to get data from the server.
Here is the code
Fragment that contains the recyclerView
public class EventViewListFragment extends Fragment implements EventListAdapter.ClickListener{
private RestClient restClient;
private Builder builder;
private String token ;
private boolean userScrolled = true;
int pastVisiblesItems, visibleItemCount, totalItemCount;
private LinearLayoutManager mLayoutManager;
private RecyclerView recyclerView;
private EventListAdapter eventListAdapter;
private Button btnNewEvent;
FloatingActionButton suggestFAB;
private int currentPage=1;
public EventViewListFragment(){
restClient = new RestClient();
}
#Override
public void onCreate(Bundle savedInstanceState) {
System.out.println("Se llamo al onCreate de EventViewListFragment");
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
System.out.println("se llamo al onCreateView de EvENT List Fragment");
// Inflate the layout for this fragment
View layout =inflater.inflate(R.layout.fragment_events_list, container, false);
setUpElements(layout);
addListeners();
return layout;
}
private void setUpElements(View layout)
{
recyclerView = (RecyclerView) layout.findViewById(R.id.eventList);
eventListAdapter = new EventListAdapter(getActivity());
eventListAdapter.setClickListener(this);
eventListAdapter.setData(getInitialData());
recyclerView.setAdapter(eventListAdapter);
mLayoutManager=new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(mLayoutManager);
suggestFAB = (FloatingActionButton) layout.findViewById(R.id.suggestFAB);
builder = new Builder();
}
private void addListeners()
{
addNewEventListener();
addScrollBottomListener();
}
private void addNewEventListener()
{
/*btnNewEvent.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View view)
{
EventsActivity.getInstance().toNewEventForm();
}
});*/
suggestFAB.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
EventsActivity.getInstance().toNewEventForm();
currentPage=1;
}
});
}
public List<Event> getInitialData()
{
List<Event> data=new ArrayList<>();
data = getEvents(data);
return data;
}
#Override
public void itemClicked(View view, int position) {
EventsActivity.getInstance().toEventPage(eventListAdapter.getItemAtPos(position));
currentPage=1;
}
private void addScrollBottomListener() {
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
userScrolled = true;
}
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx,
int dy) {
super.onScrolled(recyclerView, dx, dy);
// Here get the child count, item count and visibleitems
// from layout manager
visibleItemCount = mLayoutManager.getChildCount();
totalItemCount = mLayoutManager.getItemCount();
pastVisiblesItems = mLayoutManager.findFirstVisibleItemPosition();
if (userScrolled && (visibleItemCount + pastVisiblesItems) == totalItemCount) {
userScrolled = false;
addNewElementsToList();
}
}
});
}
private void addNewElementsToList()
{
Toast.makeText(getActivity(), "Cargando Mas Elementos", Toast.LENGTH_SHORT).show();
eventListAdapter.setData(getEvents(eventListAdapter.getData()));
}
private List<Event> getEvents(final List<Event> eventsList)
{
System.out.println("Asignando CALL");
Call<JsonElement> eventPage = restClient.getConsumerService().getEvents(token, "", currentPage, 10);
System.out.println("Enquequeing");
eventPage.enqueue(new Callback<JsonElement>() {
#Override
public void onResponse(Response<JsonElement> response) {
JsonObject responseBody = response.body().getAsJsonObject();
if (responseBody.has("events")) {
JsonArray jsonArray = responseBody.getAsJsonArray("events");
System.out.println(jsonArray.size());
for (int i = 0; i < jsonArray.size(); i++) {
JsonObject storedObject = jsonArray.get(i).getAsJsonObject();
Event current = new Event();
current.setEventId(storedObject.get("id").getAsInt());
current.setName(storedObject.get("title").getAsString());
Calendar startCal = new GregorianCalendar();
startCal.setTimeInMillis((storedObject.get("starts_at").getAsLong()) * 1000);
current.setStartDateTime(startCal);
Calendar endCal = new GregorianCalendar();
endCal.setTimeInMillis((storedObject.get("ends_at").getAsLong()) * 1000);
current.setFinishDateTime(endCal);
current.setImgUrl(storedObject.get("image").getAsString());
eventsList.add(current);
}
} else {
if (responseBody.has("error")) {
System.out.println("ERROR");
wan.wanmarcos.models.Error error = builder.buildError(responseBody.get("error").getAsJsonObject());
Toast.makeText(getActivity(), "Error : " + error.toString(), Toast.LENGTH_SHORT).show();
} else {
}
}
}
#Override
public void onFailure(Throwable t) {
}
});
eventListAdapter.notifyDataSetChanged();
currentPage++;
return eventsList;
}
}
Custom Adapter for RecyclerView
public class EventListAdapter extends RecyclerView.Adapter<EventListAdapter.EventListViewHolder>{
private LayoutInflater inflater;
private List<Event> data = Collections.emptyList();
private Context context;
private ClickListener clickListener;
public EventListAdapter(Context context){
inflater = LayoutInflater.from(context);
this.context=context;
}
#Override
public EventListViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.event_list_item, parent, false);
EventListViewHolder holder = new EventListViewHolder(view);
return holder;
}
#Override
public void onBindViewHolder(EventListViewHolder holder, int position) {
System.out.println("se llamo al onBindViewHolder de ELA");
Event current = getData().get(position);
holder.title.setText(current.getName());
Picasso.with(context)
.load(current.getImgUrl())
.into(holder.img);
String startDateAndTime = current.CalendarToString(current.getStartDateTime())+" - "+current.CalendarToString(current.getFinishDateTime());
holder.dateAndTime.setText(startDateAndTime);
}
public void setClickListener(ClickListener clickListener){
this.clickListener=clickListener;
}
#Override
public int getItemCount() {
return getData().size();
}
public List<Event> getData() {
return data;
}
public void setData(List<Event> data) {
this.data = data;
}
class EventListViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView title;
TextView dateAndTime;
ImageView img;
public EventListViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(this);
title = (TextView) itemView.findViewById(R.id.eventListTitle);
img = (ImageView) itemView.findViewById(R.id.eventListImage);
dateAndTime =(TextView) itemView.findViewById(R.id.eventListDateAndTime);
}
#Override
public void onClick(View v) {
if(clickListener!=null)
{
clickListener.itemClicked(v,getPosition());
}
}
}
public Event getItemAtPos(int pos)
{
return getData().get(pos);
}
public interface ClickListener{
public void itemClicked(View view,int position);
}
}
Fragment XML File
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
android:id="#+id/main_content"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="wan.wanmarcos.fragments.EventViewListFragment">
<android.support.v7.widget.RecyclerView
android:id="#+id/eventList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
>
</android.support.v7.widget.RecyclerView>
</RelativeLayout>
<android.support.design.widget.FloatingActionButton
android:id="#+id/suggestFAB"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
app:layout_anchorGravity="bottom|right|end"
android:layout_margin="16dp"
android:clickable="true"
android:src="#mipmap/ic_add_white_48dp" />
</android.support.design.widget.CoordinatorLayout>
eventsList in your Activity and data in your Adapter are two different collections. You are filling up the former but not the latter.
Intialize data with new ArrayList<> instead of Collections.emptyList(); and then add a method in your Adapter, call addAll, like that:
public void addAll(final List<Event> new events) {
final int currentCount = data.size();
synchronized(data) {
data.addAll(events);
}
if (Looper.getMainLooper() == Looper.myLooper()) {
notifyItemRangeInserted(currentCount, events.size());
} else {
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
notifyItemRangeInserted(currentCount, events.size());
}
});
}
}
the if else checks if you are calling addAll from the ui thread or from a different thread and call notifyItemRangeInserted in a safe way. In the Activity when onResponse is invoked, after you filled up completely eventsList, just call eventListAdapter.addAll(eventsList)