Волновая анимация в Android TextView

я использую следующий код для анимации моего TextView в своем onCreate() способ:

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/

спасибо за любую помощь

2 ответов


один из способов достичь эффекта, к которому, я думаю, вы стремитесь (я никогда не уверен, что понял это...) является использование 3 полезных бит Android:

  1. переопределение пользовательского представления onDraw
  2. A Path созданный после измерения или позже, что создает волнообразную форму по экрану (docs)
  3. использовать Canvas drawTextOnPath (String text, Path path, float hOffset, float vOffset, Paint paint) (docs) incrementing hOffset на каждом проходе розыгрыша при переключении в анимацию режим.

для будущих читателей, не знакомых с пользовательскими представлениями в Android,вот ресурс разработчика.

API для нашего представления может включать:

  1. setText(...) конечно
  2. resetTextPosition() для сброса входящей анимации
  3. animateToRight(double millis duration) для запуска анимации и со временем.

также очень важно, чтобы наш класс работал с LayoutParams как высота пиксела, Wrap_content в качестве параметров и присваиваем. Теперь это сложно, потому что сделать это полностью таким образом, который не является действительно плохой практикой наследования, обычно означает переопределение многих вещей. Итак, (верхний наконечник), мы просто продолжаем позволять парам макета указывать желаемую ширину и высоту текста для разработчика, а затем вводим новое значение topPadding и bottomPadding of setPadding вид:

  1. setPadding(int ...): аргументы topPadding и bottomPadding задает пространство, которое будет использоваться кроме того для волны.

вот некоторый компилируемый полезный код:

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() (повторный вызов onDraw через очередь сообщений пользовательского интерфейса) состояние представления, поэтому оно будет нарисовано снова с новыми параметрами. Вы будете постоянно см. этот шаблон хорошо во всем внутреннем представлении Android и базе кода виджета.

  • pxWLength является настраиваемым параметром, представляющим длину волны волны.

  • canvas.drawTextOnPath(getText().toString(), wavePath, leftOffset, (getHeight()-(getPaddingTop()+getPaddingBottom()))/4, getPaint()); нужно изменить горизонтальное смещение пути, потому что (еще один полезный совет) Android рисует текст, выровненный выше путь - так что в противном случае текст будет раздавлен в корытах вашего волна.

хороший способ назвать это тогда было бы:

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

а затем добавить его высоту wrap_content ширину приятно и долго к вашей деятельности (или использовать xml).

это потребует некоторой настройки! Я надеюсь, что это сработает или, по крайней мере, некоторые из идей вдохновят вас на создание собственных классов анимации текста!


попробуйте с номером Tweening методы (например, своевременное приложение) !

enter image description here

вы должны сделать пользовательский класс, как это:

 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 View: enter image description here

благодарности: Sriramramani , суть ,Git