English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Efecto de animación de agregar productos al carrito de compras en Android (curva de Bézier)

Cuando escribimos proyectos de tienda en línea, generalmente tendremos la función de agregar al carrito, al agregar al carrito habrá algunos animaciones parabólicas, el código específico es el siguiente:

El efecto implementado se muestra en la siguiente imagen:

Enfoque:

  1. Determinar los puntos de inicio y final de la animación
  2. Usar una curva cuadrática Bezier para rellenar la trayectoria entre los puntos de inicio y final
  3. Configurar la animación de atributos, interpolador ValueAnimator, obtener las coordenadas del punto medio
  4. Establecer las coordenadas x, y del controlador que ejecutará la animación en las coordenadas del punto medio obtenidas anteriormente
  5. Activar la animación de atributos
  6. Acciones al finalizar la animación

Dificultad:

Uso de PathMeasure
- getLength()
- Entendimiento de boolean getPosTan(float distance, float[] pos, float[] tan)

涉及到的知识点:

如何获取控件在屏幕中的绝对坐标

//得到父布局的起始点坐标(用于辅助计算动画开始/结束时的点的坐标)
  int[] parentLocation = new int[2];
  rl.getLocationInWindow(parentLocation);

如何使用贝塞尔曲线以及属性动画插值器ValueAnimator
  

 //    四、计算中间动画的插值坐标(贝塞尔曲线)(其实就是用贝塞尔曲线来完成起终点的过程)
    //开始绘制贝塞尔曲线
    Path path = new Path();
    //移动到起始点(贝塞尔曲线的起点)
    path.moveTo(startX, startY);
    //使用二次萨贝尔曲线:注意第一个起始坐标越大,贝塞尔曲线的横向距离就会越大,一般按照下面的式子取即可
    path.quadTo((startX + toX) / 2, startY, toX, toY);
    //mPathMeasure用来计算贝塞尔曲线的曲线长度和贝塞尔曲线中间插值的坐标,
    // 如果是true,path会形成一个闭环
    mPathMeasure = new PathMeasure(path, false);
    //★★★属性动画实现(从0到贝塞尔曲线的长度之间进行插值计算,获取中间过程的距离值)
    ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, mPathMeasure.getLength());
    valueAnimator.setDuration(1000);
    // Interpolador lineal uniforme
    valueAnimator.setInterpolator(new LinearInterpolator());
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
      @Override
      public void onAnimationUpdate(ValueAnimator animation) {
        // Cuando se realiza el cálculo de interpolación, obtener cada valor intermedio,
        // Este valor es la longitud de la curva en el proceso intermedio (se utilizará este valor para obtener el valor de la coordenada del punto central)
        float value = (Float) animation.getAnimatedValue();
        // ★★★★★ Empaquetar la coordenada del punto actual en mCurrentPosition
        // boolean getPosTan(float distance, float[] pos, float[] tan) :
        // Ingrese una distancia distance (0 <= distance <= getLength()), luego se calculará la distancia actual
        // La posición del punto y la tangente, pos se llenará automáticamente con la coordenada, este método es muy importante.
        mPathMeasure.getPosTan(value, mCurrentPosition, null);//mCurrentPosition en este momento es el valor de la distancia al punto central
        // La posición del producto móvil (imagen de animación) se establece en la coordenada del punto central
        goods.setTranslationX(mCurrentPosition[0]);
        goods.setTranslationY(mCurrentPosition[1});
      }
    });
//   Cinco, comenzar a ejecutar la animación
    valueAnimator.start();

所有代码:

