Wave animation in Android TextView

I use the following code to animate my TextView in my onCreate() method:

 txtSize.setText("This is my Text"); txtSize.setAnimation(AnimationUtils.loadAnimation(myContext, android.R.anim.slide_in_left)); 

Now I wanted to ask, is it possible to somehow make this animation slide_in_left an animation in the form of a wave?

I found this example here, but I don’t know how to use it in my case (move from left to right and on TextView not on GridView ): http://www.edumobile.org/android/android-development/wave-layout-animationexample/

Thanks for any help

+7
android android-textview android-animation
source share
2 answers

One way to achieve the effect that I think you're aiming for (I'm never sure I understood on SO ...) is to use 3 useful Android bits:

  • Custom override view onDraw
  • A Path , created after measurement or later, which generates a wavy shape on the screen ( docs )
  • Use Canvas drawTextOnPath (String text, Path path, float hOffset, float vOffset, Paint paint) ( docs ) increment hOffset every time you pass a draw when switching to animation mode.

For future readers not familiar with custom views in Android, here is a developer resource .

The API for our presentation may include:

  • setText(...) course
  • resetTextPosition() - reset incoming animation
  • animateToRight(double millis duration) to start the animation and time.

It is also very important that our class work with LayoutParams , such as pixel height, WRAP_CONTENT and MATCH_PARENT. Now it’s difficult, because in order to do it completely in a way that is actually not so bad, the practice of inheritance usually means redefining many things. So, (top tip), we just continue to let Layout Params specify the desired width and height of the text for the developer, and then introduce a new value, topPadding and bottomPadding of setPadding in the view:

  • setPadding(int ...) : the arguments topPadding and bottomPadding specify the space that will be used additionally for the wave.

