I have a problem with viewPager. I want to swipe pages automaticcly. I use handler to do that.
private Handler h = new Handler();
private Runnable animateViewPager = new Runnable() {
public void run() {
if (!pagerMoved) {
if (ticker == 3) {
h.removeCallbacks(animateViewPager);
h.postDelayed(startLogin, ANIM_VIEWPAGER_DELAY);
}else{
h.postDelayed(animateViewPager, ANIM_VIEWPAGER_DELAY);
}
viewPager.setCurrentItem(ticker % adapter.getCount(), true);
ticker++;
}
}
};
but when I change page this is too fast. I don't see animation like when you swipe by finger. I want to use fakeDrag to simulate swipe by I don't have idea how can I do that to take 2 seconds for example. Any ideas how can I simulate smooth slide page?
swipe pages automaticcly with slideshow using fade-in/out animation to switch between views
Ex: Using ViewPager for Screen Slides
for slowing down the swipe speed you need make custom ViewPager/Scroller.
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 want to animate the change of my RecyclerViews GridLayoutManager. I defaulty show a list of items in a grid with 3 columns and the user can select to show more or less columns.
I would like the views in the RecyclerView to move/scale to their new positions, but I have no idea how this could be done.
What I want in the end
allow to scale the grid via an expand/contract touch gesture => I know how to do that
animate the change of the LayoutManager
Does anyone know how I can animate the change of the LayoutManager?
The source of inspiration here would be the Google Photos app,the stock Sony Gallery app
There are basically 2 approaches you can go with:
You modify the spancount of the GridLayoutManager using setSpanCount(int)
You set a very high span count(~100) use the SpanSizeLookUp to change the per item spanSize on the fly.
I have used the Gist provided by Musenkishi,for this answer to provide an animator to animate the changes in grid layout changes
I have used this approach in a sample GitHub project implementing the same.
Caveats:
I have currently used the click listener to keep modifying the the span size look up.This could be changed to a ItemGestureListener to capture pinch zoom events and change accordingly.
You need to determine a way to choose a span count so that all the items in a row occupy the entire screen width (and hence you do not see any empty space)
You call notifyItemRangeChanged using a runnable post delayed since you cannot call the notifyChanged methods from within bindView/createView etc.
After changing the span size,you need to notifyItemRangeChanged with an appropriate range so that all the items currently displayed on the screen are shifted accordingly.I have used (code at the bottom)
This is not a complete solution but a 2 hour solution for the same.You can obviously improve on all the points mentioned :).
I hope to keep updating the sample since this kind of views have always fascinated me.
Do not view this as the final solution but just a particular way of achieving this approach. If you were to use a StaggerredLayoutManager instead,you could easily avoid blank spaces between items.
public int calculateRange() {
int start = ((GridLayoutManager) grv.getLayoutManager()).findFirstVisibleItemPosition();
int end = ((GridLayoutManager) grv.getLayoutManager()).findLastVisibleItemPosition();
if (start < 0)
start = 0;
if (end < 0)
end = getItemCount();
return end - start;
}
I deal with the same problem as you, and so far I have not found a good solution.
Simple change of columns number in GridLayoutManager seems weird so for now I use animation to fade out/in entire layout. Something like this:
private void animateRecyclerLayoutChange(final int layoutSpanCount) {
Animation fadeOut = new AlphaAnimation(1, 0);
fadeOut.setInterpolator(new DecelerateInterpolator());
fadeOut.setDuration(400);
fadeOut.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationRepeat(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
productsRecyclerLayoutManager.setSpanCount(layoutSpanCount);
productsRecyclerLayoutManager.requestLayout();
Animation fadeIn = new AlphaAnimation(0, 1);
fadeIn.setInterpolator(new AccelerateInterpolator());
fadeIn.setDuration(400);
productsRecycler.startAnimation(fadeIn);
}
});
productsRecycler.startAnimation(fadeOut);
}
If you combine fade out/in animation with scaling each visible item, It will be a decent animation for GridLayoutManager changes.
You can do this with "gesture detector"
see the sample tutorial here http://wiki.workassis.com/pinch-zoom-in-recycler-view/ In this tutorial we will fetch images from gallery and show them in a grid layout in recycler view. You will be able to change layout on pinch gesture. Following are the screen shots of different layouts.
mScaleGestureDetector = new ScaleGestureDetector(this, new ScaleGestureDetector.SimpleOnScaleGestureListener() {
#Override
public boolean onScale(ScaleGestureDetector detector) {
if (detector.getCurrentSpan() > 200 && detector.getTimeDelta() > 200) {
if (detector.getCurrentSpan() - detector.getPreviousSpan() < -1) {
if (mCurrentLayoutManager == mGridLayoutManager1) {
mCurrentLayoutManager = mGridLayoutManager2;
mRvPhotos.setLayoutManager(mGridLayoutManager2);
return true;
} else if (mCurrentLayoutManager == mGridLayoutManager2) {
mCurrentLayoutManager = mGridLayoutManager3;
mRvPhotos.setLayoutManager(mGridLayoutManager3);
return true;
}
} else if(detector.getCurrentSpan() - detector.getPreviousSpan() > 1) {
if (mCurrentLayoutManager == mGridLayoutManager3) {
mCurrentLayoutManager = mGridLayoutManager2;
mRvPhotos.setLayoutManager(mGridLayoutManager2);
return true;
} else if (mCurrentLayoutManager == mGridLayoutManager2) {
mCurrentLayoutManager = mGridLayoutManager1;
mRvPhotos.setLayoutManager(mGridLayoutManager1);
return true;
}
}
}
return false;
}
});
I have a ViewPager with the PageTransformer implementation that does some funky stuff to the next page of the ViewPager. My implementation looks like this:
class ZoomOutPageTransformer implements ViewPager.PageTransformer {
private static final float MIN_SCALE = 0.85f;
private static final float MIN_ALPHA = 0.5f;
#Override
public void transformPage(View view, float position) {
int pageWidth = view.getWidth();
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.setAlpha(0);
} else if (position <= 0) { // [-1,0]
// Use the default slide transition when moving to the left page
} else if (position < 1) { // (0,1]
do some funky stuff to the page currently being scrolled into the main view on scrolling left
} else if (position==1) {
do some funky stuff to the next page
}
else { // (1,+Infinity]
// This page is way off-screen to the right.
view.setAlpha(1);
}
}
}
The problem is that these transform animations are available as soon as the pager is loaded, but they're not available to the first fragment of my viewpager. I.e. the first time a fragment is loaded, the transformPage method is not being called. Is there a way to call the transformPage method manually? I.e. when I'm setting up my ViewPager inside onCreateView? Or somewhere else?
I had a similar situation and I ended up using a fake drag to cause the initial transform:
public void onResume() {
...
Handler handler = new Handler();
final Runnable r = new Runnable() {
public void run() {
// force transform with a 1 pixel nudge
mViewPager.beginFakeDrag();
mViewPager.fakeDragBy(1.0f);
mViewPager.endFakeDrag();
}
};
handler.postDelayed(r, 10); // some small delay in ms
}
As per the Android Developer's site:
A PageTransformer is invoked whenever a visible/attached page is scrolled. This offers an opportunity for the application to apply a custom transformation to the page views using animation properties
So, you wouldn't see any transition animation when the pager is loaded for the first time.
That's a good hack that you have been using, but I don't know if you had tried it already but can you animate the viewpager as a whole itself when it loads for the first time.
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.
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);
}
});