English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Primero, muestre el efecto gráfico
Animación
Imagen estática
1. Mirar atrás
【Android Custom View: Una animación de verificación delicada】En el artículo anterior, ya hemos implementado básicamente el efecto del controlador, pero... pero... después de tres o cuatro días, al mirar de nuevo el código que escribí, aunque la idea sigue presente, parte del código aún no se puede entender de inmediato...
¡Dios mío, esto debe ser reconstruido de inmediato! Afortunadamente, un amigo simple ChangQin lo imitó y lo escribió, después de lo cual pensé que también podría hacerlo de esta manera.
2. Profunda reflexión
Sobre el pensamiento de la dibujo del controlador, puedes ver el artículo anterior, aquí no se analizará. Primero, analicemos algunos problemas persistentes en el artículo anterior, ¿qué lugares necesitan mejoras?
Tomemos Dibujar el progreso del anillo Este paso para ver
//Contador private int ringCounter = 0; @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (!isChecked) { ... return; } //Dibujar el progreso del círculo, aumentar automáticamente cada vez que se dibuje12La unidad, es decir, el arco ha pasado12Grado //Aquí,12La unidad se escribe primero, luego podemos hacer una configuración para lograr la personalización ringCounter += 12; if (ringCounter >= 360) { ringCounter = 360; } canvas.drawArc(mRectF, 90, ringCounter, false, mPaintRing); ... //Redibuja forzadamente postInvalidate(); }
Aquí, definimos un contador ringCounter, cuando se dibuja, se basa en12Unidad para el autoincremento hasta360, para simular el cambio de progreso.
Piensa con atención
Al cambiar la unidad de autoincremento para controlar el cambio de velocidad de la animación, es difícil ajustarlo a tu satisfacción, en este momento podemos pensar que el control real de la velocidad de ejecución de la animación es controlar el tiempo, si se puedeUsar el tiempo para controlar la velocidad de la animaciónSería mucho más conveniente si la animación se dividiera4Paso de ejecución, si cada paso de la animación se realiza con un contador escrito a mano, se debe definir4Demasiados miembros de la variable solo harán que el código sea más caóticoSi se debe agregar animaciónInterpolador, el contador escrito a mano no puede satisfacer lo que se ve en el análisis anterior,No puedo aceptarlo
3. Cambia, cambia
Entonces, ¿cómo mejorar el problema mencionado anteriormente? La respuesta es utilizar animaciones de atributos personalizadas para resolverlo, por lo que el punto principal de este artículo es utilizar animaciones de atributos para reemplazar el contador escrito a mano, intentando mantener la lógica del código lo más clara posible, especialmente el código en el método onDraw().
Una de las ventajas del uso de animaciones de atributos es que, dada una gama de valores, te ayuda a generar una serie de valores que deseas, y con el interpolador, incluso efectos inesperados. En la próxima página, se llevará a cabo una reconstrucción paso a paso de la parte ejecutada por la animación.
3.1 Dibujar la barra de progreso del círculo
Primero, use ObjectAnimator personalizado para simular el progreso
//ringProgress es el nombre del atributo personalizado, el rango de generación de valores es 0 - 360, es el ángulo de un círculo ObjectAnimator mRingAnimator = ObjectAnimator.ofInt(this, "ringProgress", 0, 360); //Definir el tiempo de ejecución de la animación, un excelente sustituto para el uso de la unidad de autoaumento anterior para controlar la velocidad de ejecución de la animación mRingAnimator.setDuration(mRingAnimatorDuration); //Por ahora no se necesita interpolador mRingAnimator.setInterpolator(null);
Para personalizar la animación de atributos, también es necesario configurar los setter y getter correspondientes, porque durante la ejecución de la animación, se buscará el setter correspondiente para cambiar el valor correspondiente.
private int getRingProgress() { return ringProgress; } private void setRingProgress(int ringProgress) { //Cuando se ejecuta la animación, se llama a setter //Aquí podemos registrar los valores generados por la animación, almacenarlos en variables y usarlos en el momento de ondraw this.ringProgress = ringProgress; //Recuerda redraw postInvalidate(); }
Finalmente, dibujar en onDraw()
//Dibujar el arco de progreso del círculo canvas.drawArc(mRectF, 90, ringProgress, false, mPaintRing);
3.2 Dibujar la animación de contracción hacia el centro del círculo
Del mismo modo, también creamos una animación de atributos
//Aquí, el atributo personalizado es el radio de contracción del círculo ObjectAnimator mCircleAnimator = ObjectAnimator.ofInt(this, "circleRadius", radius - 5, 0); //Agregar un interpolador de desaceleración mCircleAnimator.setInterpolator(new DecelerateInterpolator()); mCircleAnimator.setDuration(mCircleAnimatorDuration);
setter/getter también es similar, no se menciona
Finalmente, dibujar en onDraw()
//Dibujar el fondo mPaintCircle.setColor(checkBaseColor); canvas.drawCircle(centerX, centerY, ringProgress == 360 ? radio : 0, mPaintCircle); //Cuando se dibuja el círculo de progreso, dibuja el círculo de contracción if (ringProgress == 360) { mPaintCircle.setColor(checkTickColor); canvas.drawCircle(centerX, centerY, circleRadius, mPaintCircle); }
3.3 Dibujar el efecto de gancho, ampliación y rebote
Estos son dos efectos independientes, aquí se ejecutan al mismo tiempo, por lo que los menciono juntos
Primero también es definir la animación de atributos
//gradiente de transparencia del tick ObjectAnimator mAlphaAnimator = ObjectAnimator.ofInt(this, "tickAlpha", 0, 255); mAlphaAnimator.setDuration(200); //La última animación de ampliación y rebote cambia el ancho del pincel //Y el ancho del pincel, el rango de cambio es //Primero, desde el ancho de inicialización, hasta el n veces el ancho de inicialización, y finalmente vuelve al ancho de inicialización ObjectAnimator mScaleAnimator = ObjectAnimator.ofFloat(this, "ringStrokeWidth", mPaintRing.getStrokeWidth(), mPaintRing.getStrokeWidth() * SCALE_TIMES, mPaintRing.getStrokeWidth() / SCALE_TIMES); mScaleAnimator.setInterpolator(null); mScaleAnimator.setDuration(mScaleAnimatorDuration); //la animación de marcar y ampliar se ejecuta juntas AnimatorSet mAlphaScaleAnimatorSet = new AnimatorSet(); mAlphaScaleAnimatorSet.playTogether(mAlphaAnimator, mScaleAnimator);
getter/setter
private int getTickAlpha() { return 0; } private void setTickAlpha(int tickAlpha) { //configurar la transparencia, no es necesario guardar variables //simplemente configure el valor de transparencia dentro del pincel mPaintTick.setAlpha(tickAlpha); postInvalidate(); } private float getRingStrokeWidth() { return mPaintRing.getStrokeWidth(); } private void setRingStrokeWidth(float strokeWidth) { //configurar el ancho del pincel, no es necesario guardar variables //simplemente configure el ancho del pincel dentro del pincel mPaintRing.setStrokeWidth(strokeWidth); postInvalidate(); }
finalmente, al igual que en onDraw(), simplemente dibujar
if (circleRadius == 0) { canvas.drawLines(mPoints, mPaintTick); canvas.drawArc(mRectF, 0, 360, false, mPaintRing); }
3.4 ejecutar animaciones sucesivamente
para ejecutar múltiples animaciones, se puede usar AnimatorSet, donde playTogether() ejecuta todas juntas, y playSequentially() ejecuta una tras otra, paso a paso
mFinalAnimatorSet = new AnimatorSet(); mFinalAnimatorSet.playSequentially(mRingAnimator, mCircleAnimator, mAlphaScaleAnimatorSet);
finalmente, ejecutar la animación en onDraw()
//aquí se define un identificador que le dice al programa que la animación solo se puede ejecutar una vez if (!isAnimationRunning) { isAnimationRunning = true; //ejecutar animación mFinalAnimatorSet.start(); }
3.5 cada método debe tener una única responsabilidad
Si coloco el método de definición de animación de atributos en onDraw(), personalmente siento que está muy desordenado, y al mirarlo con más detalle, estos atributos de animación no necesitan cambiar dinámicamente, ¿por qué no los extraer y inicializarlos al principio?
por lo tanto, definiremos el código de animación de atributos y lo pondremos en la inicialización del constructor
public TickView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); ... initAnimatorCounter(); }
/** * inicializar algunos contadores con ObjectAnimator */ private void initAnimatorCounter() { //progreso del círculo ObjectAnimator mRingAnimator = ObjectAnimator.ofInt(this, "ringProgress", 0, 360); ... //animación de contracción ObjectAnimator mCircleAnimator = ObjectAnimator.ofInt(this, "circleRadius", radius - 5, 0); ... //gradiente de transparencia del tick ObjectAnimator mAlphaAnimator = ObjectAnimator.ofInt(this, "tickAlpha", 0, 255); ... //La última animación de ampliación y rebote cambia el ancho del pincel ObjectAnimator mScaleAnimator = ObjectAnimator.ofFloat(this, "ringStrokeWidth", mPaintRing.getStrokeWidth(), mPaintRing.getStrokeWidth() * SCALE_TIMES, mPaintRing.getStrokeWidth() / SCALE_TIMES); ... //la animación de marcar y ampliar se ejecuta juntas AnimatorSet mAlphaScaleAnimatorSet = new AnimatorSet(); mAlphaScaleAnimatorSet.playTogether(mAlphaAnimator, mScaleAnimator); mFinalAnimatorSet = new AnimatorSet(); mFinalAnimatorSet.playSequentially(mRingAnimator, mCircleAnimator, mAlphaScaleAnimatorSet); }
Finalmente, el método onDraw() se encarga solo de la dibujo simple, sin preocuparse por nada más
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (!isChecked) { canvas.drawArc(mRectF, 90, 360, false, mPaintRing); canvas.drawLines(mPoints, mPaintTick); return; } //dibujar un arco de progreso canvas.drawArc(mRectF, 90, ringProgress, false, mPaintRing); //dibujar un fondo amarillo mPaintCircle.setColor(checkBaseColor); canvas.drawCircle(centerX, centerY, ringProgress == 360 ? radio : 0, mPaintCircle); //Dibujar el círculo blanco de contracción if (ringProgress == 360) { mPaintCircle.setColor(checkTickColor); canvas.drawCircle(centerX, centerY, circleRadius, mPaintCircle); } //Dibujar ticks y animación de expansión y contracción if (circleRadius == 0) { canvas.drawLines(mPoints, mPaintTick); canvas.drawArc(mRectF, 0, 360, false, mPaintRing); } //Reemplazo de animación ObjectAnimator del contador if (!isAnimationRunning) { isAnimationRunning = true; mFinalAnimatorSet.start(); } }
El efecto final es el mismo, la lógica del código es clara a la vista
Por lo tanto, personalmente creo que, en el desarrollo, es muy útil revisar el código propio a tiempo, ya sea para uno mismo o para la mantenimiento futuro.
That's all~ Gracias por leer, aquí también dejo la dirección de github del proyecto
> Dirección de Github: TickView, una pequeña animación de tick refinadahttps://github.com/ChengangFeng/TickView
Esto es todo lo que he recopilado para ustedes sobre la implementación de la función de animación de ticks personalizados en View, pueden probarlo, si tienen alguna pregunta, pueden discutirla en la sección de comentarios a continuación, gracias por su apoyo a la tutorial de grito.