首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Android TextView中的波浪动画

Android TextView中的波浪动画
EN

Stack Overflow用户
提问于 2014-01-07 04:48:29
回答 2查看 14.1K关注 0票数 7

我使用下面的代码在我的onCreate()方法中动画我的TextView

代码语言:javascript
复制
txtSize.setText("This is my Text");
txtSize.setAnimation(AnimationUtils.loadAnimation(myContext, 
android.R.anim.slide_in_left));

现在我想问一下,是否有可能以某种方式让这个slide_in_left动画像浪潮一样出现?

我在这里找到了这个例子,但我不知道如何在我的例子中使用它(在TextView上从左向右滑动,而不是在GridView上):http://www.edumobile.org/android/android-development/wave-layout-animationexample/

谢谢你的帮助

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-01-09 22:44:00

达到我想要的效果的一种方法(我从来不确定我是不是理解了…)是利用Android的3个有用的部分:

onDraw

  • A Path覆盖测量后或以后创建的自定义视图,用于在屏幕上生成波浪形状。使用画布的drawTextOnPath (String text, Path path, float hOffset, float vOffset, Paint paint) (docs)在切换到动画模式时在每次绘制过程中递增hOffset

对于不熟悉Android中的自定义视图的未来读者,请参阅here is the developer resource

我们视图的API可能包括:

  1. setText(...) of course
  2. resetTextPosition()重置传入的animation
  3. animateToRight(double millis duration)以开始动画,并设置时间。

同样重要的是,我们的类使用像素高度、WRAP_CONTENT和MATCH_PARENT等LayoutParams。现在这很棘手,因为要以一种不是很糟糕的继承实践的方式完全做到这一点,通常意味着要覆盖很多东西。因此,(顶部提示),我们简单地继续让布局参数为开发人员指定所需的文本宽度和高度,然后我们为视图上的setPaddingtopPaddingbottomPadding引入新的含义:

  1. setPadding(int ...):参数topPadding和bottomPadding设置将用于波形的额外空间。

下面是一些可编译的可用代码:

代码语言:javascript
复制
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;
    }
}

解释

  • 我们使用enum将视图放入三种状态。TRANSITION_STARTING设置初始变量并告诉视图进入TRANSITION_RUNNING状态。此状态不断地invalidates() (通过UI消息队列重新调用onDraw )视图的状态,因此将使用新参数再次绘制视图。在Android的内部视图和小部件代码库中,您会经常看到这种模式。
  • pxWLength是一个可调整的参数,代表wave.
  • canvas.drawTextOnPath(getText().toString(), wavePath, leftOffset, (getHeight()-(getPaddingTop()+getPaddingBottom()))/4, getPaint());的波长,我们需要修改路径的水平偏移,因为(另一个顶部提示) Android绘制的文本在路径上方对齐-否则文本将在波形的低谷中被挤压。

调用它的一个好方法是:

代码语言:javascript
复制
   final WaveyTextView wTV = new WaveyTextView(getActivity());
   wTV.setPadding(0, 75, 0, 75);
   wTV.setText("Some wavey text here...");
   wTV.animateToRight(10000);

然后为你的活动添加高度、宽度、宽和长(或者使用WRAP_CONTENT )。

这将需要一些调整!我希望这篇文章能起作用,或者至少其中的一些想法能激励你创建自己的时髦文本动画类!

票数 9
EN

Stack Overflow用户

发布于 2014-01-15 13:37:10

尝试使用Number Tweening技术(比如Timely )!

您应该像这样创建自定义类:

代码语言:javascript
复制
 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);
    }
    }
    }

注意:您可能需要调整它为任何视图(它可能是数字,文本或自定义视图),因为它是数字动画!

数字不是直接来自绘制为TextView的字体,而是构造为多段

如果您忘记了Android视图层次结构:

制片人:SriramramaniGistGit

票数 11
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20958990

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档