Search code examples
javaandroidandroid-tablayout

TabLayout - indicator animation is delayed


In my application I have TabLayout with two tabs: Stopwatch and Timer. When I switch between this two tabs and timer is not running then everything works great. Problem occurs when I start timer, indicator animation is delayed. Below video demonstrating this issue:

https://media.giphy.com/media/xjLnjaQ0NgH0a4yqsO/giphy.gif

I think this is caused by short countDownInterwal in CountDownTimer. I set this to 50. This value is low because I want to have smooth time indicator (in my app blue big circle - Timer).

private void startTimer() {
        countDownTimer = new CountDownTimer(timeLeftInMillis, 50) {
            @Override
            public void onTick(long millisUntilFinished) {
                timeLeftInMillis = millisUntilFinished;
                updateCountDownText();
                mProgressBar1.setProgress((int) (timeLeftInMillis/10));
            }

            @Override
            public void onFinish() {
                finishCountDownTimer();
                countDownSound.start();
                showDialogWhenFinishCountDown();
            }
        }.start();
    }

What sould I do to fix this problem?


Solution

  • A CountDownTimer is not an optimal solution (for a start the entire class is synchronised which causes immediate overhead), you shouldn't be animating this manually if you want smooth animations. Use what the Android Framework has to offer.

    I used ObjectAnimator with a very quick example :

    Example Animation

    Basic Fragment class - what you'll be most interested in is the startStopTimer() method :

    public class TimerFragment extends Fragment {
    
        public TimerFragment() { }
    
        private static int ONE_MINUTE_MILLIS = (int) TimeUnit.MINUTES.toMillis(1);
    
        private ProgressBar progressBar;
        private ObjectAnimator animator;
    
        @Nullable
        @Override
        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            return inflater.inflate(R.layout.fragment_timer, container, false);
        }
    
        @Override
        public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
            progressBar = view.findViewById(R.id.progressBar);
            progressBar.setMax(ONE_MINUTE_MILLIS);
            progressBar.setProgress(ONE_MINUTE_MILLIS);
            view.findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    startStopTimer();
                }
            });
        }
    
        private void startStopTimer() {
            if(animator == null) {
                animator = ObjectAnimator.ofInt(progressBar, "progress", ONE_MINUTE_MILLIS, 0);
                animator.setDuration(ONE_MINUTE_MILLIS);
                animator.setInterpolator(new LinearInterpolator());
                animator.start();
            } else {
                animator.cancel();
                animator = null;
                progressBar.setProgress(ONE_MINUTE_MILLIS);
            }
        }
    
        @Override
        public void onDestroyView() {
            super.onDestroyView();
    
            if(animator != null) {
                animator.cancel();
                animator = null;
            }
        }
    }