How can I keep my scroll position when I refresh my data in my RecyclerView ?
The problem in my code, is that the data I get is coming from an API, and now when I refresh my data, I use the clear() method to delete the current data, and I use a getData () method to retrieve the new data.
getData method :
private void getData() {
JsonArrayRequest arrayRequest = new JsonArrayRequest(url_get, response -> {
JSONObject jsonObject;
for (int i = 0; i < response.length(); i++) {
try {
jsonObject = response.getJSONObject(i);
Category cat = new Category();
cat.setId(jsonObject.getString("id"));
cat.setCategory(jsonObject.getString("name"));
cat.setPvisited(jsonObject.getString("person_visited"));
cat.setCompany(jsonObject.getString("company"));
cat.setDate(jsonObject.getString("date"));
category.add(cat);
} catch (JSONException e) {
e.printStackTrace();
}
}
adapterPush(category);
refresh.setRefreshing(false);
}, error -> {
});
RequestQueue requestQueue = Volley.newRequestQueue(MainActivity.this);
requestQueue.add(arrayRequest);
}
category detail :
private final ArrayList<Category> category = new ArrayList<>();
onRefresh method :
#Override
public void onRefresh() {
category.clear();
getData();
}
I think the getdata() method has a problem, when I remove
category.clear();
of my code, the data is added to the string but the position still returns to the beginning.
Or maybe I should used other thing like a refresh method rather than clear() ?
category.clear();
recyclerview.notifyDataSetChanged();
Related
Hello, I am trying to Load only first 5 elements which I am fetching from an API . Currently, it is fetching all Items.
This is my current code which displays all items :
This is the Adapter code (i am not posting unnecessary methods)
Context mCtx;
List<VolleyModel> volleyModelList;
public VolleyAdapter(Context mCtx, List<VolleyModel> volleyModelList) {
this.mCtx = mCtx;
this.volleyModelList = volleyModelList;
}
#Override
public int getItemCount() {
return volleyModelList.size();
}
This is the Activity code :
List<VolleyModel> volleyModelList;
JsonArrayRequest jsonArrayRequest = new JsonArrayRequest(Request.Method.GET, URL, null, response -> {
for (int i = 0; i < response.length(); i++) {
try {
JSONObject jsonObject = response.getJSONObject(i);
String id = jsonObject.getString("id");
authorName = insideAuthorArray.getString("name");
volleyModelList.add(new VolleyModel(id, authorName));
volleyAdapter.notifyDataSetChanged();
progressBarLoadMore.setVisibility(View.GONE);
} catch (JSONException e) {
e.printStackTrace();
Log.e("ERROR HomeFragment", e.getMessage());
}
volleyAdapter = new VolleyAdapter(getActivity(), volleyModelList);
recyclerView.setAdapter(volleyAdapter);
The above code properly displays every result which is returned by the server. What should I do so get only the first 5 items ?
Just modify this method inside your adapter code:
#Override
public int getItemCount() {
int size = volleyModelList.size();
// Return at most 5 items from the ArrayList
return (size > 5 ? 5 : size);
}
try it like this , it will only show 5 items
#Override
public int getItemCount() {
//return volleyModelList.size();
return 5;
}
Few days ago, I asked this question for avoiding repetition of reycylerview items whose accepted answer helped me to avoid data repetition.
But now I am facing new problems like: not showing all items sometimes only one like this . The real problem is even after getting all items from server properly data is not shown in recyclerView properly .Data are skipped randomly. I don't understand where the problem is. I even tried to use for loop instead of foreach but result was not different. Can anyone please help to fix this? It has been pain in the neck from last one week.
Code:
private List<TimelineData> timelineDataList=new ArrayList<>() ;
public void onCreateView(){
recyclerview.setLayoutManager(new LinearLayoutManager(ctx));
//Setting Adapter
adapter=new CustomRecyclerViewAdapter(timelineDataList);
recyclerview.setAdapter(adapter);
}
#Override
public void onStart() {
super.onStart();
// Fetching data from server
socket.disconnect();
socket.connect();
//Getting Data from server
JSONObject obj=new JSONObject();
try {
obj.put("timeline_posts","all");
socket.emit("data",obj);
} catch (JSONException e) {
e.printStackTrace();
}
}
void addTimelineData(String type,String time,String img_link){
boolean isRepeated = false;
for(TimelineData data : timelineDataList){
if(data.getTime().equals(time)){
isRepeated = true;
}
}
if(!isRepeated){
timelineDataList.add(new TimelineData(type,time,img_link));
}
adapter.notifyDataSetChanged();
}
private Emitter.Listener handlePosts = new Emitter.Listener(){
#Override
public void call(final Object... args){
try {
JSONArray jsonArray=(JSONArray)args[0];
timelineDataList.clear(); //clear data before inserting new one
for(int i=0;i<jsonArray.length();i++){
try {
JSONObject ob=jsonArray.getJSONObject(i);
post_type=ob.getString("post_type");
post_time=ob.getString("time");
post_link=ob.getString("img_link");
addTimelineData(post_type,post_time,post_link);
} catch (JSONException e) {
e.printStackTrace();
}
}
} catch (Exception e) {
Log.e("error",e.toString());
}
}
};
Adapter Code:
#Override
public void onBindViewHolder( CustomRecyclerViewHolder holder, int position) {
//Fetching TimelineData
TimelineData timelineData=totalList.get(position);
///Here I'm getting and converting array of image links which are there in jsonObject to arraylist
//Getting Imglink
Gson gson=new Gson();
Type type = new TypeToken<ArrayList<String>>() {}.getType();
ArrayList<String> arrayList = gson.fromJson(timelineData.getImg_link(), type);
//Setting ViewPager
CustomPagerAdapter adp=new CustomPagerAdapter(arrayList);
pager.setAdapter(new PagerAdapter() {
#Override
public int getCount() {
return 0;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return false;
}
});
holder.pager.setCurrentItem(position, false);
holder.pager.clearAnimation();
adp.notifyDataSetChanged();
holder.pager.setAdapter(adp);
holder.pager.setOffscreenPageLimit(1);
}
You are using notifyDataSetChnaged() very quickly, change notify method by this. So that you notify only selected item which you inserted.
Recommended method : You will put below code in your adapter and call this method from for loop where you were setting notifyDataSetChnaged here String s will be replaced by your model class. By this way you just notify only one element when inserting one element. This will also create some inserting animation automatically.
public void insertItemInList(String s) {
if (list == null) list = new ArrayList<>();
list.add(s);
notifyItemInserted(list.size() - 1);
}
Or
You can call notify outside for loop when your work is done like this.
for(int i=0;i<jsonArray.length();i++) {
try {
JSONObject ob=jsonArray.getJSONObject(i);
post_type=ob.getString("post_type");
post_time=ob.getString("time");
post_link=ob.getString("img_link");
addTimelineData(post_type,post_time,post_link);
} catch (JSONException e) {
e.printStackTrace();
}
}
adapter.notifyDataSetChanged();
Issue is that you are notifying adapter rapidly, it can also lead to UI inconsistency.
Let me know if this resolves your issue.
You are modifying the data list in the non-UI thread which could cause problems in the RecyclerView. Instead, you should collect all the data in tempTimelineDataList at once and update the adapted timelineDataList in the UI thread.
This question already has answers here:
ArrayList being empty after adding elements
(2 answers)
Using volley library, inside the onResponse Method the ArrayList have some data but outside the OnResponse method the arraylist is empty
(1 answer)
Closed 5 years ago.
So, I've got the following code that hits up a database for some info:
// This is the arraylist I am talking about
private ArrayList<String> results = new ArrayList<String>();
private String getAllSongsScriptUrl =
"https://songs-list-site-test.herokuapp.com/get_songs_list.php";
private RequestQueue requestQueue;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_songs);
requestQueue = Volley.newRequestQueue(getApplicationContext());
// Prepare the list to display the results
ArrayList<String> results = getAllSongsFromDatabase();
resultsListAdapter = new ArrayAdapter<String>(
this, android.R.layout.simple_list_item_1, results
);
Log.d("fuck", results.toString());
resultsList = (ListView)findViewById(R.id.songs_list_view);
resultsList.setAdapter(resultsListAdapter);
}
private ArrayList<String> getAllSongsFromDatabase()
{
Volley.newRequestQueue(getApplicationContext());
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(
Request.Method.GET, getAllSongsScriptUrl,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response)
{
try
{
JSONArray songs = response.getJSONArray("songs");
for(int i = 0; i < songs.length(); i++)
{
// Get the data from the JSON response
JSONObject song = songs.getJSONObject(i);
String artist = song.getString("artist");
String song_name = song.getString("name");
// Construct a result string and add it to listview
String result =
artist.toString() +
" - " +
song_name.toString();
results.add(result);
// POINT A: Printing results works fine here
}
}
catch(JSONException e)
{
// TODO: Handle
}
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error)
{
// TODO: Handle
}
}
);
requestQueue.add(jsonObjectRequest);
// POINT B, where things start to go pear shaped
return results;
}
If I print the value of results at comment marked POINT A, I get a filled arraylist as expected. However, if I print the results at POINT B, I get an empty arraylist.
Not being one to give up easily, I fiddled with the code, trying to get it to work. I noticed, interestingly, that if I marked results as static, everything seems to work. WHY?
Hi I'm working on CardSwipe functionality like tinder. (For understanding see the attached image)
For this i followed this code, when using static data like bellow, I'm successfully adding items in ListView
private void loadCards2(){
itemsaArrayList = new ArrayList<>();
for(int i = 0; i < 10; i++){
CardSwipeItems items = new CardSwipeItems();
items.setCardImageUrl("http://i.ytimg.com/vi/PnxsTxV8y3g/maxresdefault.jpg");
items.setCardDescription("But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness.");
itemsaArrayList.add(items);
}
flingContainer = (SwipeFlingAdapterView) findViewById(R.id.frame);
myAppAdapter = new CardSwipeAdapter(itemsaArrayList, CardSwipeActivity.this);
flingContainer.setAdapter(myAppAdapter);
initControlls();
}
But when I'm trying to add the items dynamically by using volley means, items are not adding in the ListView. (For this volley
please see the loadCards() method in the CardSwipeActivity)
I tried lot of approaches to load the items in the list view dynamically. For ex: I used Thread (For code see this) and i also used Handler (For code see this) but I'm not able to add the items in ListView dynamically
If any one know the solution for this means please tell me. Thank you.......
Edit
*My method*
private void loadCards(){
String Url = "myApi";
Log.e("Url", Url);
final ProgressDialog dialog = ProgressDialog.show(CardSwipeActivity.this, null, null);
ProgressBar spinner = new android.widget.ProgressBar(CardSwipeActivity.this, null,android.R.attr.progressBarStyle);
spinner.getIndeterminateDrawable().setColorFilter(Color.parseColor("#009689"), android.graphics.PorterDuff.Mode.SRC_IN);
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(android.graphics.Color.TRANSPARENT));
dialog.setContentView(spinner);
dialog.setCancelable(false);
dialog.show();
StringRequest request = new StringRequest(Request.Method.GET, Url, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
dialog.dismiss();
if(response != null && !response.startsWith("<HTML>")){
Log.e("OnResponse", response);
try {
JSONArray jsonArray = new JSONArray(response);
if(jsonArray.length() > 0){
itemsaArrayList = new ArrayList<>();
for(int i = 0; i< jsonArray.length(); i++){
JSONObject singleObj = jsonArray.getJSONObject(i);
String imgId = singleObj.getString("avatar_file_id");
String name = singleObj.getString("name");
CardSwipeItems items = new CardSwipeItems();
Log.e("imgId", imgId);
Log.e("name", name);
items.setCardImageUrl(imgId);
items.setCardDescription(name);
itemsaArrayList.add(items);
}
flingContainer = (SwipeFlingAdapterView) findViewById(R.id.frame);
myAppAdapter = new CardSwipeAdapter(itemsaArrayList, CardSwipeActivity.this);
flingContainer.setAdapter(myAppAdapter);
initControlls();
}
} catch (JSONException e) {
e.printStackTrace();
}
}else{
Log.e("Internet", "Internet");
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
dialog.dismiss();
if(error != null){
Log.e("error", error.toString());
}else{
}
}
}){
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String,String> params = new HashMap<String, String>();
params.put("token","b32daf7b50c7f21dba80dd0651174e3839c22f56");
params.put("user_id","386");
Log.e("Headers", "**********Headers*************");
Log.e("token","b32daf7b50c7f21dba80dd0651174e3839c22f56");
Log.e("user_id","386");
return params;
}
};
RequestQueue queue = Volley.newRequestQueue(CardSwipeActivity.this);
queue.add(request);
queue.getCache().remove(Url);
}
You can add items dynamically to the adapter by addding items to the list you passed originally to adapter like this:
itemsaArrayList.add(items);
than you need to notify the adapter like this from your UI thread:
runOnUiThread(new Runnable() {
public void run() {
adapter.notifyDataSetChanged();
}
});
And to call it on the UI-Thread, use have to use runOnUiThread() of Activity. Then only, notifyDataSetChanged() will work.
Also have a look at this post
If you are getting response from server then try to move below line from onResponse method to onCreate method.
flingContainer = (SwipeFlingAdapterView) findViewById(R.id.frame);
Hope this will work
if (isConnected()) {
Event eInstance = new Event();
theEvents = eInstance.downloadEvents(eventsNightlife, getActivity());
rAdapter = new RecyclerAdapter(theEvents);
recyclerView.setAdapter(rAdapter);
progrsBar.setVisibility(View.GONE);
....
This is part of the code that runs at "onCreateView". The method downloadEvents uses Volley to download JSON data, extract it and return a list of items (theEvents). Now when my app starts, the recycler view is empty. If I go to my home screen out of the app and then run my app again, this time the data sometimes gets downloaded.
I debugged step by step, and at first launch (i mean when the app is not just resuming), theEvents is empty, so the download didn't return or manage to return anything...
Suggestions on how to execute things before the UI has been shown to the user or what actually needs to be done to approach this task better?
Also, I use a swipeRefreshLayout and at its onRefresh method I do:
public void onRefresh() {
Event eInstance = new Event();
theEvents = eInstance.downloadEvents(eventsNightlife, getActivity());
rAdapter.notifyDataSetChanged();
swipeRefreshLayout.setRefreshing(false);
}
but it doesn't work. I also tried to
rAdapter = new RecyclerAdapter(theEvents);
rAdapter.notifyDataSetChanged();
recyclerView.swapAdapter(rAdapter, false);
still not working.
EDIT: My downloadEvents method implementing Volley:
public List<Event> downloadEvents(String urlService, Context context) {
eventsList = new ArrayList<>();
RequestQueue requestQueue = Volley.newRequestQueue(context);
JsonArrayRequest jsonArrayRequest = new JsonArrayRequest
(Request.Method.GET, urlService, null, new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
try {
String durationStr = null;
for (int i = 0; i < response.length(); i++) {
JSONObject eventJson = response.getJSONObject(i);
String title = eventJson.getString("EventTitle");
String body = eventJson.getString("EventBody");
String date = eventJson.getString("EventDate");
String time = eventJson.getString("EventTime");
int duration = Integer.parseInt(eventJson.getString("EventDuration"));
if (duration > 60) {
durationStr = "Duration: " + duration / 60 + " h";
} else if (duration < 60) {
durationStr = "Duration: " + duration + " m";
}
String place = eventJson.getString("EventPlace");
String organ = eventJson.getString("Organization");
Event event = new Event(title, body, date, time, durationStr, place, organ);
eventsList.add(event);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e("VOLLEY ERROR", "" + error);
}
}
);
requestQueue.add(jsonArrayRequest);
return eventsList;
}
You can use EventBus for your purpose that is a simple and truth way.
Here, i write an example for how to use EventBus with volley.
Consider that i want to download some data.
This is the class that my download methods is inside it (you can add more methods to it in the future):
Im used volley to download my data:
// Download methods is inside volley
public class MyDownloader{
public static void downloadData(){
DownloadDataEvent dlDataEvent=new DownloadDataEvent();
List<String> myResult=new ArrayList<>();
...
#Override
public void onResponse(JSONArray response) {
super.onResponse(response);
if(respone!=null){
// Do what i want with my received data
dlDataEvent.setData(response);
}
// Post my event by EventBus
EventBus.getDefault().post(dlDataEvent);
...
}
}
}
This is my event:
public class DownloadDataEvent{
private JSONArray mData;
public void setData(JSONArray data){
mData=data;
}
public JSONArray setData(){
return mData;
}
}
Now i want to use my downloadData() method inside my MainActivity:
(I called my downloadData method inside onCreate.)
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
...
// I have to register this class for EventBus subscriber:
if(!EventBus.getDefault().isRegister(this)){
EventBus.getDefault().registerSticky(this);
}
// Call my downloadData method
if(isConnected()){
MyDownloader.downloadData();
}
}
// And for receive the data through EventBus, i have to create a
// method (subscriber) in this template:
public void onEventMainThread(DownloadDataEvent downloadDataEvent){
JSONArray result=downloadDataEvent.getData();
// Do what i want with my received data
}
}
you can create more than one subscriber every where you want to use received data.
I passed JSONArray to my DownloadDataEvent that it is not good. you can deserialize your received data and pass it to your DownloadDataEvent.
I used Volley to download data
Maybe my descriptions were confusing, but EventBus is a well-known library and is very easy to use.