package cn.c.com.beziercurveanimater;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.LinearInterpolator;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
  private RecyclerView mRecyclerView;
  private ImageView cart;
  private ArrayList<Bitmap> bitmapList = new ArrayList<>();
  private RelativeLayout rl;
  private PathMeasure mPathMeasure;
  /**
   * Puntos de coordenadas en el proceso central de la curva bezier
   */
  private float[] mCurrentPosition = new float[2];
  private TextView count;
  private int i = 0;
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    initView();
    initImg();
    MyAdapter myAdapter = new MyAdapter(bitmapList);
    mRecyclerView.setAdapter(myAdapter);
    mRecyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this));
  }
  private void initImg() {
    bitmapList.add(BitmapFactory.decodeResource(getResources(), R.drawable.coin));
    bitmapList.add(BitmapFactory.decodeResource(getResources(), R.drawable.coin1));
    bitmapList.add(BitmapFactory.decodeResource(getResources(), R.drawable.coin91));
  }
  private void initView() {
    mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
    cart = (ImageView) findViewById(R.id.cart);
    rl = (RelativeLayout) findViewById(R.id.rl);
    count = (TextView) findViewById(R.id.count);
  }
  class MyAdapter extends RecyclerView.Adapter<MyVH> {
    private ArrayList<Bitmap> bitmapList;
    public MyAdapter(ArrayList<Bitmap> bitmapList) {
      this.bitmapList = bitmapList;
    }
    @Override
    public MyVH onCreateViewHolder(ViewGroup parent, int viewType) {
      LayoutInflater inflater = LayoutInflater.from(MainActivity.this);
      View itemView = inflater.inflate(R.layout.item, parent, false);
      MyVH myVH = new MyVH(itemView);
      return myVH;
    }
    @Override
    public void onBindViewHolder(final MyVH holder, final int position) {
      holder.iv.setImageBitmap(bitmapList.get(position));
      holder.buy.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
          addCart(holder.iv);
        }
      });
    }
    @Override
    public int getItemCount() {
      return bitmapList.size();
    }
  }
  /**
   * ★★★★★ Efecto de animación para agregar productos al carrito de compras ★★★★★
   * @param iv
   */
  private void addCart(ImageView iv) {
//   1. Crear el tema de la animación---ImageView
    //Código para crear un ImageView, el recurso de la imagen es el ImageView superior
    // Esta imagen es la que realiza el animación, comienza desde la posición inicial, pasa por una parábola (curva bezier) y se mueve al carrito de compras)
    final ImageView goods = new ImageView(MainActivity.this);
    goods.setImageDrawable(iv.getDrawable());
    RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(100, 100);
    rl.addView(goods, params);
//    二、计算动画开始/结束点的坐标的准备工作
    //得到父布局的起始点坐标(用于辅助计算动画开始/结束时的点的坐标)
    int[] parentLocation = new int[2];
    rl.getLocationInWindow(parentLocation);
    //得到商品图片的坐标(用于计算动画开始的坐标)
    int startLoc[] = new int[2];
    iv.getLocationInWindow(startLoc);
    //得到购物车图片的坐标(用于计算动画结束后的坐标)
    int endLoc[] = new int[2];
    cart.getLocationInWindow(endLoc);
//    三、正式开始计算动画开始/结束的坐标
    //开始掉落的商品的起始点:商品起始点-父布局起始点+该商品图片的一半
    float startX = startLoc[0] - parentLocation[0] + iv.getWidth() / 2;
    float startY = startLoc[1] - parentLocation[1] + iv.getHeight() / 2;
    //商品掉落后的终点坐标:购物车起始点-父布局起始点+购物车图片的1/5
    float toX = endLoc[0] - parentLocation[0] + cart.getWidth() / 5;
    float toY = endLoc[1] - parentLocation[1];
//    四、计算中间动画的插值坐标(贝塞尔曲线)(其实就是用贝塞尔曲线来完成起终点的过程)
    //开始绘制贝塞尔曲线
    Path path = new Path();
    //移动到起始点(贝塞尔曲线的起点)
    path.moveTo(startX, startY);
    //使用二次萨贝尔曲线:注意第一个起始坐标越大,贝塞尔曲线的横向距离就会越大,一般按照下面的式子取即可
    path.quadTo((startX + toX) / 2, startY, toX, toY);
    //mPathMeasure用来计算贝塞尔曲线的曲线长度和贝塞尔曲线中间插值的坐标,
    // 如果是true,path会形成一个闭环
    mPathMeasure = new PathMeasure(path, false);
    //★★★属性动画实现(从0到贝塞尔曲线的长度之间进行插值计算,获取中间过程的距离值)
    ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, mPathMeasure.getLength());
    valueAnimator.setDuration(1000);
    // Interpolador lineal uniforme
    valueAnimator.setInterpolator(new LinearInterpolator());
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
      @Override
      public void onAnimationUpdate(ValueAnimator animation) {
        // Cuando se realiza el cálculo de interpolación, obtener cada valor intermedio,
        // Este valor es la longitud de la curva en el proceso intermedio (se utilizará este valor para obtener el valor de la coordenada del punto central)
        float value = (Float) animation.getAnimatedValue();
        // ★★★★★ Empaquetar la coordenada del punto actual en mCurrentPosition
        // boolean getPosTan(float distance, float[] pos, float[] tan) :
        // Ingrese una distancia distance (0 <= distance <= getLength()), luego se calculará la distancia actual
        // La posición del punto y la tangente, pos se llenará automáticamente con la coordenada, este método es muy importante.
        mPathMeasure.getPosTan(value, mCurrentPosition, null);//mCurrentPosition en este momento es el valor de la distancia al punto central
        // La posición del producto móvil (imagen de animación) se establece en la coordenada del punto central
        goods.setTranslationX(mCurrentPosition[0]);
        goods.setTranslationY(mCurrentPosition[1});
      }
    });
//   Cinco, comenzar a ejecutar la animación
    valueAnimator.start();
//   Seis, el manejo después del final de la animación
    valueAnimator.addListener(new Animator.AnimatorListener() {
      @Override
      public void onAnimationStart(Animator animation) {
      }
      //Después de que finalice la animación:}
      @Override
      public void onAnimationEnd(Animator animation) {
        // Aumentar la cantidad del carrito de compras1
        i++;
        count.setText(String.valueOf(i));
        // Remover la imagen móvil imageview del diseño padre
        rl.removeView(goods);
      }
      @Override
      public void onAnimationCancel(Animator animation) {
      }
      @Override
      public void onAnimationRepeat(Animator animation) {
      }
    });
  }
  class MyVH extends RecyclerView.ViewHolder {
    private ImageView iv;
    private TextView buy;
    public MyVH(View itemView) {
      super(itemView);
      iv = (ImageView) itemView.findViewById(R.id.iv);
      buy = (TextView) itemView.findViewById(R.id.buy);
    }
  }
}

Esto es todo el contenido de este artículo, espero que sea útil para su aprendizaje y que todos los demás lo apoyen mucho en el tutorial de alarido.

Declaración: Este artículo se ha redactado en línea, el derecho de autor pertenece al propietario original, el contenido se ha contribuido y subido por los usuarios de Internet, este sitio web no posee los derechos de propiedad, no se ha realizado la edición humana y no asume la responsabilidad legal correspondiente. Si encuentra contenido sospechoso de infracción de derechos de autor, por favor envíe un correo electrónico a: notice#w3Declaración: El contenido de este artículo se ha obtenido de la red, el derecho de autor pertenece al propietario original, el contenido se ha contribuido y subido por los usuarios de Internet, este sitio web no posee los derechos de propiedad, no se ha realizado la edición humana y no asume la responsabilidad legal correspondiente. Si encuentra contenido sospechoso de infracción de derechos de autor, por favor envíe un correo electrónico a: notice#w

Te gustaría que te recomendemos