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".
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...!
I have an activity that lags (quite severely) when it's started. The lag usually lasts for under a second, but it's noticeable and I don't want it to be.
I'm not sure what's causing it to lag, but I need to fix it so that it's smooth as butter when it's loading.
Here is the activity:
public class ProfileActivity extends AppCompatActivity {
private CoordinatorLayout coordinatorLayout;
private RecyclerView recyclerView;
private LinearLayoutManager layoutManager;
private SwipeRefreshLayout swipeRefreshLayout;
private EndlessRecyclerViewScrollListener scrollListener;
private int userId;
private User user;
private List<Object> data = new ArrayList<>();
protected UserAdapter userAdapter;
private TextView username, userNumPosts;
private ImageView userBackground, userColor, userIcon;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_profile);
// Get userId from intent
userId = getIntent().getExtras().getInt("userId");
// Check if the user is in realm db
user = getRealm().where(User.class)
.equalTo("id", userId)
.findFirst();
// Make status bar black
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.parseColor("#000000"));
}
if (getSupportActionBar() != null) {
// Change toolbar color to the user's chosen color
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(Color.parseColor("#" + user.getColor())));
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
}
coordinatorLayout = (CoordinatorLayout) findViewById(R.id.coordinatorLayout);
swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipeRefreshLayout);
swipeRefreshLayout.setRefreshing(true);
username = (TextView) findViewById(R.id.username);
userNumPosts = (TextView) findViewById(R.id.userNumPosts);
userBackground = (ImageView) findViewById(R.id.userBackground);
userColor = (ImageView) findViewById(R.id.userColor);
userIcon = (ImageView) findViewById(R.id.userIcon);
username.setText(user.getUsername());
userNumPosts.setText(user.getNumPosts());
userColor.setBackgroundColor(Color.parseColor("#" + user.getColor()));
// Add the user icon
Glide.with(this)
.load(user.getIcon())
.into(userIcon);
// Add the user background
Glide.with(this)
.load(user.getBackgroundImage())
.into(userBackground);
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
recyclerView.setNestedScrollingEnabled(false);
layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
userAdapter = new UserAdapter(this, data);
recyclerView.setAdapter(userAdapter);
userAdapter.setUserAdapterListener(new UserAdapter.UserAdapterListener() {
#Override
public void onRequestRefresh() {
swipeRefreshLayout.setRefreshing(true);
getUserData(1);
}
});
scrollListener = new EndlessRecyclerViewScrollListener(layoutManager) {
#Override
public void onLoadMore(int page, int totalItemsCount, RecyclerView view) {
getUserData(page);
}
};
recyclerView.addOnScrollListener(scrollListener);
getUserData(1);
}
public void getUserData(final int page) {
ApiInterface apiService = ApiClient.createService(ApiInterface.class, userAuthToken);
Call<BasicResponse> call = apiService.getUserData(userId, page);
call.enqueue(new Callback<BasicResponse>() {
#Override
public void onResponse(Call<BasicResponse> call, Response<BasicResponse> response) {
if (response.isSuccessful()) {
// Show the data
List<Message> messageData = response.body().getData();
data.addAll(messageData);
userAdapter.notifyDataSetChanged();
}
swipeRefreshLayout.setRefreshing(false);
}
#Override
public void onFailure(Call<BasicResponse> call, Throwable t) {
//
}
});
}
}
What could be causing my activity to lag when it's being loaded? Should I be putting all of my main code in onCreate()?
You have too much process in onCreate(). try to move some method to onResume(). In other words, you are blocking your UI with your data processing. Then, in layout, please minimized the use of nested layout. If the layout have to many children, the app will render it slower. Also consider designing/loading your UI before the data. This would allow your user to see something, even if its not complete.
P/s : Consider moving your Data/Process to an AsyncTask. or Thread
I have look for solution too, this is worked for me
overridePendingTransition(0,0);
before starting the lagging activity.
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.
This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 6 years ago.
So I have an AsyncTask class to handle populating my RecyclerView. It works well but when it needs to be refreshed then it crashes with a NullPointerException. I kind of know why but then I can't do it because I am creating a new object and adding it to my list.
This is my code:
#Override
protected void onPostExecute(String s) {
try {
userslist.clear();
JSONArray jsonArray = new JSONArray(s);
for(int i=0; i < jsonArray.length(); i++){
...
UsersData usersData = new UsersData(var1, var2);
userslist.add(UsersData);
}
cAdapter.notifyDataSetChanged(); // Culprit line, despite the list being deleted and added again in the try block
} catch (JSONException e) {
e.printStackTrace();
}
}
I suspect is it something to do with the userslist.
This is my error logs:
java.lang.NullPointerException
at lukazs.newapp.UserInfo$GetUserList.onPostExecute(UserInfo.java:218)
at android.os.AsyncTask.finish(AsyncTask.java:741)
at android.os.AsyncTask.access$600(AsyncTask.java:197)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:654)
at android.os.Handler.dispatchMessage(Handler.java:100)
EDIT: This is the method call where I populate the recyclerview:
public void populateRecyclerList(){
GetUserList getUserList = new GetUserList();
getUserList.execute();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
...
setContentView(R.layout.usersdetails);
populateRecyclerList();
}
This is where cAdapter is initialised, in onCreate method:
RecyclerView.Adapter cAdapter;
ArrayList<UserDetailsProvider> userslist = new ArrayList<UserDetailsProvider>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
recyclerView = (RecyclerView) findViewById(R.id.recycleMusic);
cAdapter = new UserDefinedAdapter(userslist);
recyclerView.setAdapter(cAdapter);
populateRecyclerList();
}
EDIT: I have done something now, but the populateRecyclerList() is crashing in another AsyncTask class on onPostExecute. Basically, I want to repopulate the recyclerView after a user has been added. This is my code for the onPostExecute() method, where I am calling the populateRecyclerList() method:
#Override
protected void onPostExecute(String s) {
populateRecyclerList();
}
Maybe you are not meant to call this method here? But then how would I update the RecyclerView?
Your cAdapter is not initialized when you call cAdapter.notifyDataSetChanged().
EDIT:
Call your populateRecyclerList() after initilize your fields
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
recyclerView = (RecyclerView) findViewById(R.id.recycleMusic);
cAdapter = new UserDefinedAdapter(userslist);
recyclerView.setAdapter(cAdapter);
populateRecyclerList();
}
Hope it helps!
My bad, onPostExecute DOES run on the UI thread... Thanks you guys for correcting...
http://developer.android.com/reference/android/os/AsyncTask.html
"onPostExecute(Result), invoked on the UI thread after the background computation finishes. The result of the background computation is passed to this step as a parameter."
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.");
}