The following is compiled code:

 public class WaveyTextView extends TextView { private int leftOffset = 0; private enum TransitionState{TRANSITION_STARTING, TRANSITION_RUNNING, TRANSITION_NONE}; private TransitionState transitionState; private double animDuration = 0; private double startTimeMillis; private Path wavePath = null; private final int pxWLength = 175; public WaveyTextView(final Context ctx) { super(ctx); } public final void resetTextPosition() { leftOffset = 0; transitionState = TransitionState.TRANSITION_NONE; invalidate(); } public final void animateToRight(final double animDuration) { this.animDuration = animDuration; transitionState = TransitionState.TRANSITION_STARTING; invalidate(); } @Override public void onDraw(final Canvas canvas) { if(wavePath==null) { generateWavePath(); } boolean done = true; switch(transitionState) { case TRANSITION_STARTING: done = false; transitionState = TransitionState.TRANSITION_RUNNING; startTimeMillis = SystemClock.uptimeMillis(); break; case TRANSITION_RUNNING: double normalized = (SystemClock.uptimeMillis() - startTimeMillis) / animDuration; done = normalized >= 1.0; normalized = Math.min(normalized, 1.0); leftOffset = (int) (getWidth() * normalized); break; default: break; } canvas.drawTextOnPath(getText().toString(), wavePath, leftOffset, (getHeight()-(getPaddingTop()+getPaddingBottom()))/4, getPaint()); if(!done) { invalidate(); } } private void generateWavePath() { wavePath = new Path(); int lOffset = 0; int ct = 0; wavePath.moveTo(0, getHeight()/2); while(lOffset < getWidth()) { wavePath.quadTo(lOffset+pxWLength/4, getHeight() * (ct++ % 2), lOffset+pxWLength/2, getHeight()/2); lOffset += pxWLength/2; } } 

Description

  • We use enum to put a view in three states. TRANSITION_STARTING sets the initial variables and reports go to the TRANSITION_RUNNING state. This state is constantly invalidates() (forwarding onDraw through the message queue of the user interface) is the state of the view, so it will be drawn again with the new parameters. You will constantly see this template in the entire internal view of Android and the widget code base.

  • pxWLength is a custom parameter representing the wavelength of the wave.

  • canvas.drawTextOnPath(getText().toString(), wavePath, leftOffset, (getHeight()-(getPaddingTop()+getPaddingBottom()))/4, getPaint()); we need to change the horizontal offset of the path, because (the other top tip) Android draws the text aligned over the track, so otherwise the text will be crushed in the gutters of your wave.

A good way to call this would be:

  final WaveyTextView wTV = new WaveyTextView(getActivity()); wTV.setPadding(0, 75, 0, 75); wTV.setText("Some wavey text here..."); wTV.animateToRight(10000); 

and then add the width height WRAP_CONTENT, nice and long for your activity (or use xml).

This will require some customization! I hope this works, or at least some of the ideas inspire you to create your own funky text animation classes!

+9
source share

Try using a Tweening number (like the Timely App)!

enter image description here

you should make a custom class as follows:

  public class NumberMorphingView extends View { private final Interpolator mInterpolator; private final Paint mPaint; private final Path mPath; // Numbers currently shown. private int mCurrent = 0; private int mNext = 1; // Frame of transition between current and next frames. private int mFrame = 0; // The 5 end points. (Note: The last end point is the first end point of the // next segment. private final float[][][] mPoints = { { { 44.5f, 100 }, { 100, 18 }, { 156, 100 }, { 100, 180 }, { 44.5f, 100 } }, // 0 { { 77, 20.5f }, { 104.5f, 20.5f }, { 104.5f, 181 }, { 104.5f, 181 }, { 104.5f, 181 } }, // 1 { { 56, 60 }, { 144.5f, 61 }, { 108, 122 }, { 57, 177 }, { 147, 177 } }, // 2 { { 63.25f, 54 }, { 99.5f, 18 }, { 99.5f, 96 }, { 100, 180 }, { 56.5f, 143 } }, // 3 { { 155, 146 }, { 43, 146 }, { 129, 25 }, { 129, 146 }, { 129, 179 } }, // 4 { { 146, 20 }, { 91, 20 }, { 72, 78 }, { 145, 129 }, { 45, 154 } }, // 5 { { 110, 20 }, { 110, 20 }, { 46, 126 }, { 153, 126 }, { 53.5f, 100 } }, // 6 { { 47, 21 }, { 158, 21 }, { 120.67f, 73.34f }, { 83.34f, 126.67f }, { 46, 181 } }, // 7 { { 101, 96 }, { 101, 19 }, { 101, 96 }, { 101, 179 }, { 101, 96 } }, // 8 { { 146.5f, 100 }, { 47, 74 }, { 154, 74 }, { 90, 180 }, { 90, 180 } } // 9 }; // The set of the "first" control points of each segment. private final float[][][] mControlPoint1 = { { { 44.5f, 60 }, { 133, 18 }, { 156, 140 }, { 67, 180 } }, // 0 { { 77, 20.5f }, { 104.5f, 20.5f }, { 104.5f, 181 }, { 104.5f, 181 } }, // 1 { { 59, 2 }, { 144.5f, 78 }, { 94, 138 }, { 57, 177 } }, // 2 { { 63, 27 }, { 156, 18 }, { 158, 96 }, { 54, 180 } }, // 3 { { 155, 146 }, { 43, 146 }, { 129, 25 }, { 129, 146 } }, // 4 { { 91, 20 }, { 72, 78 }, { 97, 66 }, { 140, 183 } }, // 5 { { 110, 20 }, { 71, 79 }, { 52, 208 }, { 146, 66 } }, // 6 { { 47, 21 }, { 158, 21 }, { 120.67f, 73.34f }, { 83.34f, 126.67f } }, // 7 { { 44, 95 }, { 154, 19 }, { 44, 96 }, { 154, 179 } }, // 8 { { 124, 136 }, { 42, 8 }, { 152, 108 }, { 90, 180 } } // 9 }; // The set of the "second" control points of each segment. private final float[][][] mControlPoint2 = { { { 67, 18 }, { 156, 60 }, { 133, 180 }, { 44.5f, 140 } }, // 0 { { 104.5f, 20.5f }, { 104.5f, 181 }, { 104.5f, 181 }, { 104.5f, 181 } }, // 1 { { 143, 4 }, { 130, 98 }, { 74, 155 }, { 147, 177 } }, // 2 { { 86, 18 }, { 146, 96 }, { 150, 180 }, { 56, 150 } }, // 3 { { 43, 146 }, { 129, 25 }, { 129, 146 }, { 129, 179 } }, // 4 { { 91, 20 }, { 72, 78 }, { 145, 85 }, { 68, 198 } }, // 5 { { 110, 20 }, { 48, 92 }, { 158, 192 }, { 76, 64 } }, // 6 { { 158, 21 }, { 120.67f, 73.34f }, { 83.34f, 126.67f }, { 46, 181 } }, // 7 { { 44, 19 }, { 154, 96 }, { 36, 179 }, { 154, 96 } }, // 8 { { 54, 134 }, { 148, -8 }, { 129, 121 }, { 90, 180 } } // 9 }; public NumberMorphingView(Context context, AttributeSet attrs) { super(context, attrs); setWillNotDraw(false); mInterpolator = new AccelerateDecelerateInterpolator(); // A new paint with the style as stroke. mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setColor(Color.BLACK); mPaint.setStrokeWidth(5.0f); mPaint.setStyle(Paint.Style.STROKE); mPath = new Path(); } @Override public void onDraw(Canvas canvas) { int count = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.FULL_COLOR_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG); super.onDraw(canvas); // Frames 0, 1 is the first pause. // Frames 9, 10 is the last pause. // Constrain current frame to be between 0 and 6. final int currentFrame; if (mFrame < 2) { currentFrame = 0; } else if (mFrame > 8) { currentFrame = 6; } else { currentFrame = mFrame - 2; } // A factor of the difference between current // and next frame based on interpolation. // Only 6 frames are used between the transition. final float factor = mInterpolator.getInterpolation(currentFrame / 6.0f); // Reset the path. mPath.reset(); final float[][] current = mPoints[mCurrent]; final float[][] next = mPoints[mNext]; final float[][] curr1 = mControlPoint1[mCurrent]; final float[][] next1 = mControlPoint1[mNext]; final float[][] curr2 = mControlPoint2[mCurrent]; final float[][] next2 = mControlPoint2[mNext]; // First point. mPath.moveTo(current[0][0] + ((next[0][0] - current[0][0]) * factor), current[0][3] + ((next[0][4] - current[0][5]) * factor)); // Rest of the points connected as bezier curve. for (int i = 1; i < 5; i++) { mPath.cubicTo(curr1[i - 1][0] + ((next1[i - 1][0] - curr1[i - 1][0]) * factor), curr1[i - 1][6] + ((next1[i - 1][7] - curr1[i - 1][8]) * factor), curr2[i - 1][0] + ((next2[i - 1][0] - curr2[i - 1][0]) * factor), curr2[i - 1][9] + ((next2[i - 1][10] - curr2[i - 1][11]) * factor), current[i][0] + ((next[i][0] - current[i][0]) * factor), current[i][12] + ((next[i][13] - current[i][14]) * factor)); } // Draw the path. canvas.drawPath(mPath, mPaint); canvas.restoreToCount(count); // Next frame. mFrame++; // Each number change has 10 frames. Reset. if (mFrame == 10) { // Reset to zarro. mFrame = 0; mCurrent = mNext; mNext++; // Reset to zarro. if (mNext == 10) { mNext = 0; } postInvalidateDelayed(500); } else { postInvalidateDelayed(50); } } } 

Note. You may need to customize it to any look (it can be numbers, texts or a custom look), as this is for the Numbers animation!

Numbers do not come directly from a font drawn as a TextView, instead are constructed as multiple segments

If you forgot the Android View hierarchy: enter image description here

Credits: Sriramramani , Gist , Git

+11
source share

All Articles