一、引入
其实之前一直以为像饿了么或者是美团外卖那种把商品添加到购物车的动画会很难做,但是实际做起来好像并没有想象中的那么难哈哈。
布局主要使用CoordinatorLayout+AppBarLayout+CollapsingToolbarLayout+TabLayout+ViewPager
动画主要使用二阶贝塞尔曲线与属性动画
消息传递使用EventBus普通事件
二、大致思路

如图所示主要有三个点,起点、终点、以及贝塞尔曲线的控制点
起点即点击的View的位置,一般来说用如下方式即可取得。
startPosition[0]为x轴开始坐标,startPosition[1]为Y轴终点坐标,两点可以看作对角线上面的两个端点(左上角x坐标,右下角y坐标)
//贝塞尔起始数据点int[] startPosition = new int[2];view.getLocationOnScreen(startPosition);
- 终点即购物车篮子的位置,与起点类似
mShoppingCart.getLocationInWindow(endPosition);
- 控制点,我选的控制点为上图的C点,即A点的y坐标,B点的X坐标
controlPosition[0] = endPosition[0];controlPosition[1] = startPosition[1];
- 需要注意的地方,我不清楚是不是因为我的布局的问题,获取到的点击的A点总是会有一个偏移,后来经同事提醒,减去了TabLayout的坐标的y轴坐标即位置才可以。
// 起点int[] startPosition;// 终点int[] endPosition = new int[2];// 贝塞尔控制点int[] controlPosition = new int[2];// tablayout位置int[] tablayoutPosition = new int[2];startPosition = data.getStartPosition();mShoppingCart.getLocationInWindow(endPosition);mTabLayout.getLocationInWindow(tablayoutPosition);// 处理起点y坐标偏移的问题startPosition[1] = startPosition[1] - tablayoutPosition[1] - mTabLayout.getHeight();// 终点进行一下居中处理endPosition[0] = endPosition[0] + (mShoppingCart.getWidth() / 2);controlPosition[0] = endPosition[0];controlPosition[1] = startPosition[1];
- 通过
Path的quadTo方法绘制贝塞尔曲线,使用PathMeasure获取点的坐标(借助ValueAnimator.ofFloat()配合getPosTan()来获取坐标)
Path path = new Path();path.moveTo(startPosition[0], startPosition[1]);path.quadTo(controlPosition[0], controlPosition[1], endPosition[0], endPosition[1]);PathMeasure pathMeasure = new PathMeasure();// false表示path路径不闭合pathMeasure.setPath(path, false);// ofFloat是一个生成器ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, pathMeasure.getLength());// 匀速线性插值器valueAnimator.setInterpolator(new LinearInterpolator());valueAnimator.setDuration(800);valueAnimator.addUpdateListener(animation -> {float value = (Float) animation.getAnimatedValue();pathMeasure.getPosTan(value, currentPosition, null);imageView.setX(currentPosition[0]);imageView.setY(currentPosition[1]);});valueAnimator.start();
- 下面是用属性动画给购物车篮子做了一个放大缩小的动画效果
// mShoppingCart是ViewObjectAnimator shoppingCartX = ObjectAnimator.ofFloat(mShoppingCart, "scaleX", 1.0f, 1.3f, 1.0f);ObjectAnimator shoppingCartY = ObjectAnimator.ofFloat(mShoppingCart, "scaleY", 1.0f, 1.3f, 1.0f);shoppingCartX.setInterpolator(new AccelerateInterpolator());shoppingCartY.setInterpolator(new AccelerateInterpolator());AnimatorSet shoppingCart = new AnimatorSet();shoppingCart.play(shoppingCartX).with(shoppingCartY);shoppingCart.setDuration(800);shoppingCart.start();
三、稍完整的大部分代码
private void AddAnimation(AddEventBean data) {// 起点int[] startPosition;// 终点int[] endPosition = new int[2];// 贝塞尔控制点int[] controlPosition = new int[2];// 当前位置float[] currentPosition = new float[2];// tablayout位置int[] tablayoutPosition = new int[2];startPosition = data.getStartPosition();mShoppingCart.getLocationInWindow(endPosition);mTabLayout.getLocationInWindow(tablayoutPosition);// 处理起点y坐标偏移的问题startPosition[1] = startPosition[1] - tablayoutPosition[1] - mTabLayout.getHeight();// 终点进行一下居中处理endPosition[0] = endPosition[0] + (mShoppingCart.getWidth() / 2);controlPosition[0] = endPosition[0];controlPosition[1] = startPosition[1];final ImageView imageView = new ImageView(this);mConView.addView(imageView);imageView.setImageResource(R.drawable.specialadd);imageView.getLayoutParams().width = getResources().getDimensionPixelSize(R.dimen.dp_px_30);imageView.getLayoutParams().height = getResources().getDimensionPixelSize(R.dimen.dp_px_30);imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);imageView.setVisibility(View.VISIBLE);imageView.setX(startPosition[0]);imageView.setY(startPosition[1]);Path path = new Path();path.moveTo(startPosition[0], startPosition[1]);path.quadTo(controlPosition[0], controlPosition[1], endPosition[0], endPosition[1]);PathMeasure pathMeasure = new PathMeasure();// false表示path路径不闭合pathMeasure.setPath(path, false);// ofFloat是一个生成器ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, pathMeasure.getLength());// 匀速线性插值器valueAnimator.setInterpolator(new LinearInterpolator());valueAnimator.setDuration(800);valueAnimator.addUpdateListener(animation -> {float value = (Float) animation.getAnimatedValue();pathMeasure.getPosTan(value, currentPosition, null);imageView.setX(currentPosition[0]);imageView.setY(currentPosition[1]);});valueAnimator.start();ObjectAnimator shoppingCartX = ObjectAnimator.ofFloat(mShoppingCart, "scaleX", 1.0f, 1.3f, 1.0f);ObjectAnimator shoppingCartY = ObjectAnimator.ofFloat(mShoppingCart, "scaleY", 1.0f, 1.3f, 1.0f);shoppingCartX.setInterpolator(new AccelerateInterpolator());shoppingCartY.setInterpolator(new AccelerateInterpolator());AnimatorSet shoppingCart = new AnimatorSet();shoppingCart.play(shoppingCartX).with(shoppingCartY);shoppingCart.setDuration(800);shoppingCart.start();valueAnimator.addListener(new Animator.AnimatorListener() {@Overridepublic void onAnimationStart(Animator animation) {}//当动画结束后:@Overridepublic void onAnimationEnd(Animator animation) {goodsChange(data);}@Overridepublic void onAnimationCancel(Animator animation) {}@Overridepublic void onAnimationRepeat(Animator animation) {}});}
四、大致写下布局(同时也算留做备份)

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"... ...><RelativeLayout... ...>顶部常驻的toolbar</RelativeLayout><android.support.design.widget.CoordinatorLayoutandroid:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"><android.support.design.widget.AppBarLayout... ...><android.support.design.widget.CollapsingToolbarLayout... ...app:layout_scrollFlags="scroll|exitUntilCollapsed"><LinearLayout... ...>TabLayout上面的View</LinearLayout></android.support.design.widget.CollapsingToolbarLayout><android.support.design.widget.TabLayout... ... /></android.support.design.widget.AppBarLayout><RelativeLayout... ...android:fillViewport="true"app:layout_behavior="@string/appbar_scrolling_view_behavior"><android.support.v4.view.ViewPagerandroid:id="@+id/view_pager"android:layout_width="match_parent"android:layout_height="match_parent" /></RelativeLayout></android.support.design.widget.CoordinatorLayout><LinearLayout... ...>最下面的购物车一栏</LinearLayout></LinearLayout>
