My app scrolling is super fast!
How can I limit the scroll speed of a scroll view in my android app?
The scroll can be very fast and it's meaningless to scroll in that speed.
This thread is old, but I will reply with a partial solution: limiting the fling velocity. Feel free to comment so I can improve my solution.
As explained in the Developer Training guide:
Flinging is the type of scrolling that occurs when a user drags and lifts her finger quickly.
That's where I needed a velocity limit. So, in the Custom ScrollView (whether horizontal or vertical)
override fling method like this.
#Override
public void fling(int velocityY) {
int topVelocityY = (int) ((Math.min(Math.abs(velocityY), MAX_SCROLL_SPEED) ) * Math.signum(velocityY));
super.fling(topVelocityY);
}
I found that velocityY (in horizontal scrollview, it would be velocityX) could be between -16000 and 16000. Negative just means scrolling back. I'm still testing this values, and I have tested it in only one device. Not sure if it's the same in older devices/API versions. I will come back later to edit this.
(int) ((Math.min(Math.abs(velocityY), MAX_SCROLL_SPEED) ) * Math.signum(velocityY));
What I'm doing there is obtaining the minimum value between my constant MAX_SCROLL_SPEED and original velocityY, then obtaining the sign of the original velocityY. We need the sign to scroll back.
Finally, sending back the modified velocityY.
It's a partial solution, because if the user keeps pressing the scrollview, the speed won't change.
Again, feel free to improve my answer, I'm still learning.
ObjectAnimator anim = ObjectAnimator.ofInt(mScrollView, "scrollY", mScrollView.getBottom());
anim.setDuration(9000);
anim.start();
I think using timer you can limit the speed of scroll. look at this link Android: HorizontalScrollView smoothScroll animation time
This is how I achieved a smooth vertical scroll (like movie credits). This also allows the user to move the scroll up and down and allow it to continue scrolling when they let go. In my XML, I encapsulated my TextView inside of a ScrollView called "scrollView1". Enjoy!
final TextView tv=(TextView)findViewById(R.id.lyrics);
final ScrollView scrollView = (ScrollView) findViewById(R.id.scrollView1);
Button start = (Button) findViewById(R.id.button_start);
Button stop = (Button) findViewById(R.id.button_stop);
final Handler timerHandler = new Handler();
final Runnable timerRunnable = new Runnable() {
#Override
public void run() {
scrollView.smoothScrollBy(0,5); // 5 is how many pixels you want it to scroll vertically by
timerHandler.postDelayed(this, 10); // 10 is how many milliseconds you want this thread to run
}
};
start.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
timerHandler.postDelayed(timerRunnable, 0);
}
});
stop.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
timerHandler.removeCallbacks(timerRunnable);
}
});
Related
I am trying to expand and shrink the width of container with animation.
I am doing both of those with ValueAnimator and both of those are working fine separately.
The expand animation will be perform when the user scroll up the cards and shrink animation will be perform when the user scroll down the cards.
Because all of those cards are in ScrollView container, the user can scroll up / down whenever they want even though the expanding / shrinking Animation is not finished.
To be more clear, it looks like the shrink animation is being triggered when the cards are still expanding. Below is my code for expanding / shrinking.
private void animateResizeWidth(final View vcLogin, int widthReduce){
ValueAnimator anim;
if(widthReduce == 0){
anim = ValueAnimator.ofInt((ScreenUtils.getObjInstance().getPixelFromDPI(CARD_WIDTH) - ScreenUtils.getObjInstance().getPixelFromDPI(widthReduce)), ScreenUtils.getObjInstance().getPixelFromDPI(CARD_WIDTH));
}else{
anim = ValueAnimator.ofInt(ScreenUtils.getObjInstance().getPixelFromDPI(CARD_WIDTH), (ScreenUtils.getObjInstance().getPixelFromDPI(CARD_WIDTH) - ScreenUtils.getObjInstance().getPixelFromDPI(widthReduce)));
}
final RelativeLayout.LayoutParams finalLp2 = new RelativeLayout.LayoutParams(vcLogin.getLayoutParams());
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int val = (Integer) valueAnimator.getAnimatedValue();
finalLp2.width = val;
finalLp2.addRule(RelativeLayout.CENTER_HORIZONTAL);
vcLogin.setLayoutParams(finalLp2);
vcLogin.invalidate();
}
});
anim.setDuration(400);
anim.start();
}
So, whenever that happen, the size of the cards are being set to regular size and the resize animation won't trigger anymore (Need to restart the Host Activity).
I have tried the answer from here. But, it is not helping me.
My Questions are 1: Is there any other Animation Mechanism that
I can use for width animation except ValueAnimator ? I have checked
ObjectAnimator but, not supporting. Ref 2: Is that thread
issue ? 3: I should not be blocking user from scrolling down
just because my expanding animation is not finished. So, what I haven
in mind is if the user scroll down, I will just terminate any of my
pending animations. Is there any thread-related issue that I need to consider for this approach ? (I have written a Custom
ScrollView to detect those scroll-related events.)
I think I am missing a few thread-safe logics in above animation implementation.
Thanks
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.
As I've a master in MS Paint, I will just upload a picture selfdescripting what I'm trying to achieve.
I've searched, but I'm not really sure what do I've to search. I've found something called Animations. I managed to rotate, fade, etc an element from a View (with this great tutorial http://www.vogella.com/articles/AndroidAnimation/article.html)
But this is a bit limited for what I'm trying to achieve, and now, I'm stuck, because I don't know how is this really called in android development. Tried words like "scrollup layouts" but I didn't get any better results.
Can you give me some tips?
Thank you.
You can see a live example, with this app: https://play.google.com/store/apps/details?id=alexcrusher.just6weeks
Sincerely,
Sergi
Use something like this as your layout (Use Linear, Relative or other layout if you wish):
<LinearLayout
android:id="#+id/lty_parent">
<LinearLayout
android:id="#+id/lyt_first" />
<LinearLayout
android:id="#+id/lyt_second"/>
</LinearLayout>
And then in an onClick method on whatever you want to use to control it, set the Visibility between Visible and Gone.
public void buttonClickListener(){
((Button) findViewById(R.id.your_button))
.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (lyt_second.getVisibility() == View.GONE) {
lyt_second.setVisibility(View.VISIBILE);
}
else {
lyt_second.setVisibility(View.GONE);
}
});
Which is fine if you just want a simple appear/disappear with nothing fancy. Things get a little bit more complicated if you want to animate it, as you need to play around with negative margins in order to make it appear to grow and shrink, like so:
We use the same onClick method that we did before, but this time when we click it starts up a custom SlideAnimation for the hidden/visible view.
#Override
public void onClick(View v) {
SlideAnimation slideAnim = new SlideAnimation(lyt_second, time);
lyt_second.startAnimation(slideAnim);
}
The implementation of the SlideAnimation is based on a general Animation class, which we extend and then Override the transformation.
public SlideAnimation(View view, int duration) {
//Set the duration of the animation to the int we passed in
setDuration(duration);
//Set the view to be animated to the view we passed in
viewToBeAnimated = view;
//Get the Margin Parameters for the view so we can edit them
viewMarginParams = (MarginLayoutParams) view.getLayoutParams();
//If the view is VISIBLE, hide it after. If it's GONE, show it before we start.
hideAfter = (view.getVisibility() == View.VISIBLE);
//First off, start the margin at the bottom margin we've already set.
//You need your layout to have a negative margin for this to work correctly.
marginStart = viewMarginParams.bottomMargin;
//Decide if we're expanding or collapsing
if (marginStart == 0){
marginEnd = 0 - view.getHeight();
}
else {
marginEnd = 0;
}
//Make sure the view is visible for our animation
view.setVisibility(View.VISIBLE);
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
if (interpolatedTime < 1.0f) {
// Setting the new bottom margin to the start of the margin
// plus the inbetween bits
viewMarginParams.bottomMargin = marginStart
+ (int) ((marginEnd - marginStart) * interpolatedTime);
// Request the layout as it happens so we can see it redrawing
viewToBeAnimated.requestLayout();
// Make sure we have finished before we mess about with the rest of it
} else if (!alreadyFinished) {
viewMarginParams.bottomMargin = marginEnd;
viewToBeAnimated.requestLayout();
if (hideAfter) {
viewToBeAnimated.setVisibility(View.GONE);
}
alreadyFinished = true;
}
hideAfter = false;
}
}
EDIT: If anyone had used this code before and found that if you click on the button that starts the animation more than once before the animation was finished, it would mess up the animation from then on, causing it to always hide the view after the animation finished. I missed the reset of the hideAfter boolean near the bottom of the code, added it now.
you can do this manually by using setvisibility feature on the event onClick()
or
use this
dynamically adding two views one below other
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.
Would it be possible to hide a view off the top edge of the screen, and only have it appear if the user scrolls upwards?
My first attempt used a scrollview, but it seems that scrollTo() doesn't work unless I used postDelayed (it doesn't even work with Post()). I tried adding it to the scrollview's view tree observer onPreDraw() event and it still doesn't work unless I delay it, so there is an ugly glitch when the activity is first launched.
The second issue is that if the onscreen keyboard is minimized, the view no longer needs to scroll so hiding things by using a scroll offset no longer works. I thought about manipulating the height in code, but this seems pretty hackish.
Is there a better way to do this than by using a scrollview? Alternatively, Does anyone have any tips on the best place to place the scrollTo (the end of onCreate does not work nor the other places I have tried) so I don't need to use postDelayed? That would at least eliminate one glitch.
Thanks!
This is the code I'm using right now, which is the least glitchy but I don't understand why it doesn't work until the third time onPreDraw() is called.
mScrollView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener()
{
#Override
public boolean onPreDraw()
{
final int fieldYStart = mFieldIWantAtTheTop.getTop();
if (mFieldIWantAtTheTopYStart != fieldYStart
|| mScrollView.getScrollY() < 10)
{
mFieldIWantAtTheTopYStart = fieldYStart;
mScrollView.post(new Runnable()
{
#Override
public void run()
{
Log.v("Testing", "scrolling!");
mScrollView.scrollTo(0, mFieldIWantAtTheTopYStart);
Log.v("Testing", "scroll is now=" + mScrollView.getScrollY());
}
});
}
return true;
}
});
I also tried using a custom scrollview as mentioned below, but this does not solve the issue of the graphical glitch:
#Override
public void onMeasure(int measureWidthSpec, int measureHeightSpec) {
super.onMeasure(measureWidthSpec, measureHeightSpec);
Log.v("Testing", "Scrolling");
post(
new Runnable()
{
public void run()
{
scrollTo(0, 100);
Log.v("Testing", "ScrollY = " + getScrollY());
}
});
}
This code works as does the onPreDraw() code above but there is still a glitch when the activity is launched because the activity is first drawn with the scroll at 0.
I haven't tried this, but you may want to create a custom ScrollView and override onMeasure:
ScrollView scroll = new ScrollView(this) {
#Override
public void onMeasure(int measureWidthSpec, int measureHeightSpec) {
super.onMeasure(measureWidthSpec, measureHeightSpec);
scrollTo(...);
}
};
It seems like this would be the earliest point that scrollTo would be valid.
Edit - I found this answer, which apparently worked for the asker. Is this the method you tried?