Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android TextView counter with Top/Down Animation

We want a Meter animation in TextView

enter image description here

To make it a little more interesting, I want each digit come from top to bottom or bottom to top ?

Right now I using listview for achieving this, I have also tried with TextSwitcher but its have a limitation of two child only.

I'm using getListView().smoothScrollToPosition(0...3...6...6...n);

Is there a simple way of doing this? because right now , we need to maintain 3 ListView and Adapter as well for maintaining this.

Please refer link to more understand this question

Display StopWatch Timer animated like the petrol pump meter using NSTimer

like image 955
Lavekush Agrawal Avatar asked Jan 29 '16 13:01

Lavekush Agrawal


4 Answers

ListView's might be good enough solution, but I've implemented it with a custom View (FrameLayout), which contains inside 2 TextViews, which are animating based on the value changes:

enter image description here

The idea of code is very basic:

  • You pass to setValue desired value;
  • If it's bigger than current one - start animation from from bottom to top (and vice versa) to increment/decrement current value by 1. Here, we animating two TextViews to replace each other;
  • In AnimationEnd listener, check if we reached desired value - if not - do one more run (recursively);

        public class DigitTextView extends FrameLayout {
    
            private static int ANIMATION_DURATION = 250;
            TextView currentTextView, nextTextView;
    
            public DigitTextView(Context context, AttributeSet attrs) {
                super(context, attrs);
                init(context);
            }
    
            public DigitTextView(Context context) {
                super(context);
                init(context);
            }
    
            private void init(Context context) {
                LayoutInflater.from(context).inflate(R.layout.digit_text_view, this);
                currentTextView = (TextView) getRootView().findViewById(R.id.currentTextView);
                nextTextView = (TextView) getRootView().findViewById(R.id.nextTextView);
    
                nextTextView.setTranslationY(getHeight());
    
                setValue(0);
            }
    
            public void setValue(final int desiredValue) {
                if (currentTextView.getText() == null || currentTextView.getText().length() == 0) {
                    currentTextView.setText(String.format(Locale.getDefault(), "%d", desiredValue));
                }
    
                final int oldValue = Integer.parseInt(currentTextView.getText().toString());
    
                if (oldValue > desiredValue) {
                    nextTextView.setText(String.format(Locale.getDefault(), "%d", oldValue-1));
    
                    currentTextView.animate().translationY(-getHeight()).setDuration(ANIMATION_DURATION).start();
                    nextTextView.setTranslationY(nextTextView.getHeight());
                    nextTextView.animate().translationY(0).setDuration(ANIMATION_DURATION).setListener(new Animator.AnimatorListener() {
                        @Override
                        public void onAnimationStart(Animator animation) {}
                        @Override
                        public void onAnimationEnd(Animator animation) {
                            currentTextView.setText(String.format(Locale.getDefault(), "%d", oldValue - 1));
                            currentTextView.setTranslationY(0);
                            if (oldValue - 1 != desiredValue) {
                                setValue(desiredValue);
                            }
                        }
                        @Override
                        public void onAnimationCancel(Animator animation) {}
                        @Override
                        public void onAnimationRepeat(Animator animation) {}
                    }).start();
                } else if (oldValue < desiredValue) {
                    nextTextView.setText(String.format(Locale.getDefault(), "%d", oldValue+1));
    
                    currentTextView.animate().translationY(getHeight()).setDuration(ANIMATION_DURATION).start();
                    nextTextView.setTranslationY(-nextTextView.getHeight());
                    nextTextView.animate().translationY(0).setDuration(ANIMATION_DURATION).setListener(new Animator.AnimatorListener() {
                        @Override
                        public void onAnimationStart(Animator animation) {}
                        @Override
                        public void onAnimationEnd(Animator animation) {
                            currentTextView.setText(String.format(Locale.getDefault(), "%d", oldValue + 1));
                            currentTextView.setTranslationY(0);
                            if (oldValue + 1 != desiredValue) {
                                setValue(desiredValue);
                            }
                        }
                        @Override
                        public void onAnimationCancel(Animator animation) {}
                        @Override
                        public void onAnimationRepeat(Animator animation) {}
                    }).start();
                }
            }
        }
    

And it's XML:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="48dp"
    android:layout_height="56dp"
    android:padding="8dp"
    android:background="@drawable/rounded_blue_rect">
    <TextView
        android:id="@+id/currentTextView"
        android:textColor="#FFFFFF"
        android:textSize="18sp"
        android:gravity="center"
        android:layout_gravity="center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <TextView
        android:id="@+id/nextTextView"
        android:textColor="#FFFFFF"
        android:textSize="18sp"
        android:layout_gravity="center"
        android:gravity="center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</FrameLayout>

And it's very easy to use:

Add to layout:

<klogi.com.myapplication.DigitTextView
    android:id="@+id/digitTextView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>

And set Value in code:

DigitTextView digitTextView = (DigitTextView) findViewById(R.id.digitTextView);
digitTextView.setValue(5);

Upd:
Another option to use, from what I see, is to set up a bit customized NumberPicker

I hope, it helps!

like image 164
Konstantin Loginov Avatar answered Nov 18 '22 16:11

Konstantin Loginov


Ever since Robinhood won the Material design awards they have open sourced there custom TextView just like you are describing.

Check out Robinhood's Ticker library

enter image description here

like image 39
timr Avatar answered Nov 18 '22 17:11

timr


This code performs the same animation where number rolldown from top to bottom.

Rolling-TextView-Animation

enter image description here

like image 3
Kavita_p Avatar answered Nov 18 '22 17:11

Kavita_p


You can also use a handler to get the desired effect. Using this, you won't have to make any custom views. Create a function handleTextView which takes in initialValue, finalValue and targetTextview as arguments. The method is-

private void handleTextView(int initialValue, int finalValue, final TextView targetTextview) {
    DecelerateInterpolator decelerateInterpolator = new DecelerateInterpolator(1f);
    final int newInitialValue = Math.min(initialValue, finalValue);
    final int newFinalValue = Math.max(initialValue, finalValue);
    final int difference = Math.abs(finalValue - initialValue);
    Handler handler = new Handler();
    for (int count = newInitialValue; count <= newFinalValue; count++) {
      //Time to display the current value to the user.
      int time = Math.round(decelerateInterpolator.getInterpolation((((float) count) / difference)) * 100) * count;
      final int finalCount = ((initialValue > finalValue) ? initialValue - count : count);
      handler.postDelayed(new Runnable() {
        @Override
        public void run() {
          targetTextview.setText(finalCount.toString());
        }
      }, time);
    }
  }

UPDATE: Option 2- You can use a value animator as well-

private void handleTextView(int initialValue, int finalValue, final TextView  textview) {

    ValueAnimator valueAnimator = ValueAnimator.ofInt(initialValue, finalValue);
    valueAnimator.setDuration(1500);

    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator valueAnimator) {

            textview.setText(valueAnimator.getAnimatedValue().toString());

        }
    });
    valueAnimator.start();

}

By using this method we do not need to do any math.

like image 2
Vaibhav Sharma Avatar answered Nov 18 '22 16:11

Vaibhav Sharma