In my project I want use ProgressBar and I want set smoothly countdown animation.
I write below codes for smoothly animation and when set 1000ms (1s) for duration show smoothly animation, but I want set 10000ms (10s) for duration.
When set 10000ms (10s) for duration not smoothly animation and show Fragmentary countdown.
But I want set 10000ms for duration and show smoothly countdown.
public class SimpleFrag extends Fragment {
TextView toolbar_title;
RelativeLayout toolbar;
private ProgressBar progressBar;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_simple, container, false);
ButterKnife.bind(this, view);
toolbar_title = getActivity().findViewById(R.id.toolbar_title);
toolbar = getActivity().findViewById(R.id.toolbar);
progressBar = view.findViewById(R.id.progress);
return view;
}
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#SuppressLint("ObjectAnimatorBinding")
#Override
public void run() {
ProgressBarAnimation mProgressAnimation = new ProgressBarAnimation(progressBar, 79, 0);
mProgressAnimation.setDuration(10000);
progressBar.startAnimation(mProgressAnimation);
}
}, 50);
}
}
public class ProgressBarAnimation extends Animation {
private ProgressBar progressBar;
private float from;
private float to;
public ProgressBarAnimation(ProgressBar progressBar, float from, float to) {
super();
this.progressBar = progressBar;
this.from = from;
this.to = to;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
float value = from + (to - from) * interpolatedTime;
progressBar.setProgress((int) value);
}
}
}
How can I it? please help me . Thanks all <3
Take what you have now, and delete your setUserVisibleHint() method and your ProgressBarAnimation class. Add this:
private void setUpObserver() {
progressBar.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
startAnimation();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
progressBar.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
else {
progressBar.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
}
});
}
private void startAnimation() {
int width = progressBar.getWidth();
progressBar.setMax(width);
ValueAnimator animator = ValueAnimator.ofInt(0, width);
animator.setInterpolator(new LinearInterpolator());
animator.setStartDelay(0);
animator.setDuration(10_000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int value = (int) valueAnimator.getAnimatedValue();
progressBar.setProgress(value);
}
});
animator.start();
}
Then, inside your onCreateView() method, call setUpObserver() right before you return view;
The setUpObserver() method uses an OnGlobalLayoutListener to make sure that the system waits until the ProgressBar is measured an laid out before starting the animation.
The startAnimation() method is what takes care of actually setting up and running the animation. You can modify the values passed to setStartDelay() and setDuration() as you see fit.
The trick to this solution is making sure that the maximum value for the progress bar is equal to its width, meaning that each increment of "progress" is equal to one pixel on the screen. The android framework animation takes care of making sure everything runs as smoothly as possible.
Edit
If you want to be able to control the start/end points for the animation (rather than just go from empty to full), make these changes:
Change the declaration of startAnimation() to this:
private void startAnimation(float startPercent, float endPercent)
Delete this line from inside startAnimation()
ValueAnimator animator = ValueAnimator.ofInt(0, width);
and add these lines instead:
int start = (int) (startPercent * width);
int end = (int) (endPercent * width);
ValueAnimator animator = ValueAnimator.ofInt(start, end);
Finally, call startAnimation() by passing in fractional percentages:
startAnimation(0.79f, 0.10f); // will animate from 79% to 10%
Related
public class MainActivity extends Activity {
ValueAnimator animator;
ImageButton testImageButton;
float rad=1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
testImageButton = findViewById(R.id.ib_test);
testImageButton.setOutlineProvider(outlineProvider);
testImageButton.setClipToOutline(true);
animator = (ValueAnimator)
AnimatorInflater.loadAnimator(this,R.animator.radius);
animator.addUpdateListener(mListener);
}
public void clickedAnimator(View v){
animator.start();
}
public void testMe(View v){
l("testMe");
}
ValueAnimator.AnimatorUpdateListener mListener = new
ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float i= (float) valueAnimator.getAnimatedValue();
rad = i;
testImageButton.setOutlineProvider(outlineProvider);
}
};
ViewOutlineProvider outlineProvider = new ViewOutlineProvider() {
#Override
public void getOutline(View view, Outline outline) {
outline.setRoundRect(0,0,view.getWidth(),view.getHeight(),
rad*((float)view.getWidth())/2);
}
};
public void l(String s){
Log.v(getClass().getSimpleName(),s);
}
}
I am trying to animate the outline of an ImageView from Circle to Rectangle by constantly updating the ViewOutlineProvider that is set to the imageview.
Everything works fine but when the animation ends, the Image in the imageView Blinks once.
The value of rad changes from 1 to 0 in 500ms.
Hmmm, this line might help you: android:fillAfter="true"
Place this in your animation XML file inside the set attribute like:
<set [...]
android:fillAfter="true">
This helped me on a number of occasions when the animation was bugging at the end. Might work for you, might not, it's worth trying.
I have a fragment with 3 LinearLayout inside. By default, the first one have the weight attribute to 1.
Each LinearLayout have a OnClickListener that trigger 2 animations:
Animation with a weight from 0 to 1 for the one that is clicked.
Animation with a weight from 1 to 0 for the one currently opened.
My problem is that sometime the animations are not finishing completely.
Here is the code triggered by the OnClickListener:
private void launchAnimation(final LinearLayout llToExpand) {
ViewWeightAnimationWrapper animationWrapper = new ViewWeightAnimationWrapper(llToExpand);
ObjectAnimator anim = ObjectAnimator.ofFloat(animationWrapper,
"weight",
animationWrapper.getWeight(),
1f);
anim.setDuration(1000);
anim.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
Log.d("Anim1", "onAnimationEnd: Anim1 have ended");
}
#Override
public void onAnimationCancel(Animator animation) {
Log.d("Anim1", "onAnimationCancel: Anim1 have been canceled.");
}
});
ViewWeightAnimationWrapper animationWrapper2 = new ViewWeightAnimationWrapper(mLlCurrentlyOpened);
ObjectAnimator anim2 = ObjectAnimator.ofFloat(animationWrapper2,
"weight",
animationWrapper2.getWeight(),
0f);
anim2.setDuration(1000);
anim2.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
Log.d("Anim2", "onAnimationEnd: Anim2 have ended");
mLlCurrentlyOpened = llToExpand;
}
#Override
public void onAnimationCancel(Animator animation) {
Log.d("Anim2", "onAnimationCancel: Anim2 have been canceled.");
}
});
anim.start();
anim2.start();
}
Here is the code of my ViewWeightAnimationWrapper:
public class ViewWeightAnimationWrapper {
private View view;
public ViewWeightAnimationWrapper(View view) {
if (view.getLayoutParams() instanceof LinearLayout.LayoutParams) {
this.view = view;
} else {
throw new IllegalArgumentException("The view should be a LinearLayout");
}
}
public void setWeight(float weight) {
Log.i(String.valueOf(view.getId()), "setWeight: " + weight);
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) view.getLayoutParams();
params.weight = weight;
view.requestLayout();
}
public float getWeight() {
return ((LinearLayout.LayoutParams) view.getLayoutParams()).weight;
}
Here are the log that I have when the animation isn't going to the end:
I/2131755212: setWeight: 0.5
I/2131755207: setWeight: 0.5
I/2131755212: setWeight: 0.5251222
I/2131755207: setWeight: 0.47487777
I/2131755212: setWeight: 0.55174345
I/2131755207: setWeight: 0.44825655
D/Anim1: onAnimationCancel: Anim1 have been canceled.
D/Anim1: onAnimationEnd: Anim1 have ended
D/Anim2: onAnimationCancel: Anim2 have been canceled.
D/Anim2: onAnimationEnd: Anim2 have ended
I have no clue about why my animation are not canceled every time.
How can I know what is canceling my animations? Is it possible to force the animations to finish when they are cancelled?
I haven't found the reason about why the animation is canceled.
On the documentation, they say that the animation is canceled when there the same animation is triggered on the same object, which wasn't my case.
However, i have found a workaround here that is giving me the expected result.
I have replaced the ObjectAnimator by a custom animation:
/**
* Launch the expand animation on the LinearLayout in parameter and launch the folding animation
* on the currently opened LinearLayout.
* #param llToExpand LinearLayout that we want to expand.
*/
private void launchAnimation(final LinearLayout llToExpand) {
// Create the and assign the animations
ExpandAnimation expand = new ExpandAnimation(llToExpand, 0, 1);
expand.setDuration(500);
ExpandAnimation fold = new ExpandAnimation(mLlCurrentlyOpened, 1, 0);
fold.setDuration(500);
// Start the animations
llToExpand.startAnimation(expand);
mLlCurrentlyOpened.startAnimation(fold);
// Switch the currently opened LinearLayout for the next time
mLlCurrentlyOpened = llToExpand;
}
Here is my ExpandAnimation:
public class ExpandAnimation extends Animation {
private float mStartWeight;
private final float mEndWeight;
private final LinearLayout mContent;
public ExpandAnimation(LinearLayout content, float startWeight,
float endWeight) {
mStartWeight = startWeight;
mEndWeight = endWeight;
mContent = content;
}
#Override
protected void applyTransformation(float interpolatedTime,
Transformation t) {
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mContent
.getLayoutParams();
mStartWeight = lp.weight;
lp.weight = (mStartWeight + ((mEndWeight - mStartWeight) * interpolatedTime));
mContent.setLayoutParams(lp);
}
#Override
public boolean willChangeBounds() {
return true;
}
}
I just ran into what might be the same issue. Looking at the ObjectAnimator source, it's holding a WeakReference to the animation target. In my case I was using a dedicated object created only to perform the animation, and it turned out that the weak reference was the only reference. Thus it would sometimes be de-referenced during the animation. ObjectAnimator handles this "cleanly", simply canceling the animation.
How can I add a slide in animation on my recycler view items one after the other. Like as the activity starts, the list items of the recycler view slides in one by one. I am using LinearLayoutManager
Not all at the same time should slide in. And not even while scrolling. Just at the time of activity creation.
I searched but didn't find anything.
I want to achieve something like this : https://youtu.be/Q8TXgCzxEnw?t=30s
I put together a sample app a couple of months ago that has a sequential slide in-slide out animation during reshuffles. A demo video is available here. It should give you some ideas.
A link to the most relevant class file is here, and I'll copy the code below.
public class AllNotesFragmentRecyclerView extends RecyclerView {
private static final int BASE_ANIMATION_TIME = 50;
private static final int MAX_ANIMATION_TIME_INCREMENT = 100;
private int screenWidth;
private int startX, finalX;
private int[] interpolatedAnimationTimes;
public AllNotesFragmentRecyclerView(Context context) {
super(context);
init(context);
}
public AllNotesFragmentRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public AllNotesFragmentRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
calculateScreenWidth(context);
startX = 0;
finalX = -(screenWidth);
}
private void calculateScreenWidth(Context context) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics metrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(metrics);
screenWidth = metrics.widthPixels;
}
private int calculateInterpolatedAnimationTime(int currentIndex, int maxIndex) {
float percentage = ((float)currentIndex/(float)maxIndex);
float increment = (float) MAX_ANIMATION_TIME_INCREMENT * percentage;
return (int) (BASE_ANIMATION_TIME + increment);
}
public void updateListOrder() {
createAnimatorSet();
}
private void createAnimatorSet() {
AnimatorSet set = new AnimatorSet();
ArrayList<Animator> animArrayList = new ArrayList<>();
for (int i = 0; i < getChildCount(); i++) {
ObjectAnimator anim = ObjectAnimator
.ofFloat(getChildAt(i), "translationX", finalX);
int duration = calculateInterpolatedAnimationTime(i, getChildCount());
anim.setDuration(duration);
anim.addListener(new RowAnimationListener(i, duration, startX));
animArrayList.add(anim);
}
set.setInterpolator(new AccelerateInterpolator());
set.playSequentially(animArrayList);
set.start();
}
private void animateOn(int childPosition, int duration, int targetValue) {
ObjectAnimator animator = ObjectAnimator
.ofFloat(getChildAt(childPosition), "translationX", targetValue);
animator.setInterpolator(new DecelerateInterpolator());
animator.setDuration(duration);
animator.start();
}
//...
private class RowAnimationListener implements Animator.AnimatorListener {
private int position, duration, targetX;
public RowAnimationListener(int position, int duration, int targetX) {
this.position = position;
this.duration = duration;
this.targetX = targetX;
}
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
int currentItem = getLinearLayoutManager().findFirstVisibleItemPosition() + position;
getAdapter().notifyItemChanged(currentItem);
notifyRowsPeripheralToVisibleItemsDataChanged(position);
animateOn(position, duration, targetX);
}
#Override
public void onAnimationCancel(Animator animation) { }
#Override
public void onAnimationRepeat(Animator animation) { }
}
}
Finally I found a solution. In below snippet I will explain how to implement. It is simple and can be done on any existing working RecyclerView. I have explained everything in comments.
Here is the onCreate/onCreateView method (I have used this inside Fragment, You can change accordingly if needed):
RecyclerView recList = (RecyclerView) rootView.findViewById(R.id.event_list);
recList.setHasFixedSize(true);
LinearLayoutmanager llm = new LinearLayoutManager(getActivity().getApplicationContext());
llm.setOrientation(LinearLayoutManager.VERTICAL);
recList.setLayoutManager(llm);
// This is important. Setting recyclerView's alpha to zero.
// Basically this is just to hide recyclerview at start before binding data
// As setVisibility is not working on recycler view object.
recList.setAlpha(0);
// Create the EventAdapter with the result we got
// EventAdapter is my custom adapter class.
// you should set your adapter class
EventAdapter ea = new EventAdapter(eventResultList);
// Binding the Adapter to RecyclerView list to show the data
recList.setAdapter(ea);
// ********************* Animate at start ********************************
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
// This will give me the initial first and last visible element's position.
// This is required as only this elements needs to be animated
// Start will be always zero in this case as we are calling in onCreate
int start = llm.findFirstVisibleItemPosition();
int end = llm.findLastVisibleItemPosition();
Log.i("Start: ", start + "");
Log.i("End: ", end + "");
// Multiplication factor
int DELAY = 50;
// Loop through all visible element
for (int i = start; i <= end; i++) {
Log.i("Animatining: ", i + "");
// Get View
View v = recList.findViewHolderForAdapterPosition(i).itemView;
// Hide that view initially
v.setAlpha(0);
// Setting animations: slide and alpha 0 to 1
PropertyValuesHolder slide = PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, 150, 0);
PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat(View.ALPHA, 0, 1);
ObjectAnimator a = ObjectAnimator.ofPropertyValuesHolder(v, slide, alpha);
a.setDuration(300);
// It will set delay. As loop progress it will increment
// And it will look like items are appearing one by one.
// Not all at a time
a.setStartDelay(i * DELAY);
a.setInterpolator(new DecelerateInterpolator());
a.start();
}
// Set Recycler View visible as all visible are now hidden
// Animation will start, so set it visible
recList.setAlpha(1);
}
}, 50);
This is quite a small code without comments.
Some things needs an explanation:
Why hiding RecyclerView initially?
If RecyclerView is not hidden initially you will notice a blink initially before the animation starts. The reason for it is when you set a data adapter it will position it on its default positions and after the loop it starts animating. So in between while loop is running you will notice sudden blink in the RecyclerView that at first all are at its initial position and than suddenly animating.
So hiding it at first and than after loop completes and all visible positions animations are set with delays and started, we can show the RecyclerView. It makes sliding looks smooth.
The reason for hiding it with setAlpha(0) is as setVisibility() function is not working on the RecyclerView object.
How only visible elements will animate?
There are functions in the LayoutManager class to get the visible elements position. In LinearLayoutManager used findFirstVisibleItemPosition() to get the position of the first visible view from the recycler view data which is visible on screen. And the last visible view's position can be retried with findLastVisibleItemPosition(). So we can loop from the first view to last view and animate the initial views which are going to be on screen at start.
How delay Works?
As loop will progress from 0(start) to end it will set delay from 0,50,100,150,.. if DELAY variable is set to 50. So this will make first element start animating, second after 50ms delay, third after 100ms delay and so on. So it will look like they are coming in one by one. Not all together.
Create animation in anim/slide_in.xml file like below
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="#android:anim/decelerate_interpolator">
<translate
android:fromXDelta="100%" android:toXDelta="0%"
android:fromYDelta="0%" android:toYDelta="0%"
android:duration="2000"/>
</set>
And then apply this animation on each view of RecyclerView in onBindViewHolder method.
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
ViewHolder vh = (ViewHolder) holder;
vh1.tv_header.setText(mList.get(position));
Animation animation = AnimationUtils.loadAnimation(mContext,R.anim.rec_anim);
animation.setStartOffset(30 * position);//Provide delay here
holder.itemView.startAnimation(animation);
}
Am trying to do a simple animation in order scale a relative layout's width. I managed to do it using this code but the animation is done very quickly even with 5000ms duration. I cant see the intermidiate results. Any suggestion?
RelativeLayout baseLayer;
RelativeLayout fillLayer;
public void animate(int duration){
Animation a = new Animation()
{
#Override
protected void applyTransformation(float interpolatedTime, Transformation t)
{
fillLayer.getLayoutParams().width = (int)(baseLayer.getLayoutParams().width * interpolatedTime);
fillLayer.requestLayout();
}
#Override
public boolean willChangeBounds() {
return true;
}
};
a.setDuration(duration);
a.setInterpolator(new LinearInterpolator());
fillLayer.startAnimation(a);
}
I have found the solution. The problem was that i was calling the animation method from OnStart(). Now am calling it from onWindowFocusChanged().
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
testBar.animate(2000,0.8);
}
RelativeLayout baseLayer;
RelativeLayout fillLayer;
public void animate(int duration,float fillPercentage){
final int targetWidth = (int)(baseLayer.getMeasuredWidth() * fillPercentage);
Animation a = new Animation()
{
#Override
protected void applyTransformation(float interpolatedTime, Transformation t)
{
fillLayer.getLayoutParams().width = (int)(targetWidth * interpolatedTime);
fillLayer.requestLayout();
}
#Override
public boolean willChangeBounds() {
return true;
}
};
a.setDuration(duration);
a.setInterpolator(new LinearInterpolator());
fillLayer.startAnimation(a);
}
And throughout this procedure I have found from other post an alternative way to do the same job. But again it the method need to be called form the onWindowFocusChanged().
ValueAnimator.ofObject(new WidthEvaluator(fillLayer), fillLayer.getLayoutParams().width, targetWidth).setDuration(2000).start();
class WidthEvaluator extends IntEvaluator {
private View v;
public WidthEvaluator(View v) {
this.v = v;
}
#Override
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
int num = (Integer)super.evaluate(fraction, (Integer)startValue, (Integer)endValue);
ViewGroup.LayoutParams params = v.getLayoutParams();
params.width = num;
v.setLayoutParams(params);
return num;
}
}
[Update Solution]
Following to that I have added my custom views into a ListView and then the problems started again. ListView should be getting ready before views get their actual size. So the animation wasn't able to be done because getMeasuredWidth() was returning always 0 (this was and the problem from the beginning).
So what i did is i used this code snippet in OnCreate().
ViewTreeObserver vto = listBars.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (!HackClass.GlobalDrawnFlag) {
adapter.notifyDataSetChanged();
HackClass.GlobalDrawnFlag = true;
}
}
});
And this inside my list adapter class
if (item.getMeasuredWidth==0)
HackClass.GlobalDrawnFlag = false;
else
HackClass.GlobalDrawnFlag = true;
This approach solved all my problems apparently. However is that a good solution ? Any other suggestion is welcome.
So what i am trying to achieve is user would open to first page of the view pager, and the view pager would bounce to half of the second page and bounce back to the fist page indicating that there are more pages to scroll to. I was wondering on how i could implement this?
You can use fakeDragBy method to achieve this effect:
viewPager.beginFakeDrag();
viewPager.fakeDragBy(offset); //offset in pixels.
viewPager.endFakeDrag();
EDIT:
I have made method for this:
private int animFactor;
private ValueAnimator animator = new ValueAnimator();
private void animateViewPager(final ViewPager pager, final int offset, final int delay) {
if (!animator.isRunning()) {
animator.removeAllUpdateListeners();
animator.removeAllListeners();
//Set animation
animator.setIntValues(0, -offset);
animator.setDuration(delay);
animator.setRepeatCount(1);
animator.setRepeatMode(ValueAnimator.RESTART);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
Integer value = animFactor * (Integer) animation.getAnimatedValue();
if (!pager.isFakeDragging()) {
pager.beginFakeDrag();
}
pager.fakeDragBy(value);
}
});
animator.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationStart(Animator animation) {
animFactor = 1;
}
#Override
public void onAnimationEnd(Animator animation) {
pager.endFakeDrag();
}
#Override
public void onAnimationRepeat(Animator animation) {
animFactor = -1;
}
});
animator.start();
}
}
Example of usage:
animateViewPager(pager, 10, 1000);
Edit2: ValueAnimator is class for Api level 11. Also set pager adapter before calling this method.
Adding a note to #Yuraj's answer. Call the method in onWindowFocusChanged when hasFocus==true as follows to avoid indexOutOfBoundsException:
#Override
public void onWindowFocusChanged(boolean hasFocus)
{
super.onWindowFocusChanged(hasFocus);
if(hasFocus)
{
Handler handler = new Handler();
final Runnable r = new Runnable()
{
public void run()
{
if(mViewPager.getCurrentItem() == 0)
{
Context context = Activity_main.this;
String filename="Init";
SharedPreferences stats;
stats = context.getSharedPreferences(filename, 0);
int appOpen = stats.getInt("appOpen", 0);
if(appOpen <= 5)
{
animateViewPager(mViewPager, 10, 300);
appOpen += 1;
SharedPreferences.Editor editor = stats.edit();
editor.putInt("appOpen", appOpen);
editor.commit();
}
}
}
};
handler.postDelayed(r, WAIT_VIEWPAGER_NUDGE);
}
}
Thank you Yuvraj! It worked with a simple modification. If anybody is getting "Invalid index 0, size is 0" error, here's a simple fix for it. If you call animateViewPager() method in onCreate() you might get this error, "Invalid index 0, size is 0". I believe viewpager.beginFakeDrag(); is being called before viewPager items / childs are initialized. So, call animateViewPager() with a delay like so:
new Handler().postDelayed(() -> animateViewPager(viewPager, 10, 1000), 500);
500 is the delay in milisecond