Efecto de animación de corazón flotante de Android (me gusta en vivo) (2) -realización de la curva de Bezier

El último artículo de animación de corazón flotante de Android (Me gusta en vivo) solo afecta el código, sin instrucciones relacionadas. Como no lo entendí yo mismo, me remito a otro artículo en Internet sobre la curva de Bezier para lograr el efecto de animación de corazón flotante. El propósito es facilitar la comprensión del código del artículo anterior, y luego escribir mi propia comprensión de animación de corazón flotante.

El siguiente es el artículo al que me refiero: te enseño paso a paso para lograr el efecto de Periscope , -el artículo es del libro corto. También dibujo una calabaza según la calabaza, así que la defino como un artículo reimpreso, pero he agregado algo que entiendo en el artículo. Las representaciones son las siguientes:

Escriba la descripción de la imagen aquí


1. Definir el diseño de Piaoxin

Creo que todos pueden pensar fácilmente en usar RelativeLayout, sí, sí, luego definamos un Layout primero, heredemos de RelativeLayout, y sobrecarguemos el constructor y definamos algunas variables.

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.widget.RelativeLayout;
import com.myapplication2.app.R;

import java.util.Random;

/**
 * http://www.jianshu.com/p/03fdcfd3ae9c
 * https://github.com/AlanCheen/PeriscopeLayout/blob/master/library/src/main/java/me/yifeiyuan/library/PeriscopeLayout.java
 *
 * 参考实现的自定义飘心动画的布局
 * time:2016年8月31日10:10:34
 * @see android.widget.RelativeLayout
 */
public class PeriscopeLayout extends RelativeLayout{
    
    

    private int dHeight; //爱心的高度
    private int dWidth; //爱心的宽度
    private int mHeight; //自定义布局的高度
    private int mWidth;  //自定义布局的宽度

    private LayoutParams layoutParams;
    private Random random = new Random();  //用于获取随机心的随机数
    private Drawable[] drawables;  //存放初始化图片的数组

    /**
     * 是在java代码创建视图的时候被调用,如果是从xml填充的视图,就不会调用这个
     */
    public PeriscopeLayout(Context context) {
        super(context);
        init();
    }

    /**
     * 这个是在xml创建但是没有指定style的时候被调用
     */
    public PeriscopeLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    /**
     * 这个是在xml创建但是 有指定style的时候被调用
     */
    public PeriscopeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }


    /**
     * 初始化布局和随机心型图片
     */
    private void init(){

        //初始化显示的图片,暂时使用3 种图片
        drawables = new Drawable[3];

        //getResources().getDrawable 过期的替代方法 ContextCompat.getDrawable(getContext(),R.drawable.heart3);
//        Drawable red = getResources().getDrawable(R.drawable.heart3);
        Drawable red = ContextCompat.getDrawable(getContext(),R.drawable.heart3);
        Drawable yellow = ContextCompat.getDrawable(getContext(),R.drawable.heart8);
        Drawable blue = ContextCompat.getDrawable(getContext(),R.drawable.heart6);

        drawables[0] = red;
        drawables[1] = yellow;
        drawables[2] = blue;

        //获取图的宽高 用于后面的计算
        //注意 我这里3张图片的大小都是一样的,所以我只取了一个
        dHeight = red.getIntrinsicHeight();
        dWidth = red.getIntrinsicWidth();

        //定义心型图片出现的位置,底部 水平居中
        layoutParams = new LayoutParams(dWidth,dHeight);
        layoutParams.addRule(CENTER_HORIZONTAL,TRUE);
        layoutParams.addRule(ALIGN_PARENT_BOTTOM,TRUE);


    }

    /**
     * http://blog.csdn.net/pi9nc/article/details/18764863
     * 自定义布局 onMeasure 的作用
     * 获取控件的实际高度
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        //注意!!  获取本身的宽高 需要在测量之后才有宽高
        mWidth = getMeasuredWidth();
        mHeight = getMeasuredHeight();
    }
}

1.1 Dónde lidiar con la imagen del corazón

// Defina la posición donde aparece la imagen en forma de corazón, y la parte inferior está centrada horizontalmente
layoutParams = new LayoutParams (dWidth, dHeight);
layoutParams.addRule (CENTER_HORIZONTAL, TRUE);
layoutParams.addRule (ALIGN_PARENT_BOTTOM, TRUE);

1.2 Use una matriz para almacenar la imagen en forma de corazón predeterminada

//getResources().getDrawable método alternativo caducado ContextCompat.getDrawable (getContext (), R.drawable.heart3);

Dibujable rojo = ContextCompat.getDrawable (getContext (), R.drawable.heart3);
………
..
dibujables [0] = rojo;
dibujables [1] = amarillo;
dibujables [2] = azul;

1.3 Las imágenes aleatorias se pueden obtener directamente mediante el método de números aleatorios

drawables [random.nextInt (3)] // representa un número aleatorio de 0-2, tenga en cuenta que 3 no está disponible, es un intervalo abierto

1.4 Descripción del método onMeasure

Escriba la descripción de la imagen aquí

getWidth (): el ancho de toda la vista después de establecer el diseño.
getMeasuredWidth (): el ancho ocupado por el contenido de la Vista obtenido después de medir el contenido en la Vista.

Las instrucciones específicas son las siguientes:
http://blog.csdn.net/pi9nc/article/details/18764863

2. Realice la animación de cambios de zoom y transparencia.

Es decir, mediante las propiedades directas de escala animada de ObjectAnimator y la transparencia de los cambios de animación
para agregar el archivo correspondiente que se puede poner PeriscopeLayout.java

Escriba la descripción de la imagen aquí

2.1 Implementación de animación de atributos

/**
     * 通过属性动画 实现爱心图片的缩放和透明度变化的动画效果
     * target 就是爱心图片的view
     */
    private AnimatorSet getEnterAnimtor(final View target){


        ObjectAnimator scaleX = ObjectAnimator.ofFloat(target, View.SCALE_X, 0.2f, 1f);
        ObjectAnimator scaleY = ObjectAnimator.ofFloat(target, View.SCALE_Y, 0.2f, 1f);

        AnimatorSet enter = new AnimatorSet();
        enter.setDuration(500);
        enter.setInterpolator(new LinearInterpolator());//线性变化
        enter.playTogether(scaleX,scaleY);
        enter.setTarget(target);

        return enter;
    }

 /**
     * 动画结束后,remove
     */
    private class AnimEndListener extends AnimatorListenerAdapter {
    
    
        private View target;

        public AnimEndListener(View target) {
            this.target = target;
        }

        @Override
        public void onAnimationEnd(Animator animation) {
            super.onAnimationEnd(animation);
            //因为不停的add 导致子view数量只增不减,所以在view动画结束后remove掉
            removeView((target));
        }
    }

