I have a listview wrapped in a SwipeRefreshLayout. The listview renders very smoothly in anytime I update it with new data. But whenever I pull down SwipeRefreshLayout to get new messages,the listview gives me a blink effect which is negative in terms of user experience. I found nothing useful to fix this issue after searching a lot on the internet.Can any one teach me how to resolve this problem.
Below is part of my code:
public void fetchMoreMsgs(){
sizeOfChatLV=chatLVArray.size();
if(lvPos==0){
toast = Toast.makeText(ma,"No more messages",Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER,0,0);
toast.show();
return;
}else {
if(lvPos-fetchStep>=0){
arrayList = chatLVArray.subList(lvPos-fetchStep, lvPos);
msgsList.addAll(0, arrayList);
chatLVArrayAdapter = new ChatLVArrayAdapter(ma, msgsList);
ma.chatListViewIMP2P.setAdapter(chatLVArrayAdapter);
ma.chatListViewIMP2P.setSelection(fetchStep);
lvPos-=fetchStep;
}else {
arrayList = chatLVArray.subList(0, lvPos);
msgsList.addAll(0, arrayList);
chatLVArrayAdapter = new ChatLVArrayAdapter(ma, msgsList);
ma.chatListViewIMP2P.setAdapter(chatLVArrayAdapter);
ma.chatListViewIMP2P.setSelection(lvPos);
lvPos=0;
}
}
}
The fetchMoreMsgs method is called when I pull down the listview(wrapped in a SwipeRefreshLayout).Any help is much appreciated!
Whenever you call setAdapter, it completely destroys every view in the ListView and then creates it anew. This is what causes your blinking effect.
Instead, your chatLVArrayAdapter should have a way to updating the underlying list and then you should call your adapter's notifyDataSetChanged() to tell ListView the data has changed.
Related
I have been working with RecyclerView for a while. I am following lazy loading, so I am showing 10 data on the view each time. If user scroll to the bottom, the page re-load from the TOP! however, I want to stay where it was previously! So, I have tried to use
recyclerView.scrollToPosition(position);
However, this breaks the UI flow!
My second try is using onSaveInstanceState() and onRestoreInstanceState(Bundle state); However, that does not work! My page is re-loads to the top!
Parcelable state = layoutManager.onSaveInstanceState();
layoutManager.onRestoreInstanceState(state);
I have tried every other methods, apparently none is working for me!
Any help would be highly appreciated! Thanks in advance!
Note : Make sure that you are not initialising or calling setAdapter() method each time after updating your dataset. If not, then
You have to update your data list and call notifyDataSetChanged() which will update your adapter from existing position.
Let's say you have stored your data into ArrayList mData;
Your getItemCount() would be
#Override
public int getItemCount() {
if (mData != null && mData.length() > 0)
return mData.size();
return 0;
}
Now create one more method in your adapter which you will called each time whenever you will get new data from server. This method will simply override your dataset and will update your adapter
private void updateDataSet(ArrayList<String> mData){
this.mData = mData;
notifyDataSetChanged();
}
I have done this functionality before 2 days ago
I share my idea with you
Make
Listview lv; //Assume Find view by Id
List<Model> models = new ArrayList();
Now Make an Adapter and assign blank models
CustomAdapter adapter = new CustomeAdapter(context,models);
lv.setAdapter(adapter);
Now when you have new data to load with lazylodaing
do this
models.addAll(newModels); //new ModelList
adapter.notifyDataSetChanged();
Thats it.
This is the default behaviour Recycler View to recycle/resuse views. As in official docs:
https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html
If you are having any view related issues then save your view state in your List<Object> like setting visible item or not per position. And in your onBindViewHolder method as per position show/hide your view.
I use viewpager+fragment+RecyclerView+SwipeRefreshLayout.
as the gif shows when the RecyclerView refreshed, it shows blank and it can be shows after pull up RecyclerView.
Can anyone tell me what's the reason and how to solve the problem?
http://www.sszhe.com/static/images/1.gif
refresh_listview.setHasFixedSize(false);
StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(2, OrientationHelper.VERTICAL);
refresh_listview.setLayoutManager(staggeredGridLayoutManager);
...................
try{
Document doc=Jsoup.parse(htmlstr,GB.site);
Elements es=doc.select(array.get(3));
if(es.size()>=1){
activity.clearOneArray(titleNum);
adapter.clearDate();
activity.convertDateForItem(GB.getPubuItem(es,array), titleNum);
}
}catch(Exception e){
}
adapter=null;
adapter=new Dynamic2Adapter(this, activity, titleNum);
refresh_listview.setAdapter(adapter);
refresh_listview.getAdapter().notifyItemRangeChanged(0, activity.subCategoryItems.get(titleNum).size()-1);
refresh_listview.getLayoutManager().requestLayout();
refresh_listview.requestLayout();
refresh_listview.smoothScrollToPosition(0);
finally, i find out the soultion myself.
in fact when the fragment come back,refresh_listview.getAdapter() will become null.
so it should be a judgment for this.
if null, you should make a new adapter
I have two listview, like listview_1 and listview_2. I wanna refresh the listview_2 while listview_1 is refreshed.
My code like this:
public void updateTwoListView() {
listview_1.getAdapter().notifyDataSetChanged();
listview_2.getAdapter().notifyDataSetChanged();
}
But it don't work, listview_1 can refresh but the listview_2 can't.
And at that moment what I found is that listview_1 was on focus.
And then I tried to set focus to other views before ran the method, both of them didn't refresh. It likes to refresh a listview only if the listview has focus.
What's more I found that when I called the method to refresh, listview_2 didn't, and then I set focus to listview_2, refreshed itself!
So, What all I want to ask is:
How to refresh two listview at one moment in Android?
What's more code:
//init two listview there
public void init() {
listview_1 = (ListView)findViewById(R.id.listView1);
listview_2 = (ListView)findViewById(R.id.listView2);
adapter1 = new MyListViewAdapter(mContext);
adapter2 = new MyListViewAdapter(mContext); //I have tried use different adapter, that also didn't work.
listview_1.setAdapter(adapter1);
listView_2.setAdapter(adapter2);
}
the real code of upside snippet is:
public void updateTwoListView(int currentPosition) {
adapter1.updateCurPos(currentPosition);
adapter2.updateCurPos(currentPosition);
}
and in MyListViewAdapter.java:
public void updateCurPos(int currentPosition) {
mCurrentPos = currentPosition;
notifyDataSetChanged();
}
And I will call method like listViewManager.updateTwoListView(1) outside to refresh.
Any reply is appreciated.
You have called listview_1 twice. Just change one of them to listview_2 as below:
public void updateTwoListView() {
listview_1.getAdapter().notifyDataSetChanged();
listview_2.getAdapter().notifyDataSetChanged();
}
It seems the problem of your code is that you call getAdapter().
Sets the data behind this ListView. The adapter passed to this method may be wrapped by a WrapperListAdapter, depending on the ListView features currently in use. For instance, adding headers and/or footers will cause the adapter to be wrapped.
Source: http://developer.android.com/reference/android/widget/ListView.html#setAdapter(android.widget.ListAdapter)
The solution is save your Adapter as member variable in your activity and call the notifyDataSetChanged()from there.
See more on this question's answer: https://stackoverflow.com/a/31893525/2742759
I am trying to keep my last scrolled position and populate the adapter with new data but same scrolling position. In my scenerio, I have updating expandable listview every 5 second and updating new items very 5 seconds but with last scrolling position left by user.
I have searched a lot about it and found few solutions but still it is behaving strangly each time I set scrolling position.
I am trying to achieve it with this method:
activity.runOnUiThread(new Runnable()
{
#Override public void run() {
state = list.onSaveInstanceState();
if(settings != null) {
if (mode.equals("Now")) {
info_panel = new esInfoListAdapter(activity, data, expanHash, false, esActivity.lastgrpPosition);
}
else {
info_panel = new esInfoListAdapter(Activity.this, data, tchildData, true, lastgrpPosition);
}
}
list.setAdapter(info_panel);
if(state != null)
list.onRestoreInstanceState(state);
info_panel.notifyDataSetChanged();
}
}
});
So this method gets called just before I updating expandable listview. So in short in every five seconds. I am using Parceable object to save list.onSaveInstanceState(); and then use list.onRestoreInstanceState(state); when updating new data, but the issue is sometimes it works and sometimes not? Am I missing some trick here? Thanks for your help.
I have not yet used ExpandableListView yet but I might. I think you should use combination of methods like getFirstVisiblePosition, getLastVisiblePosition, and getExpandableListPosition. I think getting the groupPosition is sufficient for a user. A link I referenced is ...get index of first/last visible group in an ExpandableListView
Android has the transcript mode to allow to automatically scroll a list view to the bottom when new data is added to the adapter.
Can this be somehow reversed so that new items are automatically added at the top of the list ("inverse transcript mode")
Method stackFromBottom seems about right, but does not do the auto-scrolling on input change.
Does anyone have some example code where a list is constantly adding stuff that gets always inserted at the top? Am I on the right track here?
Update
Thanks for the answers, that made me think more. Actually. I want to have new entries to appear at the top, but the screen still show the item the user is looking at. The user should actively scroll to the top to view the new items. So I guess that transcript mode is not what I want.
Hmm, well, if I was going to try this, I'd do something like the following:
List items = new ArrayList();
//some fictitious objectList where we're populating data
for(Object obj : objectList) {
items.add(0, obj);
listAdapter.notifyDataSetChanged();
}
listView.post(new Runnable() {
#Override
public void run() {
listView.smoothScrollToPosition(0);
}
}
I don't know for certain that this will work, but it seems logical. Basically, just make sure to add the item at the beginning of the list (position 0), refresh the list adapter, and scroll to position (0, 0).
instead of this:
items.add(edittext.getText().toString());
adapter.notifyDataSetChanged();
you should try that (works for me):
listview.post(new Runnable() {
#Override
public void run() {
items.add(0, edittext.getText().toString());
adapter.notifyDataSetChanged();
listview.smoothScrollToPosition(0);
}
});
Shouldn't it be enough to just add a smoothScrollToPosition(0) whenever stuff gets added to the ListView? Don't think there's an automatic scroll option.
I spent several hours attempting to accomplish the same thing. Essentially, this acts like a chat app where the user scrolls up to view older messages at the top of the list.
The problem is that, you want to dynamically add another 50 or 100
records to the top but the scrolling should be continuous from where
the prepended items were added.
The moment you do a notifyDataSetChanged, the ListView will automatically position itself at the first item in your data set and NOT at the position that preceded the position where the new items got inserted.
This makes it look like your list just jumped 50 or 100 records. I believe TranscriptMode set to normal is not the solution. The listview needs to function as a normal listview and you need to programmatically scroll to the bottom of the list to simulate the TranscriptMode as it functions under "normal".
Try to use
LinkedList items = new LinkedList<Object>();
//some fictitious objectList where we're populating data
for(Object obj : objectList) {
items.addFirst(obj);
}
listAdapter.notifyDataSetChanged();
This resolves the problem:
...
ListView lv;
ArrayAdapter<String> adapter;
ArrayList<String> aList;
...
lv = (ListView) findViewById(R.id.mylist);
aList = new ArrayList<String>();
adapter = new ArrayAdapter<String>(getApplicationContext(), android.R.layout.simple_spinner_item, aList);
lv.setAdapter(aAdapter);
...
adapter.insert ("Some Text 1", 0);
adapter.insert ("Some Text 2", 0);
adapter.insert ("Some Text 3", 0);
...
you should try that (list position and refresh adapter)
list.add(0,editTextSName.getText().toString());
adapter.notifyDataSetChanged();