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

Animation d'ajout de produits au panier sur Android (curbe de Bézier)

Lorsque nous écrivons un projet de classe de商城, nous avons généralement une fonction de panier d'achat, et lors de l'ajout au panier, il y a des animations paraboloïdales, le code spécifique est le suivant :

L'effet réalisé est représenté comme suit :

Idée :

  1. Déterminer les points de départ et d'arrivée de l'animation
  2. Utiliser une courbe de Bézier quadratic pour combler la trajectoire entre le début et la fin entre les points
  3. Définir l'animation des propriétés, l'interpolateur ValueAnimator, obtenir les coordonnées du point central
  4. Définir les coordonnées x, y du contrôle qui exécute l'animation comme le point central entre le début et la fin
  5. Activer l'animation des propriétés
  6. Opérations à effectuer à la fin de l'animation

Difficulté :

L'utilisation de PathMeasure
- getLength()
- Comprendre la méthode 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);
    // 匀速线性插值器
    valueAnimator.setInterpolator(new LinearInterpolator());
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
      @Override
      public void onAnimationUpdate(ValueAnimator animation) {
        // 当插值计算进行时,获取中间的每个值,
        // 这里这个值是中间过程中的曲线长度(下面根据这个值来得出中间点的坐标值)
        float value = (Float) animation.getAnimatedValue();
        // ★★★★★获取当前点坐标封装到mCurrentPosition
        // boolean getPosTan(float distance, float[] pos, float[] tan) :
        // 传入一个距离distance(0<=distance<=getLength()),然后会计算当前距
        // 离的坐标点和切线,pos会自动填充上坐标,这个方法很重要。
        mPathMeasure.getPosTan(value, mCurrentPosition, null);//mCurrentPosition此时就是中间距离点的坐标值
        // 移动的商品图片(动画图片)的坐标设置为该中间点的坐标
        goods.setTranslationX(mCurrentPosition[0]);
        goods.setTranslationY(mCurrentPosition[1});
      }
    });
//   五、 开始执行动画
    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;
  /**
   * coordonnées des points du milieu de la courbe de Bézier
   */
  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();
    }
  }
  /**
   * ★★★★★ Effet d'animation pour ajouter un article au panier ★★★★★
   * @param iv
   */
  private void addCart(ImageView iv) {
//   1. Créer le thème de l'animation---ImageView
    //Code pour créer un ImageView avec la ressource d'image de l'ImageView ci-dessus
    // Ce fichier image est celui utilisé pour l'animation, qui part de la position de départ, suit une parabole (courbe de Bézier) et se déplace vers le panier d'achat)
    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);
    // 匀速线性插值器
    valueAnimator.setInterpolator(new LinearInterpolator());
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
      @Override
      public void onAnimationUpdate(ValueAnimator animation) {
        // 当插值计算进行时,获取中间的每个值,
        // 这里这个值是中间过程中的曲线长度(下面根据这个值来得出中间点的坐标值)
        float value = (Float) animation.getAnimatedValue();
        // ★★★★★获取当前点坐标封装到mCurrentPosition
        // boolean getPosTan(float distance, float[] pos, float[] tan) :
        // 传入一个距离distance(0<=distance<=getLength()),然后会计算当前距
        // 离的坐标点和切线,pos会自动填充上坐标,这个方法很重要。
        mPathMeasure.getPosTan(value, mCurrentPosition, null);//mCurrentPosition此时就是中间距离点的坐标值
        // 移动的商品图片(动画图片)的坐标设置为该中间点的坐标
        goods.setTranslationX(mCurrentPosition[0]);
        goods.setTranslationY(mCurrentPosition[1});
      }
    });
//   五、 开始执行动画
    valueAnimator.start();
//   六、动画结束后的处理
    valueAnimator.addListener(new Animator.AnimatorListener() {
      @Override
      public void onAnimationStart(Animator animation) {
      }
      //Après la fin de l'animation :
      @Override
      public void onAnimationEnd(Animator animation) {
        // Ajouter la quantité du panier1
        i++;
        count.setText(String.valueOf(i));
        // Retirer l'imageview mobile de la vue parente
        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);
    }
  }
}

Voici la totalité du contenu de cet article, j'espère qu'il vous aidera dans vos études, et j'espère que vous soutiendrez également le tutoriel d'alarme.

Déclaration : ce contenu est issu du réseau, la propriété intellectuelle appartient à l'auteur original, le contenu est apporté par les utilisateurs d'Internet et téléchargé spontanément, ce site Web ne possède pas de propriété, n'a pas été édité par l'homme, ni n'assume la responsabilité juridique. Si vous trouvez du contenu suspect de violation de droits d'auteur, veuillez envoyer un e-mail à : notice#w3Déclaration : le contenu de cet article est issu du réseau, la propriété intellectuelle appartient à l'auteur original, le contenu est apporté par les utilisateurs d'Internet et téléchargé spontanément, ce site Web ne possède pas de propriété, n'a pas été édité par l'homme, ni n'assume la responsabilité juridique. Si vous trouvez du contenu suspect de violation de droits d'auteur, veuillez envoyer un e-mail à : notice#w

Vous pourriez aussi aimer