2.2 Proporcionar la implementación de métodos externos

/**
     * 提供外部实现点击效果,只有缩放和变淡的效果
     */
    public void addFavorWithoutBiz(){
        ImageView imageView = new ImageView(getContext());
        //随机心型颜色
        imageView.setImageDrawable(drawables[random.nextInt(3)]);
        imageView.setLayoutParams(layoutParams);

        addView(imageView);

        Animator set = getEnterAnimtor(imageView);
        set.addListener(new AnimEndListener(imageView));
        set.start();
    }

2.3 El efecto de la ausencia de curva de Bezier

Escriba la descripción de la imagen aquí


3. Realice el efecto de la curva de Bezier

Explicación de la curva de Bezier de la enciclopedia Baidu

Los
cuatro puntos de las fórmulas cúbicas P0, P1, P2 y P3 definen una curva de Bezier cúbica en un plano o en un espacio tridimensional. La curva comienza de P0 a P1 y de P2 a P3. Generalmente, no pasará por P1 o P2; estos dos puntos solo están ahí para proporcionar información de dirección. La distancia entre P0 y P1 determina la "longitud" de la curva en la dirección de P2 antes de girar a P3.
La forma de parámetro de la curva es:

Escriba la descripción de la imagen aquí

Los sistemas de imágenes modernos, como PostScript, Asymptote y Metafont, utilizan curvas de Bézier cúbicas compuestas por splines de Bézier para describir el contorno de la curva.

Escriba la descripción de la imagen aquí


La fórmula requiere cuatro P, P0, es nuestro punto de partida, P3 es el punto final, P1, P2 son dos puntos de la ruta
y nosotros es un factor en el rango 0-1


3.1 BezierEvaluator personalizado para implementar TypeEvaluator

import android.animation.TypeEvaluator;
import android.graphics.PointF;

/**
 * 我们自定义一个BezierEvaluator 实现 TypeEvaluator
 * 由于我们view的移动需要控制x y 所以就传入PointF 作为参数,是不是感觉完全契合
 */
public class BezierEvaluator implements TypeEvaluator<PointF> {
    
    

    private PointF pointF1;
    private PointF pointF2;

    public BezierEvaluator(PointF pointF1,PointF pointF2){
        this.pointF1 = pointF1;
        this.pointF2 = pointF2;
    }

