Android利用二阶贝塞尔曲线实现添加购物车动画详解

作者:张钦 时间:2022-03-01 09:49:49 

一、引入

  1. 其实之前一直以为像饿了么或者是美团外卖那种把商品添加到购物车的动画会很难做,但是实际做起来好像并没有想象中的那么难哈哈。

  2. 布局主要使用CoordinatorLayout+AppBarLayout+CollapsingToolbarLayout+TabLayout+ViewPager

  3. 动画主要使用二阶贝塞尔曲线与属性动画

  4. 消息传递使用EventBus普通事件

Android利用二阶贝塞尔曲线实现添加购物车动画详解

二、大致思路

Android利用二阶贝塞尔曲线实现添加购物车动画详解

1、如图所示主要有三个点,起点、终点、以及贝塞尔曲线的控制点

2、起点即点击的View的位置,一般来说用如下方式即可取得。startPosition[0]为x轴开始坐标,startPosition[1]为Y轴终点坐标,两点可以看作对角线上面的两个端点(左上角x坐标,右下角y坐标)


//贝塞尔起始数据点
int[] startPosition = new int[2];
view.getLocationOnScreen(startPosition);

3、终点即购物车篮子的位置,与起点类似


mShoppingCart.getLocationInWindow(endPosition);

4、控制点,我选的控制点为上图的C点,即A点的y坐标,B点的X坐标


controlPosition[0] = endPosition[0];
controlPosition[1] = startPosition[1];

5、需要注意的地方,我不清楚是不是因为我的布局的问题,获取到的点击的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];

6、通过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();

7、下面是用属性动画给购物车篮子做了一个放大缩小的动画效果


// mShoppingCart是View
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();

三、稍完整的大部分代码


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() {
@Override
public void onAnimationStart(Animator animation) {

}

//当动画结束后:
@Override
public void onAnimationEnd(Animator animation) {
goodsChange(data);
}

@Override
public void onAnimationCancel(Animator animation) {

}

@Override
public void onAnimationRepeat(Animator animation) {

}
});
}

四、大致写下布局(同时也算留做备份)

Android利用二阶贝塞尔曲线实现添加购物车动画详解


<?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.CoordinatorLayout
android: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.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>

</android.support.design.widget.CoordinatorLayout>

<LinearLayout
... ...>

最下面的购物车一栏

</LinearLayout>
</LinearLayout>

五、推荐资源

View的位置参数

来源:https://juejin.im/post/5b69a0505188251af86c1301

标签:android,二阶贝塞尔曲线,购物车
0
投稿

猜你喜欢

  • Java并发编程面试之线程池

    2023-11-11 10:58:33
  • 详解如何实现SpringBoot的底层注解

    2023-11-23 06:21:04
  • Java Springboot的目的你知道吗

    2022-12-20 14:16:24
  • Spring Boot将项目打包成war包的操作方法

    2022-01-02 22:30:50
  • SpringMVC RESTFul实现列表功能

    2023-10-30 21:15:22
  • 基于synchronized修饰静态和非静态方法

    2021-10-30 06:58:03
  • Java编程实现基于用户的协同过滤推荐算法代码示例

    2022-09-06 12:44:24
  • Java如何设置系统参数和运行参数

    2023-12-19 10:47:01
  • Java使用 try-with-resources 实现自动关闭资源的方法

    2022-01-09 06:54:46
  • 介绍java中Pair(配对)

    2021-09-03 10:15:39
  • Android 开发之dataBinding与ListView及事件

    2023-10-13 21:14:28
  • Java 判断字符串中是否包含中文的实例详解

    2023-11-06 13:17:18
  • Java线程休眠的5种方法

    2022-02-21 04:49:48
  • 解决JSTL foEach标签 刷新报错的方法

    2021-08-12 13:48:25
  • 详解JAVA 弱引用

    2022-03-12 01:30:29
  • Android同步屏障机制sync barrier实例应用详解

    2023-04-24 05:27:04
  • SpringMVC上传文件的两种方法

    2023-11-17 14:27:20
  • C#基于socket模拟http请求的方法

    2022-09-12 09:55:11
  • 详解C++ bitset用法

    2022-10-30 08:57:16
  • 解决CollectionUtils.isNotEmpty()不存在的问题

    2021-11-15 12:03:17
  • asp之家 软件编程 m.aspxhome.com