I am currently trying to update a RecyclerView once an AsyncTask has completed its operation.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Initializing RecycleView
mRecyclerView = (RecyclerView)findViewById(R.id.list);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
mAdapter = new AdvertAdapter(this.results, R.layout.card_advert, this);
mRecyclerView.setAdapter(mAdapter);
Here is a code snippet from my AsyncTask:
#Override
protected void onPostExecute(String result){
// your stuff
listener.onTaskCompleted(data);
}
In the listener Activity class, I am updating the data set and attempting to notify my custom adapter that the data set has been changed:
#Override
public void onTaskCompleted(ArrayList<Data>results) {
this.results = results;
this.mAdapter.notifyDataSetChanged();
System.out.println("Done.");
}
It seems that notifyDataSetChanged() is not actually doing anything, as the list remains empty even after the final line. From the debugger, I can verify that results does contain the correct results, and the onTaskCompleted is being run on the main thread. How can I update my RecyclerView with the new results?
I think you should add the ArrayList<Data>results to your adapter data.
Something like this:
#Override
public void onTaskCompleted(ArrayList<Data>results) {
//this.results = results;
this.mAdapter.clear();
this.mAdapter.add(results);
//or this.mAdapter.results = results;
this.mAdapter.notifyDataSetChanged();
System.out.println("Done.");
}
Related
I am trying to make a layout with recyclerview something like the video. I made a recyclerview which update list after certain interval but the problem is after data update it scroll to top position automatically. I want to make something like the video. https://youtu.be/omcS-6LeKoo
I have tried with link from SO
RecyclerView scrolls to top position when change the adapter data RecyclerView notifyDataSetChanged scrolls to top position but unable to solve. below is my attempt
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.recyclerViewId);
handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
// Toast.makeText(getApplicationContext(),"Updating",Toast.LENGTH_LONG).show();
listShow();
handler.postDelayed(this,1000);
}
},1000);
}
void listShow(){
retrofitApiCall = RetrofitInstance.getRetrofitInstance().create(RetrofitApiCall.class);
Call<ModelClass_JSONParse> getDetails = retrofitApiCall;
anime = ExtendedAnime.getAll();
getDetails.enqueue(new Callback<ModelClass_JSONParse>() {
#Override
public void onResponse(Call<ModelClass_JSONParse> call,
Response<ModelClass_JSONParse> response) {
Log.v("Res",response.toString());
getWithValues = new HashMap<>();
if (response.isSuccessful()){
list.add(mModelClass_adapter);
}
adapter = new Adapter(getApplicationContext(),list);
StaggeredGridLayoutManager layoutManager = new
StaggeredGridLayoutManager(1,StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
}
#Override
public void onFailure(Call<ModelClass_JSONParse> call, Throwable t) {
Log.v("Res",call.toString());
}
});
}
These lines of code are causing the problem for you. You're setting a new adapter reference and linear layout manager reference every time of your API calling.
adapter = new Adapter(getApplicationContext(),list);
StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(1,StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
To Do your desired task you need to do following steps -
Just set your LayoutManager and adapter for the first time.
Make a setDataList method in your adapter class. And set your updated list to adapter list.
And then every time of calling API set that list to setDataList and call adapter.notifyDataSetChanged() method of your adapter class.
The above steps will solve your problem. Just give it a try.
The problem is probably because of you are setting new adapter reference in network callback method onResponse(). Try setting adapter in onCreate and then update dataset in callback.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.recyclerViewId);
recyclerView.setAdapter(yourAdapter);
}
In network callback,
#Override
public void onResponse(Call<ModelClass_JSONParse> call,
Response<ModelClass_JSONParse> response) {
Log.v("Res",response.toString());
getWithValues = new HashMap<>();
if (response.isSuccessful()){
adapter.setDataSet(newDataList) //not change adapter reference,only update data set
}
}
Implement setDataSet() method in your adapter to update list like below.
class YourAdapter extends RecyclerView.Adapter<>{
priavate List<> list = new ArrayList();
public void setDataSet(newList:List<>){
list.clear();
list.addAll(newList);
notifyDataSetChanged();
}
}
Don't use adapter.notifyDataSetChanged(); method because I think your main view must be wrap content so either set a fixed height like 150dp.
Try different methods like notifyItemChanged(), notifyItemRangeChanged(), notifyItemInserted()
You are setting adapter again and again when the response is changing, so you should change list and set adapter in onCreate.
Arraylist<ModelClass_adapter> list = new Arraylist<ModelClass_adapter>;
Adapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.recyclerViewId);
//set adapter here
adapter = new Adapter(getApplicationContext(),list);
StaggeredGridLayoutManager layoutManager = new
StaggeredGridLayoutManager(1,StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
// Toast.makeText(getApplicationContext(),"Updating",Toast.LENGTH_LONG).show();
listShow();
handler.postDelayed(this,1000);
}
},1000);
}
void listShow(){
retrofitApiCall = RetrofitInstance.getRetrofitInstance().create(RetrofitApiCall.class);
Call<ModelClass_JSONParse> getDetails = retrofitApiCall;
anime = ExtendedAnime.getAll();
getDetails.enqueue(new Callback<ModelClass_JSONParse>() {
#Override
public void onResponse(Call<ModelClass_JSONParse> call,
Response<ModelClass_JSONParse> response) {
Log.v("Res",response.toString());
getWithValues = new HashMap<>();
if (response.isSuccessful()){
list.clear();
list.add(mModelClass_adapter);
}
adapter.notifyDataSetChanged();
}
}
#Override
public void onFailure(Call<CurrencyModelClass_JSONParse> call, Throwable t) {
Log.v("Res",call.toString());
}
});
}
You are setting a new adapter every time and a new layout manager response comes.
which may cause this type of problem. you need to set adapter and layout manager in onCreate. just update adapter list in response of the api.
according to this answer you need linear layout manager only.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.recyclerViewId);
list= ArrayList<>();
adapter = new Adapter(getApplicationContext(),list);
LinearLayoutManager linearLayoutManager = new
LinearLayoutManager(context, OrientationHelper.VERTICAL, false);
recycleView.setLayoutManager(linearLayoutManager);
recyclerView.setAdapter(adapter);
handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
// Toast.makeText(getApplicationContext(),"Updating",Toast.LENGTH_LONG).show();
listShow();
handler.postDelayed(this,1000);
}
},1000);
}
void listShow(){
retrofitApiCall = RetrofitInstance.getRetrofitInstance().create(RetrofitApiCall.class);
Call<ModelClass_JSONParse> getDetails = retrofitApiCall;
anime = ExtendedAnime.getAll();
getDetails.enqueue(new Callback<ModelClass_JSONParse>() {
#Override
public void onResponse(Call<ModelClass_JSONParse> call,
Response<ModelClass_JSONParse> response) {
Log.v("Res",response.toString());
getWithValues = new HashMap<>();
if (response.isSuccessful()){
adapter.getList().add(mModelClass_adapter);
}
adapter.notifyDataSetChanged();
}
}
#Override
public void onFailure(Call<CurrencyModelClass_JSONParse> call, Throwable t) {
Log.v("Res",call.toString());
}
});
}
you can do by following way
First get the count of your current datalist
int position = datalist.size();
after adding data into datalist
call DataAdapter.notifyDataSetChanged();
then move cursor to position in recyclerview
recyclerView.scrollToPosition(position);
Happy coding...!
This question already has answers here:
recyclerview No adapter attached; skipping layout
(38 answers)
Closed 4 years ago.
I have a simple program that connect to an API and get some data from it.I use Retrofit library.There are two activity. MainActivity includes a RecyclerView that show data received from the server. If press any item,application go to UserInfoActivity and show detailed information of that item.My app when go UserInfoActivity crashed after a few seconds and don't show information. However response correctly received but the program gives an error
on txtId.setText(user.getId) line. The error is E/RecyclerView: No adapter attached; skipping layout
In fact my problem starts when call Call<User> getUserInfo(#Path("id") int id); in second Activity.
This is MainActivity code.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
userRequest();
}
private void userRequest(){
APIInterface apiInterface= APIClient.getClient().create(APIInterface.class);
retrofit2.Call<ArrayList<User>> call= apiInterface.getUsers();
call.enqueue(new Callback<ArrayList<User>>() {
#Override
public void onResponse(retrofit2.Call<ArrayList<User>> call, Response<ArrayList<User>> response) {
if(response.isSuccessful()){
ArrayList<User> users = response.body();
setupRecycelerView(users);
}
}
#Override
public void onFailure(retrofit2.Call<ArrayList<User>> call, Throwable t) {
Log.i("RETROFIT","response not successful");
}
});
}
private void setupRecycelerView(ArrayList<User> users){
recyclerView=findViewById(R.id.RcyView);
adapter=new RecycelerAdapter(this,users);
linearLayoutManager=new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false);
recyclerView.setAdapter(adapter);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
}
This is onBindViewHolder function of RecycelerAdapter.
public void onBindViewHolder(#NonNull UserViewHolder holder, int position) {
final User user=values.get(position);
holder.txtName.setText(user.getName());
holder.txtPhone.setText(user.getPhone());
final Intent intent=new Intent(context, UserInfoActivity.class);
intent.putExtra("user",user);
holder.root.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
context.startActivity(intent);
}
});
}
This is UserInfoActivity code.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_user_info);
txtId=findViewById(R.id.txtId);
txtName=findViewById(R.id.txtName);
txtPhone=findViewById(R.id.txtPhone);
Intent intent=getIntent();
User user=(User)intent.getSerializableExtra("user");
id=user.getId();
userInfoRequest();
}
private void userInfoRequest(){
APIInterface apiInterface= APIClient.getClient().create(APIInterface.class);
retrofit2.Call<User> call= apiInterface.getUserInfo(id);
call.enqueue(new Callback<User>() {
#Override
public void onResponse(Call<User> call, Response<User> response) {
if(response.isSuccessful()){
User user=response.body();
Log.e("MF","ON Response");
Log.e("MF",user.getName());
Log.e("MF",user.getPhone());
txtId.setText(user.getId());
txtName.setText(user.getName());
txtPhone.setText(user.getPhone());
}
}
#Override
public void onFailure(Call<User> call, Throwable t) {
Log.e("MF","on failure");
}
});
}
Try this:
linearLayoutManager=new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false);
recyclerView.setLayoutManager(linearLayoutManager);
adapter=new RecycelerAdapter(this,users);
recyclerView.setAdapter(adapter);
adapter.notifyDataSetChanged();
recyclerView.setHasFixedSize(true);
recyclerView.setItemAnimator(new DefaultItemAnimator());
Used notifyDataSetChanged() to tell the RecyclerView that data changed-added and set the Adapter after setLayoutManager.
If this didn't help, initialize the RecyclerView inside onCreate method and set the Adapter in onResponse with runOnUiThread like following:
runOnUiThread(new Runnable() {
#Override
public void run() {
//Do something on UiThread
ArrayList<User> users = response.body();
adapter=new RecycelerAdapter(this,users);
recyclerView.setAdapter(users);
}
});
Then call notifyDataSetChanged() and it should work then.
You're calling the setupRecycelerView on the response of the Retrofit api call, the problem is that till the response comes the RecyclerView doesnt have an adapter or a layoutManager attached to itself, hence the error.
Instead, you can call the setupRecycelerView inside onCreate itself and then create a global object of the Users list. Something like this:
private final ArrayList<Users> users = new ArrayList();
This list would be empty and the adapter would be initialised with the same empty list, then when you get the response from the retrofit api, use:
users.addAll(response.getBody());
this will fill the contents and then call adapter.notifyDataSetChanged(); to make the adapter reflect the updated contents.
This happens because you initialize RecyclerView when/if the response from server is successful.
Initialize it (call setupRecycelerView() ) in onCreate(). And change adapter=new RecycelerAdapter(this,users); to adapter=new RecycelerAdapter(this);
Make your RecycelerAdapter's constructor take one argument only (probably Context)
In your adapter, make a public method that sets the users.
public setUserData(ArrayList<User> users) {
this.values = users;
notifyDataSetChanged();
}
Call RecycelerAdapter.setUserData(users); from Retrofit's response is successful.
Just call setAdapter() after setLayoutManager() in your setupRecycelerView()
I've read so many posts here in Stackoverflow about this problem... I implemented all the solution that I've seen, but none of them worked.
What is happening: the notifyDataSetChanged doesn't work when opening the app. If I change orientation or change the sort (popular to top rated, or vise-versa), it works. I debbuged the code and the data arrives correctly in the Adapter, but the notify doesn't work, so the interface doesn't got updated.
Can anyone help me, please?
My onCreate method:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Resources rs = getResources();
int numColumns = rs.getInteger(R.integer.list_columns);
mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view_popular_movies);
mErrorMessageDisplay = (TextView)findViewById(R.id.tv_error_message_display);
mLoadingIndicator = (ProgressBar) findViewById(R.id.pb_loading_indicator);
//Resource based on https://discussions.udacity.com/t/is-there-a-way-to-fit-columns-in-gridlayoutmanager/221936/4
GridLayoutManager layoutManager = new GridLayoutManager(this, numColumns);
mRecyclerView.setLayoutManager(layoutManager);
mRecyclerView.setHasFixedSize(true);
mPopularMoviesAdapter = new PopularMoviesAdapter(this, this);
mRecyclerView.setAdapter(mPopularMoviesAdapter);
loadMoviesData(NetworkUtils.POPULAR_SORT);
}
The loadMoviesData method simply calls the execute() from AsyncTask.
My onPostExecute code:
#Override
protected void onPostExecute(String result) {
Log.v(TAG, "onPostExecute");
mLoadingIndicator.setVisibility(View.INVISIBLE);
if(result != null){
showMoviesDataView();
Gson gson = new Gson();
Movies data = gson.fromJson(result, Movies.class);
mPopularMoviesAdapter.setMovieList(data);
} else {
showErrorMessage();
}
}
My setMovieList method:
public void setMovieList(Movies movieList) {
this.movieList = movieList;
this.notifyDataSetChanged();
}
My full code: https://github.com/guuilp/PopularMovies
I had a look at your code and your notifyDataSetChanged() is working, the problem is that your RecyclerView has no height.
Change it to android:layout_height="match_parent".
I am trying to set the adapter of a RecyclerView within the onCreate(). I read that the notifyDatasetChanged() can be invoked on the main thread only. But how can this be achieved? Here is my code so far:
RecyclerView recyclerViewSt;
List<GitHubstarredRepos> myDataSource = new ArrayList<>();
RecyclerView.Adapter myAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
recyclerViewSt = (RecyclerView) findViewById(R.id.starred_repos_recycler_view);
recyclerViewSt.setLayoutManager(new LinearLayoutManager(this));
myAdapter = new StarredReposAdapter(myDataSource, R.layout.list_item_starred_repo,
getApplicationContext());
recyclerViewSt.setAdapter(myAdapter);
}
public void loadStarredRepos (View view){
GitHubStarredRepoAPI apiService =
ApiClient.getClient().create(GitHubStarredRepoAPI.class);
Call<List<GitHubstarredRepos>> call = apiService.getStarredRepoName(newString);
call.enqueue(new Callback<List<GitHubstarredRepos>>() {
#Override
public void onResponse(Call<List<GitHubstarredRepos>> call, Response<List<GitHubstarredRepos>>
response) {
myDataSource.clear();
myDataSource.addAll(response.body());
recyclerViewSt.notify();
}
Thank you!
instead of notifying RecyclerView, Notify your Adapter.
I have read some similar questions and and i have tried to get it work but i can't understand why my adapter is not updating my recyclerview after return from my SaveItem activity. So i have two tabs: ALL notes and Favourite notes. To be more specific: App image
.
When i press the floating action button from the bottom it starts a new activity where i can record a new note, but when i return to MainActivity the first fragment it's not updating my recyclerview:
Here is where I fill my adapter:
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
adapter = new NoteAdapter(fillAdapter(),this,getActivity());
recycler.setAdapter(adapter);
recycler.setLayoutManager(new LinearLayoutManager(getActivity(),LinearLayoutManager.VERTICAL,false));
}
Here is where I try to update my recyclerview:
#Override
public void onResume() {
super.onResume();
adapter.addAll(fillAdapter());
recycler.setAdapter(adapter);
}
Here is my addAll method:
public class NoteAdapter extends RecyclerView.Adapter<NoteAdapter.ViewHolderNote> {
// Other codes....
public void addAll(List<Note> newNotes) {
this.notes.clear();
this.notes.addAll(newNotes);
this.notifyDataSetChanged();
}
}
Take a list of Note in your Fragment class.
private List<Note> allNotes;
Then inside your onActivityCreated
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
allNotes = fillAdapter();
adapter = new NoteAdapter(allNotes, this, getActivity());
recycler.setAdapter(adapter);
recycler.setLayoutManager(new LinearLayoutManager(getActivity(),LinearLayoutManager.VERTICAL,false));
}
Now to update your list call the fillAdapter function again to popuate the allNotes list again and modify your onResume like this.
#Override
public void onResume() {
super.onResume();
allNotes = fillAdapter();
adapter.notifyDatasetChanged();
}
The addAll function has no necessity right now. Remove the function from your NoteAdapter.
The key thing is the reference of your list which is being passed to the adapter.