    @Override
    public PointF evaluate(float time, PointF startValue,
                           PointF endValue) {

        float timeLeft = 1.0f - time;
        PointF point = new PointF();//结果

        point.x = timeLeft * timeLeft * timeLeft * (startValue.x)
                + 3 * timeLeft * timeLeft * time * (pointF1.x)
                + 3 * timeLeft * time * time * (pointF2.x)
                + time * time * time * (endValue.x);

        point.y = timeLeft * timeLeft * timeLeft * (startValue.y)
                + 3 * timeLeft * timeLeft * time * (pointF1.y)
                + 3 * timeLeft * time * time * (pointF2.y)
                + time * time * time * (endValue.y);
        return point;
    }
}

3.2 Definir la realización de la animación de la curva de Bezier

/**
     * 贝塞尔曲线的动画实现
     */

    private ValueAnimator getBezierValueAnimator(View target) {
        //初始化一个BezierEvaluator
        BezierEvaluator evaluator = new BezierEvaluator(getPointF(2), getPointF(1));

        //第一个PointF传入的是初始点的位置
        ValueAnimator animator = ValueAnimator.ofObject(evaluator, new PointF((mWidth - dWidth) / 2, mHeight - dHeight-20), new PointF(random.nextInt(getWidth()), 0));//随机
        animator.addUpdateListener(new BezierListenr(target));
        animator.setTarget(target);
        animator.setDuration(3000);
        return animator;
    }

    /**
     * 获取中间的两个点
     */
    private PointF getPointF(int scale) {

        PointF pointF = new PointF();
        pointF.x = random.nextInt((mWidth - 50));//减去50 是为了控制 x轴活动范围,看效果 随意~~
        //再Y轴上 为了确保第二个点 在第一个点之上,我把Y分成了上下两半 这样动画效果好一些  也可以用其他方法
        pointF.y = random.nextInt((mHeight - 150)) / scale;
        return pointF;
    }

    /**
     * 只有在回调里使用了计算的值,才能真正做到曲线运动
     */
    private class BezierListenr implements ValueAnimator.AnimatorUpdateListener {
    
    
        private View target;

        public BezierListenr(View target) {
            this.target = target;
        }

        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            //这里获取到贝塞尔曲线计算出来的的x y值 赋值给view 这样就能让爱心随着曲线走啦
            PointF pointF = (PointF) animation.getAnimatedValue();
            target.setX(pointF.x);
            target.setY(pointF.y);

            // alpha动画
            target.setAlpha(1 - animation.getAnimatedFraction());
        }
    }

Nota: Si se encuentra que la posición inicial es incorrecta, hay un fenómeno de jitter en la
función getBezierValueAnimator.
ValueAnimator animator = ValueAnimator.ofObject (evaluator, new PointF ((mWidth-dWidth) / 2, mHeight-dHeight-20), new PointF (random.nextInt (getWidth ()), 0)); // aleatorio

El primer punto F punto para controlar la posición

Escriba la descripción de la imagen aquí

3.3 agregar método de llamada

public void addFavor() {
        ImageView imageView = new ImageView(getContext());
        imageView.setImageDrawable(drawables[random.nextInt(3)]);
        imageView.setLayoutParams(layoutParams);

        addView(imageView);

        Animator set = getAnimator(imageView);
        set.addListener(new AnimEndListener(imageView));
        set.start();
    }

3.4 Pantalla de efectos
El efecto final es el efecto que se muestra al principio.

4. Uso específico

Escriba la descripción de la imagen aquí

código xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
        android:background="@color/grey"
        android:alpha="0.5">

    <com.myapplication2.app.newsdemo.view.bizHeartview.PeriscopeLayout
            android:id="@+id/heart_layout"
            android:layout_alignParentRight="true"
            android:layout_width="100dp"
            android:layout_height="match_parent"/>

    <TextView
            android:id="@+id/member_send_good"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:layout_gravity="center"
            android:layout_alignParentBottom="true"
            android:layout_alignParentRight="true"
            android:layout_marginRight="30dp"
            android:layout_marginBottom="10dp"
            android:background="@drawable/live_like_icon"
            />

</RelativeLayout>

5. Materiales de referencia

http://www.jianshu.com/p/03fdcfd3ae9c
https://github.com/AlanCheen/PeriscopeLayout

6. Resumen

Descubrí que algunas cosas solo se veían a la vez, lo que no tenía ningún efecto. Solo haciéndolo yo mismo puedo conocer las dificultades que hay en su interior. Aunque se imita paso a paso, también he aprendido algunas cosas, como los puntos de conocimiento relacionados con las vistas personalizadas, y el uso de animaciones de atributos e interpoladores, porque no No sé lo que se necesita Verifique la información usted mismo, y el proceso de verificar la información es el proceso de aprendizaje.

Supongo que te gusta

Origin blog.csdn.net/android_freshman/article/details/52385278
Recomendado
Clasificación