I am trying to implement pagination in recyclerview to load more chat messages when the user scrolls to top , this is achieved by sending the last message time i.e coversations[0] time to the API , but when the new list is added the old List gets repeated many times . I think this is because i am not updating the time properly , What is the correct way to achieve this?
This is the code i am using, first time i am setting the flag to false and time as empty.
getConvoData(k, " ", "", false);
private String last_msg_time = " ";
private Boolean flag = false;
rv_convo.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (!recyclerView.canScrollVertically(-1)) {
if (conversations != null) {
String time = last_msg_time;
getConvoData(k, " ", time, true);
}
}
}
});
this is the method for fetching conversation Data
private void getConvoData(final String k, final String new_message, final String last_time, final boolean flag) {
final String token1 = Global.shared().tb_token;
final String url = "https://app.aer.media/v2/message_router/_getChat";
final JSONObject jsonBody = new JSONObject();
final ProgressDialog progressDialog = new ProgressDialog(this);
final String mRequestBody = jsonBody.toString();
StringRequest stringRequest = new StringRequest(Request.Method.POST, url, new com.android.volley.Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
JSONObject jObj = new JSONObject(response);
final JSONObject data = jObj.getJSONObject("data");
conversations = data.getJSONArray("conversation");
JSONObject for_chat = data.getJSONObject("for_chat");
JSONArray jsonArr_chat = new JSONArray();
jsonArr_chat.put(for_chat);
params = (RelativeLayout.LayoutParams) rv_convo.getLayoutParams();
GsonBuilder gsonBuilder = new GsonBuilder();
Gson gson = gsonBuilder.create();
if (!flag) {
convobeans = gson.fromJson(conversations.toString(), convType);
last_msg_time = conversations.getJSONObject(0).getString("time");
Log.d("OldList", convobeans.toString());
adapter = new ChatDetailsAdapter(forChatBeen, convobeans, ChatDetailsActivity.this, forChatBeansList, image, name, initials, new_message, bitmap);
// Collections.reverse(convobeans);
rv_convo.setAdapter(adapter);
rv_convo.smoothScrollToPosition( rv_convo.getAdapter().getItemCount() - 1);
adapter.notifyDataSetChanged();
rv_convo.setNestedScrollingEnabled(true);
} else {
newConvo = gson.fromJson(conversations.toString(), convType);
last_msg_time = conversations.getJSONObject(0).getString("time");
if (newConvo != null && newConvo.size() > 0) {
Log.d("newList", newConvo.toString());
convobeans.addAll(0, newConvo);
adapter.notifyItemChanged(0, newConvo.size());
}
}
}
}
}
Depending on the flag I am updating the list and updating the time as well but the list gets repeated in the RecyclerView due to the previous time being passed , how do I update the time optimally and fetch the new list each time?
This code is used to fetch the data when the user scroll down in a recylerview. Just analyze this code you will get the basic idea.
rvCategory.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (dy > 0) {
visibleItemCount = mLinearLayoutManager.getChildCount();
totalItemCount = mLinearLayoutManager.getItemCount();
pastVisiblesItems = mLinearLayoutManager.findFirstVisibleItemPosition();
if (loading) {
if ((visibleItemCount + pastVisiblesItems) >= totalItemCount) {
loading = false;
fetchData();
}
}
}
}
});
Function FetchData()
private void fetchData() {
String url = EndPoints.location + "getMobileData.php?lastData=" + lastData;
JsonObjectRequest jsObjRequest = new JsonObjectRequest
(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
lastData = response.getString("last");
JSONArray jArray = response.getJSONArray("response");
if (jArray.length() == 0) {
//Empty condition
} else {
for (int i = 0; i < jArray.length(); i++) {
//Append the chat with the Dataobject of your modelAnd swap the recylerview view with new data
//Example
}
adapter.swap(rvHomeModel.createHomeList(DataPathsHome, true));
}
} catch (JSONException e) {
e.printStackTrace();
}
loading = true;
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
// TODO Auto-generated method stub
loading = true;
Toast.makeText(CategoryView.this, "No internet connection", Toast.LENGTH_LONG).show();
}
});
// Add a request (in this example, called stringRequest) to your RequestQueue.
MySingleton.getInstance(this).addToRequestQueue(jsObjRequest);
}
Create a function called swap in your adapter class that accept the new dataset
public void swap(List<rvHomeModel> list) {
//Check your previouse dataset used in adapter is empty or not
if (rvHomeModels!= null) {
rvHomeModels.clear();
rvHomeModels.addAll(list);
} else {
rvHomeModels = list;
}
notifyDataSetChanged();
}
At server
1. Get the previous value
2. Do the database operation and get the chats id < of previous
2. Create a JSON Object contain
{
last:last_chat_id,
response:{
//Your chat
}
}
This is not a perfect solution for this question. But you will get the basic idea about what you are looking for.
Related
I am using Laravel API with paginated data in Android...
I want to get auto paginated data.
e.g. data on first page load first and when I scroll down, next pages load continously!
You can use laravel default pagination method instate of get rows.
For example i want show users list:
//instate of :
$users = User::get();
//Use :
$users = User::paginate();
more info at : laravel site
https://laravel.com/docs/5.5/pagination
You can do so easily using the LengthAwarePaginator
protected function paginate(Collection $collection)
{
$rules = [
'per_page' => 'integer|min:2|max:50',
];
Validator::validate(request()->all(), $rules);
$page = LengthAwarePaginator::resolveCurrentPage();
$perPage = 15;
if (request()->has('per_page')) {
$perPage = (int) request()->per_page;
}
$results = $collection->slice(($page - 1) * $perPage, $perPage)->values();
$paginated = new LengthAwarePaginator($results, $collection->count(), $perPage, $page, [
'path' => LengthAwarePaginator::resolveCurrentPath(),
]);
$paginated->appends(request()->all());
return $paginated;
}
Make sure you import all the namespaces needed,
You can go ahead and paginate the collection thus
$collection = $this->paginate($collection);
Hope this helps
On Laravel Side
// Get Collection
$query = SomeModel::where('some_thing', "5")
->paginate(10);
return Response::json(array(
'some_result' => $query,
));
On Android Side (Retrofit Call)
#FormUrlEncoded
#POST
Call<JsonObject> getActions(
#Url String url);
On Android Side (Activity/Fragment)
public class NewTemplateActivity
extends AppCompatActivity {
// Recycler View
private SomeActionListAdapter someActionsAdapter;
private List<SomesomeActionListModel> listSomeActionModel;
private RecyclerView rv;
private Boolean isScrolling = false;
private int currentItems, totalItems, scrollOutItems;
private int pageNumber = 1;
private Boolean firstLoad = true;
private Boolean firstSearch = false;
private LinearLayoutManager manager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list);
// Init Recycler View
rv = findViewById(R.id.f_list_rv_list);
rv.setHasFixedSize(true);
listSomeActionModel = new ArrayList<>();
someActionsAdapter = new SomeActionListAdapter(
mContext,
listSomeActionModel);
manager = new LinearLayoutManager(mContext);
rv.setLayoutManager(manager);
rv.setAdapter(someActionsAdapter);
}
private void getSomeAction(
String url) {
if (firstLoad || firstSearch) {
listSomeActionModel.clear();
}
// Retrofit Call
Call<JsonObject> call = RetrofitClientLaravel.getInstance(mContext)
.discussGetApi()
.getSomeAction(url);
call.enqueue(new Callback<JsonObject>() {
#Override
public void onResponse(
Call<JsonObject> call,
Response<JsonObject> response) {
JSONObject jsonObject;
try {
jsonObject = new JSONObject(new Gson().toJson(response.body()));
// Get The Relevant Array
JSONObject jsonObject2 = jsonObject.getJSONObject("some_result");
JSONArray returnArray = jsonObject2.getJSONArray("data");
onScrolled();
if (!returnArray.isNull(0)) {
for (int l = 0; l < returnArray.length(); l++) {
if (returnArray.length() > 0) {
// Get The Json Object
JSONObject returnJSONObject = returnArray.getJSONObject(l);
// Get Details
String id = returnJSONObject.optString("SomesomeAction_id");
// Populate The Array List
listSomeActionModel.add(new SomesomeActionListModel(id));
}
}
}
if (firstLoad || firstSearch) {
someActionsAdapter = new SomeActionListAdapter(
mContext,
listSomeActionModel);
rv.setAdapter(someActionsAdapter);
}
someActionsAdapter.notifyDataSetChanged();
} catch (JSONException e) {
e.printStackTrace();
}
}
#Override
public void onFailure(
Call<JsonObject> call,
Throwable t) {
}
});
}
public void onScrolled() {
rv.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(
RecyclerView recyclerView,
int newState) {
super.onScrollStateChanged(
recyclerView,
newState);
if (newState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
isScrolling = true;
}
}
#Override
public void onScrolled(
RecyclerView recyclerView,
int dx,
int dy) {
super.onScrolled(
recyclerView,
dx,
dy);
currentItems = manager.getChildCount();
totalItems = manager.getItemCount();
scrollOutItems = manager.findFirstVisibleItemPosition();
if (isScrolling && (currentItems + scrollOutItems == totalItems)) {
isScrolling = false;
firstLoad = false;
firstSearch = false;
Toast.makeText(
mContext,
"Loading More...",
Toast.LENGTH_SHORT)
.show();
pageNumber++;
getSomeAction(Env.LARAVEL_MAIN_URL +
"/api/get_SomesomeActions" +
"?page=" +
pageNumber);
}
}
});
}
}
I have a problem with the endless scroll. Every time it loads more data, it returns to the top view. What I want is the RecyclerView to stay in the last position when it loads new data.
I am trying to implement this code https://github.com/codepath/android_guides/wiki/Endless-Scrolling-with-AdapterViews-and-RecyclerView
here is the endless scroll code
public abstract class EndlessRecyclerViewScrollListener extends RecyclerView.OnScrollListener{
// The minimum amount of items to have below your current scroll position
// before loading more.
private int visibleThreshold = 30;
// The current offset index of data you have loaded
private int currentPage = 0;
// The total number of items in the dataset after the last load
private int previousTotalItemCount = 0;
// True if we are still waiting for the last set of data to load.
private boolean loading = true;
// Sets the starting page index
private int startingPageIndex = 0;
RecyclerView.LayoutManager mLayoutManager;
public EndlessRecyclerViewScrollListener(LinearLayoutManager layoutManager) {
this.mLayoutManager = layoutManager;
}
public int getLastVisibleItem(int[] lastVisibleItemPositions) {
int maxSize = 0;
for (int i = 0; i < lastVisibleItemPositions.length; i++) {
if (i == 0) {
maxSize = lastVisibleItemPositions[i];
}
else if (lastVisibleItemPositions[i] > maxSize) {
maxSize = lastVisibleItemPositions[i];
}
}
return maxSize;
}
// This happens many times a second during a scroll, so be wary of the code you place here.
// We are given a few useful parameters to help us work out if we need to load some more data,
// but first we check if we are waiting for the previous load to finish.
#Override
public void onScrolled(RecyclerView view, int dx, int dy) {
int lastVisibleItemPosition = 0;
int totalItemCount = mLayoutManager.getItemCount();
if (mLayoutManager instanceof LinearLayoutManager) {
lastVisibleItemPosition = ((LinearLayoutManager) mLayoutManager).findLastVisibleItemPosition();
}
// If the total item count is zero and the previous isn't, assume the
// list is invalidated and should be reset back to initial state
if (totalItemCount < previousTotalItemCount) {
this.currentPage = this.startingPageIndex;
this.previousTotalItemCount = totalItemCount;
if (totalItemCount == 0) {
this.loading = true;
}
}
// If it’s still loading, we check to see if the dataset count has
// changed, if so we conclude it has finished loading and update the current page
// number and total item count.
if (loading && (totalItemCount > previousTotalItemCount)) {
loading = false;
previousTotalItemCount = totalItemCount;
}
// If it isn’t currently loading, we check to see if we have breached
// the visibleThreshold and need to reload more data.
// If we do need to reload some more data, we execute onLoadMore to fetch the data.
// threshold should reflect how many total columns there are too
if (!loading && (lastVisibleItemPosition + visibleThreshold) > totalItemCount) {
currentPage++;
onLoadMore(currentPage, totalItemCount, view);
loading = true;
}
}
// Call this method whenever performing new searches
public void resetState() {
this.currentPage = this.startingPageIndex;
this.previousTotalItemCount = 0;
this.loading = true;
}
// Defines the process for actually loading more data based on page
public abstract void onLoadMore(int page, int totalItemsCount, RecyclerView view);
}
OnCreateView
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
View view = inflater.inflate(R.layout.booking_fragment, container, false);
recyclerView = (RecyclerView) view.findViewById(R.id.bookingRecyclerView);
linearLayoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setHasFixedSize(true);
scrollListener = new EndlessRecyclerViewScrollListener(linearLayoutManager) {
#Override
public void onLoadMore(int page, int totalItemsCount, RecyclerView view) {
// Triggered only when new data needs to be appended to the list
// Add whatever code is needed to append new items to the bottom of the list
if(CurrentStatus.equals("notSearch")){
if (current < Integer.parseInt(TP)) {
current++;
loadMoreBookings(sort);
}
else if(current == Integer.parseInt(TP)){
Toast.makeText(getActivity(),"No More Data to Load", Toast.LENGTH_SHORT).show();
}
}else{
if (current < Integer.parseInt(TP)) {
current++;
searchMoreBookings(search, sort);
}
else if(current == Integer.parseInt(TP)){
Toast.makeText(getActivity(),"No More Data to Be Load", Toast.LENGTH_SHORT).show();
}
}
}
LoadMoreBookings
private void loadMoreBookings(final String sort){
CurrentStatus = "notSearch";
final ProgressDialog progressDialog = new ProgressDialog(getActivity());
progressDialog.setMessage("Please Wait While Retrieving Data");
progressDialog.setCancelable(false);
progressDialog.show();
StringRequest requesting = new StringRequest(Request.Method.POST, URL,
new Response.Listener<String>() {
#Override
public void onResponse(String JSONString) {
progressDialog.dismiss();
try{
JSONObject jsonTP = new JSONObject(JSONString);
JSONArray jsonArrayB = jsonTP.getJSONArray("Data");
for(int i = 0; i < jsonArrayB.length(); i++){
JSONObject o = jsonArrayB.getJSONObject(i);
Booking list = new Booking(
o.getString("bookID"),
o.getString("userEmail"),
o.getString("paymentMethod"),
o.getString("paymentStatus"),
o.getString("totalPrice"),
o.getString(String.valueOf("securityCode")),
o.getString(String.valueOf("travelDate")),
o.getString("paymentID"),
o.getString("userFN"),
o.getString("userLN"),
o.getString(String.valueOf("createdAt")),
o.getString("tTM"),
o.getString("messageToCustomer"),
o.getString("messageFromMerchant"),
o.getString("wCN"),
o.getString("wLocation"),
o.getString("wWebsite")
);
listBooking.add(list);
count++;
}
Toast.makeText(getActivity(), "Data : "+count, Toast.LENGTH_SHORT).show();
adapter = new BookingAdapter(listBooking,getActivity());
recyclerView.setAdapter(adapter);
} catch (JSONException e) {
loadMoreBookings(sort);
}
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
progressDialog.dismiss();
Toast.makeText(getActivity().getApplicationContext(), "Failed To Retrieve Data. Please Try Again.",Toast.LENGTH_LONG).show();
}
}
){
#Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String,String> params = new HashMap<String, String>();
String user = getActivity().getIntent().getStringExtra("username");
params.put("username", user);
params.put("currentpage", String.valueOf(current));
params.put("sorting", sort);
return params;
}
};
RequestQueue requestQueue = Volley.newRequestQueue(getActivity().getApplicationContext());
requestQueue.add(requesting);
}
set adapter in onCreateView()
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
View view = inflater.inflate(R.layout.booking_fragment, container, false);
recyclerView = (RecyclerView) view.findViewById(R.id.bookingRecyclerView);
linearLayoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(linearLayoutManager);
adapter = new BookingAdapter(listBooking,getActivity());
recyclerView.setAdapter(adapter);
scrollListener = new EndlessRecyclerViewScrollListener(linearLayoutManager) {
#Override
public void onLoadMore(int page, int totalItemsCount, RecyclerView view) {
// Triggered only when new data needs to be appended to the list
// Add whatever code is needed to append new items to the bottom of the list
if(CurrentStatus.equals("notSearch")){
if (current < Integer.parseInt(TP)) {
current++;
loadMoreBookings(sort);
}
else if(current == Integer.parseInt(TP)){
Toast.makeText(getActivity(),"No More Data to Load", Toast.LENGTH_SHORT).show();
}
}else{
if (current < Integer.parseInt(TP)) {
current++;
searchMoreBookings(search, sort);
}
else if(current == Integer.parseInt(TP)){
Toast.makeText(getActivity(),"No More Data to Be Load", Toast.LENGTH_SHORT).show();
}
}
}
don't set adapter every time just notify the adapter item inserted at position.
private void loadMoreBookings(final String sort){
CurrentStatus = "notSearch";
final ProgressDialog progressDialog = new ProgressDialog(getActivity());
progressDialog.setMessage("Please Wait While Retrieving Data");
progressDialog.setCancelable(false);
progressDialog.show();
StringRequest requesting = new StringRequest(Request.Method.POST, URL,
new Response.Listener<String>() {
#Override
public void onResponse(String JSONString) {
progressDialog.dismiss();
try{
JSONObject jsonTP = new JSONObject(JSONString);
JSONArray jsonArrayB = jsonTP.getJSONArray("Data");
for(int i = 0; i < jsonArrayB.length(); i++){
JSONObject o = jsonArrayB.getJSONObject(i);
Booking list = new Booking(
o.getString("bookID"),
o.getString("userEmail"),
o.getString("paymentMethod"),
o.getString("paymentStatus"),
o.getString("totalPrice"),
o.getString(String.valueOf("securityCode")),
o.getString(String.valueOf("travelDate")),
o.getString("paymentID"),
o.getString("userFN"),
o.getString("userLN"),
o.getString(String.valueOf("createdAt")),
o.getString("tTM"),
o.getString("messageToCustomer"),
o.getString("messageFromMerchant"),
o.getString("wCN"),
o.getString("wLocation"),
o.getString("wWebsite")
);
listBooking.add(list);
adapter.notifyItemInserted(count);
count++;
}
Toast.makeText(getActivity(), "Data : "+count, Toast.LENGTH_SHORT).show();
} catch (JSONException e) {
loadMoreBookings(sort);
}
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
progressDialog.dismiss();
Toast.makeText(getActivity().getApplicationContext(), "Failed To Retrieve Data. Please Try Again.",Toast.LENGTH_LONG).show();
}
}
){
#Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String,String> params = new HashMap<String, String>();
String user = getActivity().getIntent().getStringExtra("username");
params.put("username", user);
params.put("currentpage", String.valueOf(current));
params.put("sorting", sort);
return params;
}
};
RequestQueue requestQueue = Volley.newRequestQueue(getActivity().getApplicationContext());
requestQueue.add(requesting);
}
I have fragments with RecyclerView in my activity, But whenever I start the fragment, the RecyclerView is already scrolled to the bottom. To load more item, user have to scroll a little bit upwards and then scroll back to the bottom, And when more items load, RecyclerView is again scrolled to the new bottom.
Here is the code in fragment.
public class FavouriteFragment extends Fragment{
public RecyclerView recyclerView ;
public RecyclerView.Adapter adapter ;
public LinearLayoutManager layoutManager ;
int pastVisiblesItems, visibleItemCount, totalItemCount;
Constants constants;
ArrayList<CardSetterGetter> arrayList;
private List<CardSetterGetter> imagelist;
int start ;
public int end ;
boolean loading = true ;
int totalcount ;
ArrayList<String> imageNameArray ;
String androidId;
String categoryName ;
String cateGoryFlag = "false";
String url;
String tagname = "";
ArrayList<String> title ;
ProgressBar progressBar ;
TextView errormsg ;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//to hide search
setHasOptionsMenu(true);
}
#Override
public void onPrepareOptionsMenu(Menu menu) {
MenuItem item = menu.findItem(R.id.search);
item.setVisible(false);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.favourite_fragment,container,false);
arrayList = new ArrayList<CardSetterGetter>();
imageNameArray = new ArrayList<String>();
start = 0;
end = start + 2 ;
imagelist = new ArrayList<CardSetterGetter>();
recyclerView = (RecyclerView)view.findViewById(R.id.recyclerview);
progressBar = (ProgressBar)view.findViewById(R.id.progressBar);
errormsg = (TextView) view.findViewById(R.id.error);
getdata();
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if(dy > 0)
{
visibleItemCount = layoutManager.getChildCount();
totalItemCount = layoutManager.getItemCount();
pastVisiblesItems = layoutManager.findFirstVisibleItemPosition();
int i = visibleItemCount + pastVisiblesItems ;
System.out.println(i);
System.out.println(totalItemCount);
if(loading) {
if (i == totalItemCount) {
loading = false ;
start = arrayList.size() ;
end = arrayList.size() + 2 ;
requestWithSomeHttpHeaders2(start,end);
new Handler().postDelayed(new Runnable() { // This thread runs in the UI
#Override
public void run() {
loading = true;
}
},2000);
}
}
}
}
});
requestWithSomeHttpHeaders2(start, end);
return view ;
}
public void requestWithSomeHttpHeaders2(int s , int e) {
RequestQueue queue = Volley.newRequestQueue(getActivity());
url = "my url";
StringRequest postRequest = new StringRequest(Request.Method.GET, url ,
new Response.Listener<String> ()
{
#Override
public void onResponse(String response) {
try {
progressBar.setVisibility(View.GONE);
JSONObject jsonObject = new JSONObject(response);
JSONArray payload = jsonObject.getJSONArray("Payload");
JSONObject MetaData = jsonObject.getJSONObject("MetaData");
System.out.println(response);
totalcount = MetaData.getInt("TotalCount");
System.out.println("200" + " " + totalcount);
System.out.println("payload length"+payload.length());
int i ;
for(i =0 ; i < payload.length() ; i++)
{
System.out.println(payload.getJSONObject(i).getString("image_title"));
System.out.println(payload.getJSONObject(i).getString("image_title"));
System.out.println(payload.getJSONObject(i).getString("image_file"));
System.out.println(payload.getJSONObject(i).getString("image_description"));
System.out.println(payload.getJSONObject(i).getString("image_category"));
System.out.println(payload.getJSONObject(i).getString("image_tags"));
System.out.println(payload.getJSONObject(i).getString("id"));
System.out.println(payload.getJSONObject(i).getString("favourite"));
String newString = payload.getJSONObject(i).getString("image_file").replace("original_image", "thumbnail");
CardSetterGetter all = new CardSetterGetter(payload.getJSONObject(i).getString("image_file"),
payload.getJSONObject(i).getString("image_title"),
payload.getJSONObject(i).getString("image_description"),
payload.getJSONObject(i).getString("image_category"),
payload.getJSONObject(i).getString("image_tags"),
payload.getJSONObject(i).getString("id"),
payload.getJSONObject(i).getString("favourite"));
System.out.println("i value is " + i);
arrayList.add(all);
Handler handler = new Handler();
final Runnable r = new Runnable() {
public void run() {
adapter = new RecyclerViewAdapter(getActivity(),arrayList,imageNameArray,totalItemCount);
recyclerView.setHasFixedSize(true);
layoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
adapter.notifyDataSetChanged();
//adapter.notifyItemInserted(arrayList.size() - 1);
if (end >= totalcount)
{
int index = totalcount -1 ;
recyclerView.scrollToPosition(index);
}
else {
recyclerView.scrollToPosition(end);
}
}
};
handler.post(r);
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("error" + e);
}
}
},
new Response.ErrorListener()
{
#Override
public void onErrorResponse(VolleyError error) {
// TODO Auto-generated method stub
Log.d("ERROR","error => "+error.toString());
// definitions.setText("check your internet connection");
NetworkResponse networkResponse = error.networkResponse;
// Log.e("Volley", "Error. HTTP Status Code:"+networkResponse.statusCode);
if (networkResponse != null) {
Log.e("Volley", "Error. HTTP Status Code:"+networkResponse.statusCode);
}
if (error instanceof TimeoutError) {
Log.e("Volley", "TimeoutError");
}else if(error instanceof NoConnectionError){
Log.e("Volley", "NoConnectionError");
} else if (error instanceof AuthFailureError) {
Log.e("Volley", "AuthFailureError");
} else if (error instanceof ServerError) {
Log.e("Volley", "ServerError");
} else if (error instanceof NetworkError) {
Log.e("Volley", "NetworkError");
} else if (error instanceof ParseError) {
Log.e("Volley", "ParseError");
}
}
}
);
queue.add(postRequest);
}
public void getdata(){
try {
SharedPreferences sharedPref = getActivity().getSharedPreferences("mobileId" , Context.MODE_PRIVATE);
androidId = sharedPref.getString("mobileId" , "");
sharedPref = getActivity().getSharedPreferences("categoryInfo" , Context.MODE_PRIVATE);
categoryName = sharedPref.getString("categoryName" , "");
cateGoryFlag = sharedPref.getString("categoryFlag" , "");
tagname = sharedPref.getString("tagname" , "");
}catch (Exception e){
}
}
}
I tried removing if else part of the code, but that made the recyclerView scroll to the top whenever user reach the bottom of the recyclerView.
Please tell me if you need more of the code or code of RecyclerViewAdapter.
I fixed a similar problem by providing the correct orientation to the layout manager. I would change your code to something like:
public RecyclerView.Adapter adapter ;
adapter = new RecyclerViewAdapter(getActivity(),arrayList,imageNameArray,totalItemCount);
recyclerView.setHasFixedSize(true);
//This line should do the trick:
//first parameter is the context
//second parameter is the orientation
//third parameter is used for reverse layout (if you need to show items from last to first)
layoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
//probably better to use notifyDataSetChanged to let the adapter know you just initialized it (instead of notifying a single element)
adapter.notifyDataSetChanged();
Not sure why you provide the total item count to the adapter as well, but that's outside the scope of the question and surely doesn't give any problems.
EDIT:
Now that you posted the whole fragment code, I am seeing at least a couple problems:
- the RecyclerView initialization should not be in a runnable, but in the onCreateView or some starting function. You just need to set the manager and the adapter once.
- You provide imageNameArray to your adapter, but where are you inserting new items into it? If you want your RecyclerView to scroll down to the last inserted item, you first need to add an item to the ArrayList, then notify the insertion at that position, something like this:
int position = imageNameAray.size();
imageNameArray.add(item);
adapter.notifyItemInserted(position); //this makes the view update the item in that position, making the recycler to scroll there
I have a website which publishes news on daily basis.
Now, I'm sending a JsonArrayRequest to retrieve and parse the title and summary of each news published on the website. The parsed items are then used to populate RecyclerView.
The problem I'm having is the way volley implements caching .
Let's take this scenario: the app is installed, launched and the RecyclerView is populated. The user reads the news and forgets about the app
Later, the user launches the app and the items are fetched and RecyclerView is populated.
Between the first and the second launch, new news are published on the website. But in the second launch, these new items are not displayed. However, if the user manually go to app settings and clear cache of the app, and relaunch, the new items are displayed.
You get my point?
While I don't want to disable Volley caching, how do I make it to always fetch new items?
EDIT
MainActivity
public class MainActivity extends AppCompatActivity {
private final String TAG = "MainActivity";
//Creating a list of newss
private List<NewsItems> mNewsItemsList;
//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.news_recycler);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
//Initializing the newslist
mNewsItemsList = new ArrayList<>();
adapter = new NewsAdapter(mNewsItemsList, 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();
}
}
//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(ConfigNews.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++) {
NewsItems newsItem = new NewsItems();
JSONObject jsonObject = null;
try {
jsonObject = array.getJSONObject(i);
newsItem.setNews_title(jsonObject.getString(ConfigNews.TAG_VIDEO_TITLE));
newsItem.setNews_body(jsonObject.getString(ConfigNews.TAG_VIDEO_BODY));
} catch (JSONException w) {
w.printStackTrace();
}
mNewsItemsList.add(newsItem);
}
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");
}
}
}
Option 1) Delete Cache
before you make a call you can delete the whole cache by myDiskBasedCache.clear() or specific entries by myDiskBasedCache.remove(entryUrl)
Option 2) Custom CacheParser (in the Request)
#Override
protected Response<Bitmap> parseNetworkResponse(NetworkResponse response) {
Response<Bitmap> resp = super.parseNetworkResponse(response);
if(!resp.isSuccess()) {
return resp;
}
long now = System.currentTimeMillis();
Cache.Entry entry = resp.cacheEntry;
if(entry == null) {
entry = new Cache.Entry();
entry.data = response.data;
entry.responseHeaders = response.headers;
entry.ttl = now + 60 * 60 * 1000; //keeps cache for 1 hr
}
entry.softTtl = 0; // will always refresh
return Response.success(resp.result, entry);
}
Option 3) send requests that does not cache
myRequest.setShouldCache(false);
Option 4) use custom Cache implementation
UPDATE:
Example with your code:
//Creating a json request
JsonArrayRequest jsonArrayRequest = new JsonArrayRequest(ConfigNews.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) {
}
}) {
#Override
protected Response<JSONArray> parseNetworkResponse(NetworkResponse response) {
Response<JSONArray> resp = super.parseNetworkResponse(response);
if(!resp.isSuccess()) {
return resp;
}
long now = System.currentTimeMillis();
Cache.Entry entry = resp.cacheEntry;
if(entry == null) {
entry = new Cache.Entry();
entry.data = response.data;
entry.responseHeaders = response.headers;
entry.ttl = now + 60 * 60 * 1000; //keeps cache for 1 hr
}
entry.softTtl = 0; // will always refresh
return Response.success(resp.result, entry);
}
};
UPDATE 2
Http protocol caching supports many ways to define how the client can cache responses and when to update them. Volley simplifies those rules to:
entry.ttl (time to live in ms) if greater than the current time then cache can be used otherwise fresh request needs to be made
and
entry.softTtl (soft time to live in ms :) if greater than the current time
cache is absolutely valid and no request to the server needs to be made, otherwise new request is still made (even if the ttl is good) and if there is a change new response will be delivered.
note that if ttl is valid and softTtl is not you can receive 2 onResponse calls
My Recyclerview was working fine , what i did was simple, i fetched data from a webervice and displayed it using recyclerView
This is my code for loading more data
public void loadmoredata(int limit) {
limit++;
String url = "http://oilpeople.talenetic.com/api/SearchJob?limit="+limit+ "&jobkeyword=" + key + "&countrytext=" + coun + "&location=" +loc+ "&apikey=1111111111&siteid=1";
JsonObjectRequest loadmore = new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
JSONArray jsonArray = response.getJSONArray(loadArray);
jobTitle = new String[jsonArray.length()];
salary = new String[jsonArray.length()];
companyName = new String[jsonArray.length()];
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject c = jsonArray.getJSONObject(i);
// Storing each json item in variable
if(loadArray.equals("Indeedjobslist")) {
String salaryLoc = c.getString("location");
jobTitle[i] = c.getString("jobtitle");
companyName[i] = c.getString("companyname");
salary[i] = salaryLoc;
} else {
String salaryLoc = c.getString("salary")+ " \u2022 " +c.getString("location");
jobTitle[i] = c.getString("jobtitle");
companyName[i] = c.getString("companyname");
salary[i] = salaryLoc;
}
// show the values in our logcat
m.setCompanyName(companyName);
m.setJobTitle(jobTitle);
m.setSalary(salary);
adapter.notifyDataSetChanged();
}
} catch(JSONException e) {
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
});
Volley.newRequestQueue(SearchResults.this).add(loadmore);
}
adapter is a global variable. I am trying to load more data like this:
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.fTextView.setText(jobTitle[position]);
holder.mTextView.setText(companyName[position]);
holder.sTextView.setText(jobs[position]);
if (position % 10 == 0) {
int limit = position + 5;
loadmoredata(limit);
}
}
Whenever the % of position is 0 I load more data, but I am getting indexoutofBounds error.
remove
if (position % 10 == 0) {
int limit = position + 5;
loadmoredata(limit);
}
and use this method calling to know count from your main activity then call your above cod but i think that will cause infinite loop
public int getItemCount() {
return mDataset.length;
}