I have been working on a ListViewidea where it keeps scrolling automatically with no user interaction and that is absolutely doable using the android APIs for instance smoothScrollToPositionFromTop.
I have implemented ListView BaseAdapter where it load items forever (almost) to get a non stopping self repeated ListView.
What I want to achieve here is to keep myListViewscrolling forever with certain speed (slow) to make items clear and readable while scrolling down, I not sure yet if ListView is my best choice here.
below is a snippet of what I am trying to do. the result is good somehow but it's not smooth enough, I can feel the ListView flickers.
I need to improve smoothness, efficiency and control the speed
new Thread(new Runnable() {
#Override
public void run() {
int listViewSize = mListView.getAdapter().getCount();
for (int index = 0; index < listViewSize ; index++) {
mListView.smoothScrollToPositionFromTop(mListViewA.getLastVisiblePosition() + 100, 0, 6000);
try {
// it helps scrolling to stay smooth as possible (by experiment)
Thread.sleep(60);
} catch (InterruptedException e) {
}
}
}
}).start();
I suggest, thath your adapter implemented in effective way.
so this code is just scrolls listview
you need to try another values of variables
final long totalScrollTime = Long.MAX_VALUE; //total scroll time. I think that 300 000 000 years is close enouth to infinity. if not enought you can restart timer in onFinish()
final int scrollPeriod = 20; // every 20 ms scoll will happened. smaller values for smoother
final int heightToScroll = 20; // will be scrolled to 20 px every time. smaller values for smoother scrolling
listView.post(new Runnable() {
#Override
public void run() {
new CountDownTimer(totalScrollTime, scrollPeriod ) {
public void onTick(long millisUntilFinished) {
listView.scrollBy(0, heightToScroll);
}
public void onFinish() {
//you can add code for restarting timer here
}
}.start();
}
});
Here a few pointers : Simulate onFling() programmatically instead of detecting it (Android)
and Programmatically Fling ListView Android
It's hard to figure out what you call smooth enough in your case. Usually smoothness problems are related to a non optimal usage of listviews and troubles in either cell layouts or view creation / recycling inside the getView method of adapters.
Do you use a placeholder ?
An important thing to consider is also Drawables usage.
I never achieved what you are looking for, but a simple idea that comes to mind is :
find a way to scroll the view of 1 position or 2.
use a ring buffer inside your adapter. For instance let's say you got 100 items in your list of items. Then at the beginning, item 0 of the listview is item 0 of your list. When listview is scrolled up of 1 item, then item 0 of listview should become item 1 in your list. Thus the problem would not be scrolling but more syncing with scrolling and displaying an endless list of items.
Related
Background:
I'm developing a tournament management software and I want to show the current contenders classification on a screen that is feed by an android device.
The number of contenders exceeds the vertical size of the screen, so I would like to show it using a nice vertical animate smoth scroll from bottom to top that would be endless (when the list shows the last element it would start showing the first as it would be a loop.
Approach:
After some research (sorry I don't have the all the references) I come up with the following implementation:
I'm using a recyclerview with an adapter on a fragment. In order to automatically scroll it up I have added a timer that would scroll the recycleview on fixed itervals:
CountUpTimer recyclerViewAutoScroll = new CountUpTimer(1) {
public void onTick(long millis) {
recyclerView.scrollBy(0, mSpeed * 2);
if (!recyclerView.canScrollVertically(1)) {
recyclerView.scrollToPosition(0);
}
}
};
This timer is being created and launched on a global layout listener:
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
layoutListener = () -> {
recyclerView.post(() -> recyclerViewAutoScroll.start());
recyclerView.getViewTreeObserver().addOnGlobalLayoutListener(layoutListener);
}
The CountUpTimer class is just an "infinite" timer implemented using an android CountDowntimer.
With this piece of code I can scroll up at regular times, the problem is that is not smoth at all. There are two parameters to play with: the timer tick time, and the amount of vertical scroll that is made each time the timer "ticks". The only way to make it "smooth" is to reducing so much the scroll movement that it ended being very very very slow for the user.
Just for completeness, to achieve the endless scroll efect I added an scroll listener to the recycler view:
recyclerView.addOnScrollListener(new EndlessRecyclerOnScrollListener(5) {
#Override
public void onLoadMore(int currentPage) {
Timber.d("new Page " + currentPage);
recyclerView.post(() ->
{
itemAdapter.addModel(currentClassificationList);
if (currentPage > 2)
itemAdapter.removeModelRange(0, currentClassificationList.size());
});
}
What I do there is just add twice the classificationList to have enough space on the bottom to show the top of the list after the last element, and I keep adding the list to the bottom and removing from the top every time the recyclerview is about to reach the end of the list.
So my question is: How can I implement a really smoth scroll animation of the recyclerview? As I said this is being shown WITHOUT any user interaction, just on a screen where the classification is continuously being shown and scrolled.
UPDATE
After one comment suggestion I substituted the count timer by a TimeAnimator class this way:
timeAnimator.setTimeListener(new TimeAnimator.TimeListener() {
#Override
public void onTimeUpdate(TimeAnimator timeAnimator, long l, long l1) {
Timber.d("elapsed: " + l1);
recyclerView.scrollBy(0, (int)(l1/ mSpeed));
if (!recyclerView.canScrollVertically(1)) {
recyclerView.scrollToPosition(0);
}
}
});
Also I removed the post calls as they are not needed.
Now the smoothness has improved, but is not perfect, it still hessitate a bit when a new item is about to appear from the bottom.
I'm looking now to the itemadapter to improve any calculation made there and any image loading (I added Picasso for image loading also) But still it is not perfect, they are some flickering there.
I basically have to smooth scroll a listview and update a row at the same time.
I do it with a simple approach for now:
mListViewWeeks.post(new Runnable() {
#Override
public void run() {
// update the row concerned
updateItemAtPosition(rowIndex);
int duration = 200;
mListView.smoothScrollToPositionFromTop(rowIndex, 0, duration);
}
});
with the updateItemAtPosition() function:
private void updateItemAtPosition(int position) {
int visiblePosition = mListView.getFirstVisiblePosition();
View view = mListView.getChildAt(position - visiblePosition);
mListView.getAdapter().getView(position, view, mListView);
}
It's working well at a reasonable scroll speed, but when going faster (calling the first block above at a high rate) it can get a bit laggy. Is there anything that I can do to improve updating a row while scrolling smoothly?
You should'nt acces the iew dirrectly like this. Instead you should update your model object displayed by the list and call notifyDataSetChanged() in your adapter.
Well, I'm really late in the game. It looks like one of the reason why RecyclerView was introduced. I'm gonna try to use this component from now on.
A combination of layoutManager.scrollToPosition(position);
and adapter.notifyItemChanged(position); does the job. Everything runs smoothly!!
I want to make a gridView (or ListView) scrolling automatically (without user interaction) repeatly.
I want it on the background, the user has not the possibility to scroll the gridView, he has only one button in foreground to start an activity. It is just a "presentation" activity
How can I make it possible? I have no idea what to use to do it, if there is somes simple android api to make it.
Should I use animation thread or can it be done only with smoothToScroll?
Use duration
mListView.smoothScrollToPositionFromTop(position, duration);
You have the smoothScrollToPosition method that will scroll to a position on the listview/gridview
http://developer.android.com/reference/android/widget/AbsListView.html#smoothScrollToPosition(int)
As for the possibility to scroll on the gridview/listview just implement a touchListener and return true
For example if you want to to this slowly you can't just create a for. It will scroll too fast. You can use handlers for that. Create a recursive function scrollTo.
public void scrollTo(final ListView myView, final int position) {
final Handler myHandler = new Handler();
myHandler.postDelayed(new Runnable() {
#Override
public void run() {
myView.smoothScrollToPosition(position);
if(position<myView.getAdapter().getCount())
scrollTo(myView, position + 1);
}
}, 2000);
}
And next call it once like scrollTo(myListview,0);. The function will do the rest. Change 2000 for the number of seconds you want to wait *1000.
I have a ListView Object and use:
Timer timer=new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#SuppressLint("NewApi")
#Override
public void run() {
lv1.scrollBy(0, counter_automatic);
counter_automatic++;
}
});
}
}, 0, 100);
code work correctly and don't get any Error! But I want Scrolling to go till end of listView!
My listView has 286 item! but it goes only for 3 items and after 3 items scrolling continue but items don't appear!
What's the problem?
The scrollBy() method is a member of the View class, and isn't going to work as you're expecting for a ListView because of how it handles its child Views. Depending on what exactly the desired behavior is, you probably want to use smoothScrollToPosition(int position) or smoothScrollBy(int distance, int duration).
The scrollBy method takes pixels as parameters. I suppose you mix up of number of items with amount of pixels.
If you want to smooth scroll to a certain item use smoothScrollToPosition instead. See http://developer.android.com/reference/android/widget/AbsListView.html#smoothScrollToPosition(int)
scrollBy() scrolls by an amount of pixel, and not from item to item. The documentation of scrollBy() says:
public void scrollBy (int x, int y)
Move the scrolled position of your view. This will cause a call to onScrollChanged(int, int, int,
int) and the view will be invalidated.
Parameters
x - the amount of pixels to scroll by horizontally
y - the amount of pixels to scroll by vertically
So what you want to do is something like this:
listView.scrollTo(0, listView.getHeight());
Using scrollTo() instead of scrollBy() should be more reliable for your use-case.
But I guess the method you are actually looking for is smoothScrollToPosition()! Try something like this:
listView.smoothScrollToPosition(adapter.getCount() - 1);
Instead of using scrollBy, try using setSelectionFromTop(int position, int pixelsFromTop). In your case, use counter_automatic as position and let the pixelsFromTop variable not change.
I have a listview with 200 items. I use a custom view for each row. There is a code that takes some time to calculate, and because of this the list hangs out on scrolling and loads in slow (2-3sec).
I have subclassed SimpleCursorAdapter, and using Filterable and SectionIndexer.
I have in mind to show initially the name of the record, and put in a thread the calculation, and will show up later when it's done.
How do I push back some work, and later update the listview to include the calculated data? This should show up on fly without user interaction.
Since android the Android UI is singlethreaded it will be blocked whenever you perform a long operation.
You could do regular java threading and have a Handler() in the main UI class that you can feed back information into. But..
The simplest way would be to create an AsyncTask(). They are java threads with a slight change. They can perform a UI operation before/after the main calculation. So for example:
private class AndroidThread extends AsyncTask {
protected Object doInBackground(Object... params) {
return result;
}
protected void onPostExecute(Object result) {
// do things here with the result of the doInBackground function
// Like: "UiElement"."performTask(result)"
}
}
For more information on AsyncTask see this
Hmm, interesting question. Here are some quick thoughts:
1) Put the values which can be retrieved directly, i.e. the name of the record as you put it, straight into the list. Leave default values, such as "Loading...", for everything that might take a while to compute.
2) Create a second thread to do the calculations, but to make sure it doesn't calculate everything use the ListView's onScrollStateChanged listener. This lets you only do the background work when the list is either idle, or being scrolled slowly by touch-scroll. In other words, don't bother when it's in 'fling' mode since the rows would be long past before the calculations finished.
3) I'm not entirely sure how you can enqueue the loading stage in the thread. You only want to have one loading thread - opening a new thread for each row would be rather messy and also pointless as the phones aren't multicore - but that means the thread has to have a queueing system. What I mean is that the thread has to be aware of what rows are waiting for calculation, but I haven't thought of how you'd do that yet. However, this would also have to be tied to which rows are visible. There's no point in the thread calculating information for rows long past in the list.
4) Whenever the loading thread finishes a particular row, you could call notifyDatasetChanged() on the list adapter. This might get a bit heavy if the ListView tries to refresh its data for every row rather than just those visible, but you'd have to experiment to see.
I hope these ideas help a bit!
In response to the comment: Assuming you're asking when a row is visible, you can do that by implementing ListView.OnScrollListener. I took inspiration from ApiDemos List13, but here are the relevant bits of code:
public class YourClass extends ListActivity implements ListView.OnScrollListener {
public void onCreate(Bundle sIS){
(...)
// Note, you may have to write your own list adapter, extending BaseAdapter
// See List13 in Api Demos for an example if you're not sure how to do that
setListAdapter(yourImageAdapter);
getListView().setOnScrollListener(this);
}
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
int totalItemCount) {
Log.d(LOG_TAG, "onScroll, firstVisibleItem = " + firstVisibleItem +
", visibleItemCount = " + visibleItemCount
+ ", totalItemCount = " + totalItemCount);
}
public void onScrollStateChanged(AbsListView view, int scrollState) {
Log.d(LOG_TAG, "onScrollStateChanged called, scrollState = " + scrollState);
switch (scrollState) {
case OnScrollListener.SCROLL_STATE_IDLE:
int first = view.getFirstVisiblePosition();
int count = view.getChildCount();
Log.d(LOG_TAG, "first = " + first + ", count = " + count);
break;
case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
//Scrolling with the finger touching the screen
break;
case OnScrollListener.SCROLL_STATE_FLING:
// User 'threw' the list up or down and it's scrolling rapidly
break;
}
}
}
That tells you everything about your list: whether it's scrolling, what's visible, etc. If you use that with the loading thread to only do the background work for the visible rows, that should help reduce unnecessary overhead.
Another point about BaseAdapter: I haven't tried using any other form of list adapter so maybe it's the same with all of them, but one very useful thing with BaseAdapter is that it lets you handle the 'inflation' of each row individually. Every time the list is about to display a new row, getView is called. You can put custom code in there to do things like put "Loading..." if the information hasn't been calculated yet, or actually display it if it has.
By the way, what are you actually calculating in this list that takes so long?