Android -- 贝塞尔二阶实现饿了么加入购物车效果

时间:2023-01-01 14:19:42

1,上周我们实现了简单的三阶贝塞尔曲线效果实例,今天是使用二阶贝塞尔曲线加动画实现的加入购物车效果,在码代码过程中出现了些问题,过一下和大家来探讨探讨,先看一下效果图

  Android -- 贝塞尔二阶实现饿了么加入购物车效果

2,从上面的效果来看我们基本上可以把功能拆分为两个动画效果:+号图片按照曲线掉下(曲线的轨迹就是一个简单的贝塞尔曲线)、购物车图标从缩小到放大。知道了实现的原理我们开始我们功能的实现

  • 实现基本基本布局、RecyclerView展示数据

  从上面的效果我们可以得到,我们的布局是一个简单的RecyclerView和下面的RelativeLayout,布局文件如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/main_layout"
tools:context="com.qianmo.beziershopcart.MainActivity"> <android.support.v7.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"> </android.support.v7.widget.RecyclerView> <LinearLayout
android:id="@+id/shopping_cart_bottom"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_alignParentBottom="true"
android:background="#fd383838"
android:orientation="horizontal"> </LinearLayout> <FrameLayout
android:id="@+id/shopping_cart_layout"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_alignParentBottom="true"
android:layout_marginBottom="5dp"
android:layout_marginLeft="35dp"
android:background="@drawable/circle"
android:clickable="true"> <ImageView
android:id="@+id/shopping_cart"
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_gravity="center"
android:src="@mipmap/ic_shopping_cart_white_24dp"/>
</FrameLayout>
</RelativeLayout>

  添加Shop实体类、添加RecyclerView的Adapter

package com.qianmo.beziershopcart;

/**
* Created by wangjitao on 2017/4/10 0010.
* E-Mail:543441727@qq.com
*/ public class ShopBean { private String title;
private String price;
private int count; public ShopBean(String title, String price, int count) {
this.title = title;
this.price = price;
this.count = count;
} public String getTitle() {
return title;
} public void setTitle(String title) {
this.title = title;
} public String getPrice() {
return price;
} public void setPrice(String price) {
this.price = price;
} public int getCount() {
return count;
} public void setCount(int count) {
this.count = count;
}
}

  适配器

package com.qianmo.beziershopcart;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast; import java.util.List; /**
* Created by Administrator on 2017/4/10 0010.
* E-Mail:543441727@qq.com
*/ public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private List<ShopBean> datas;
private Context mContext;
private ShopOnClickListtener mShopOnClickListtener; public MyAdapter(List<ShopBean> datas, Context mContext) {
this.datas = datas;
this.mContext = mContext;
} @Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new ViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item_shop_menu, parent, false));
} @Override
public void onBindViewHolder(MyAdapter.ViewHolder holder, final int position) {
holder.shop_name.setText(datas.get(position).getTitle());
holder.shop_price.setText(datas.get(position).getPrice());
holder.count.setText(datas.get(position).getCount() + ""); holder.ic_add.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mShopOnClickListtener != null) {
mShopOnClickListtener.add(v, position);
}
}
});
holder.ic_reduce.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mShopOnClickListtener != null) {
mShopOnClickListtener.remove(v, position);
} }
});
} @Override
public int getItemCount() {
return datas == null ? 0 : datas.size();
} public class ViewHolder extends RecyclerView.ViewHolder {
TextView shop_name;
TextView shop_price;
TextView count;
ImageView ic_add;
ImageView ic_reduce; public ViewHolder(View itemView) {
super(itemView);
shop_name = (TextView) itemView.findViewById(R.id.tv_title);
shop_price = (TextView) itemView.findViewById(R.id.tv_price);
count = (TextView) itemView.findViewById(R.id.tv_count);
ic_add = (ImageView) itemView.findViewById(R.id.iv_add);
ic_reduce = (ImageView) itemView.findViewById(R.id.iv_remove);
}
} public ShopOnClickListtener getShopOnClickListtener() {
return mShopOnClickListtener;
} public void setShopOnClickListtener(ShopOnClickListtener mShopOnClickListtener) {
this.mShopOnClickListtener = mShopOnClickListtener;
} public interface ShopOnClickListtener { void add(View view, int position); void remove(View view, int position);
}
}

  在Activity中简单的添加数据

package com.qianmo.beziershopcart;

import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Point;
import android.graphics.PointF;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.animation.AccelerateInterpolator;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout; import java.util.ArrayList;
import java.util.List; public class MainActivity extends AppCompatActivity implements MyAdapter.ShopOnClickListtener {
private Context mContext = MainActivity.this; private RelativeLayout main_layout;
private RecyclerView mRecyclerView;
private ImageView mImageViewShopCat;
private List<ShopBean> datas;
private MyAdapter myAdapter; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView(); } private void initView() {
mRecyclerView = (RecyclerView) findViewById(R.id.recycler);
mImageViewShopCat = (ImageView) findViewById(R.id.shopping_cart);
main_layout = (RelativeLayout) findViewById(R.id.main_layout);
initData();
} private void initData() {
datas = new ArrayList<>();
datas.add(new ShopBean("面包", "1.00", 10));
datas.add(new ShopBean("蛋挞", "1.00", 10));
datas.add(new ShopBean("牛奶", "1.00", 10));
datas.add(new ShopBean("肠粉", "1.00", 10));
datas.add(new ShopBean("绿茶饼", "1.00", 10));
datas.add(new ShopBean("花卷", "1.00", 10));
datas.add(new ShopBean("包子", "1.00", 10)); datas.add(new ShopBean("粥", "1.00", 10));
datas.add(new ShopBean("炒饭", "1.00", 10));
datas.add(new ShopBean("炒米粉", "1.00", 10));
datas.add(new ShopBean("炒粿条", "1.00", 10));
datas.add(new ShopBean("炒牛河", "1.00", 10));
datas.add(new ShopBean("炒菜", "1.00", 10)); datas.add(new ShopBean("淋菜", "1.00", 10));
datas.add(new ShopBean("川菜", "1.00", 10));
datas.add(new ShopBean("湘菜", "1.00", 10));
datas.add(new ShopBean("粤菜", "1.00", 10));
datas.add(new ShopBean("赣菜", "1.00", 10));
datas.add(new ShopBean("东北菜", "1.00", 10)); datas.add(new ShopBean("淋菜", "1.00", 10));
datas.add(new ShopBean("川菜", "1.00", 10));
datas.add(new ShopBean("湘菜", "1.00", 10));
datas.add(new ShopBean("粤菜", "1.00", 10));
datas.add(new ShopBean("赣菜", "1.00", 10));
datas.add(new ShopBean("东北菜", "1.00", 10)); mRecyclerView.setLayoutManager(new LinearLayoutManager(mContext));
mRecyclerView.addItemDecoration(new RecyclerViewDivider(mContext, LinearLayoutManager.VERTICAL, 50, ContextCompat.getColor(mContext, R.color.colorAccent)));
myAdapter = new MyAdapter(datas, mContext);
mRecyclerView.setAdapter(myAdapter);
myAdapter.setShopOnClickListtener(this);
} @Override
public void add(final View view, int position) {
Toast.makeText(mContext, "加", Toast.LENGTH_SHORT).show();
} @Override
public void remove(View view, int position) {
Toast.makeText(mContext, "减", Toast.LENGTH_SHORT).show();
}
}

  看一下我们的效果:

  Android -- 贝塞尔二阶实现饿了么加入购物车效果

  OK,大致的展示效果基本上是实现了

  • 使用加入购物车效果

  下面我们来实现贝塞尔曲线效果,首先获取两个数据点:一个是每次点击“+”号的坐标位置、一个是下面红色的购物车图标。控制点我打算取开始点的Y坐标和结束点的X坐标,那么怎么获取当前控件相对于整个屏幕的坐标呢,这里View里面有一个getLocationInWindow()方法(这里要留心一下这个方法),然后不了解的同学可以搜索一下view.getLocationInWindow(int[] location)和view.getLocationOnScreen(int[] location),方法的区别,这里我从网上偷了了一个图,如下:

Android -- 贝塞尔二阶实现饿了么加入购物车效果

  getLocationInWindow是以B为原点的C的坐标
  getLocationOnScreen以A为原点

  ok,这样我们看一下我们的贝塞尔三个坐标的初始化

 @Override
public void add(final View view, int position) {
//贝塞尔起始数据点
int[] startPosition = new int[2];
//贝塞尔结束数据点
int[] endPosition = new int[2];
//控制点
int[] recyclerPosition = new int[2]; view.getLocationInWindow(startPosition);
mImageViewShopCat.getLocationInWindow(endPosition); PointF startF = new PointF();
PointF endF = new PointF();
PointF controllF = new PointF(); startF.x = startPosition[0];
startF.y = startPosition[1] ;
endF.x = endPosition[0];
endF.y = endPosition[1];
controllF.y = startF.y;
}

  继续,自定义差值器,我们按照贝塞尔的三阶公式来套,上一篇详细的介绍过了就不在废话了,这里不懂的话建议你去看一下我的上一篇博客,里面有详细的介绍

  Android -- 贝塞尔二阶实现饿了么加入购物车效果

package com.qianmo.beziershopcart;

import android.animation.TypeEvaluator;
import android.graphics.PointF; /**
* Created by Administrator on 2017/4/10 0010.
* E-Mail:543441727@qq.com
*/ public class BezierTypeEvaluator implements TypeEvaluator<PointF> {
private PointF mControllPoint; public BezierTypeEvaluator(PointF mControllPoint) {
this.mControllPoint = mControllPoint;
} @Override
public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
PointF pointCur = new PointF();
pointCur.x = (1 - fraction) * (1 - fraction) * startValue.x + 2 * fraction * (1 - fraction) * mControllPoint.x + fraction * fraction * endValue.x;
pointCur.y = (1 - fraction) * (1 - fraction) * startValue.y + 2 * fraction * (1 - fraction) * mControllPoint.y + fraction * fraction * endValue.y;
return pointCur;
}
}

  开启动画自动调用

@Override
public void add(final View view, int position) {
//贝塞尔起始数据点
int[] startPosition = new int[2];
//贝塞尔结束数据点
int[] endPosition = new int[2]; view.getLocationInWindow(startPosition);
mImageViewShopCat.getLocationInWindow(endPosition); PointF startF = new PointF();
PointF endF = new PointF();
PointF controllF = new PointF(); startF.x = startPosition[0];
startF.y = startPosition[1] ;
endF.x = endPosition[0];
endF.y = endPosition[1];
controllF.x = endF.x;
controllF.y = startF.y; ValueAnimator valueAnimator = ValueAnimator.ofObject(new BezierTypeEvaluator(controllF), startF, endF);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
PointF pointF = (PointF) animation.getAnimatedValue();
view.setX(pointF.x);
view.setY(pointF.y);
// Log.i("wangjtiao", "viewF:" + view.getX() + "," + view.getY());
}
});
valueAnimator .setDuration(800);
valueAnimator .start();
}

  按照我们的思路,这个功能基本上是实现了,感觉很简单,又可以多了些时间去打两把王者农药了,看一下我们的效果图:

  Android -- 贝塞尔二阶实现饿了么加入购物车效果

  呃!!!我的动画呢??? ,看一下我们打印动画轨迹的效果

04-10 03:55:37.271 29510-29510/com.qianmo.beziershopcart I/wangjtiao: viewF:899.42816,608.6747
04-10 03:55:37.306 29510-29510/com.qianmo.beziershopcart I/wangjtiao: viewF:841.05066,615.05853
04-10 03:55:37.369 29510-29510/com.qianmo.beziershopcart I/wangjtiao: viewF:700.9506,644.04346
04-10 03:55:37.399 29510-29510/com.qianmo.beziershopcart I/wangjtiao: viewF:626.5742,669.1555
04-10 03:55:37.427 29510-29510/com.qianmo.beziershopcart I/wangjtiao: viewF:552.1316,703.05035
04-10 03:55:37.474 29510-29510/com.qianmo.beziershopcart I/wangjtiao: viewF:477.8907,747.9864
04-10 03:55:37.502 29510-29510/com.qianmo.beziershopcart I/wangjtiao: viewF:410.36102,801.63885
04-10 03:55:37.528 29510-29510/com.qianmo.beziershopcart I/wangjtiao: viewF:348.99896,864.93835
04-10 03:55:37.549 29510-29510/com.qianmo.beziershopcart I/wangjtiao: viewF:320.21585,901.0647
04-10 03:55:37.601 29510-29510/com.qianmo.beziershopcart I/wangjtiao: viewF:247.92773,1019.08307
04-10 03:55:37.629 29510-29510/com.qianmo.beziershopcart I/wangjtiao: viewF:210.69424,1104.1696
04-10 03:55:37.650 29510-29510/com.qianmo.beziershopcart I/wangjtiao: viewF:194.71275,1149.3108
04-10 03:55:37.672 29510-29510/com.qianmo.beziershopcart I/wangjtiao: viewF:180.81482,1194.8556
04-10 03:55:37.716 29510-29510/com.qianmo.beziershopcart I/wangjtiao: viewF:150.94038,1327.2924
04-10 03:55:37.744 29510-29510/com.qianmo.beziershopcart I/wangjtiao: viewF:138.87828,1409.5398
04-10 03:55:37.771 29510-29510/com.qianmo.beziershopcart I/wangjtiao: viewF:134.5641,1449.0864
04-10 03:55:37.799 29510-29510/com.qianmo.beziershopcart I/wangjtiao: viewF:129.06772,1518.4921
04-10 03:55:37.813 29510-29510/com.qianmo.beziershopcart I/wangjtiao: viewF:127.390335,1549.7286
04-10 03:55:37.852 29510-29510/com.qianmo.beziershopcart I/wangjtiao: viewF:125.646034,1600.0463
04-10 03:55:37.873 29510-29510/com.qianmo.beziershopcart I/wangjtiao: viewF:125.263214,1620.108
04-10 03:55:37.902 29510-29510/com.qianmo.beziershopcart I/wangjtiao: viewF:125.0176,1646.6577
04-10 03:55:37.932 29510-29510/com.qianmo.beziershopcart I/wangjtiao: viewF:125.0,1656.0

  view的坐标一直在改变啊!思路上没什么问题啊,想了好久,会不会是因为这是RecyclerView的item的ImageView,是不是它不能超过它父控件的距离呢?(到最后我也没搞懂为什么不显示view,如果知道的同学请告知一下),姑且算是这个问题,那我们就每次都new一个ImageView,添加到主布局中,代码如下:

@Override
public void add(final View view, int position) {
//贝塞尔起始数据点
int[] startPosition = new int[2];
//贝塞尔结束数据点
int[] endPosition = new int[2];
//控制点
int[] recyclerPosition = new int[2]; view.getLocationInWindow(startPosition);
mImageViewShopCat.getLocationInWindow(endPosition);
mRecyclerView.getLocationInWindow(recyclerPosition); PointF startF = new PointF();
PointF endF = new PointF();
PointF controllF = new PointF(); startF.x = startPosition[0];
startF.y = startPosition[1];
endF.x = endPosition[0];
endF.y = endPosition[1];
controllF.x = endF.x;
controllF.y = startF.y; Log.i("wangjtiao", "startF:" + startF.x + "," + startF.y);
Log.i("wangjtiao", "endF:" + endF.x + "," + endF.y);
Log.i("wangjtiao", "ControllF:" + endF.x + "," + controllF.y);
Log.i("wangjtiao", "ControllF:" + recyclerPosition[0] + "," + recyclerPosition[1]); final ImageView imageView = new ImageView(this);
main_layout.addView(imageView);
imageView.setImageResource(R.mipmap.ic_add_circle_blue_700_36dp);
imageView.getLayoutParams().width = getResources().getDimensionPixelSize(R.dimen.item_dish_circle_size);
imageView.getLayoutParams().height = getResources().getDimensionPixelSize(R.dimen.item_dish_circle_size);
imageView.setVisibility(View.VISIBLE);
imageView.setX(startF.x);
imageView.setY(startF.y); ValueAnimator valueAnimator = ValueAnimator.ofObject(new BezierTypeEvaluator(controllF), startF, endF);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
PointF pointF = (PointF) animation.getAnimatedValue();
imageView.setX(pointF.x);
imageView.setY(pointF.y);
Log.i("wangjtiao", "viewF:" + view.getX() + "," + view.getY());
}
}); valueAnimator.setDuration(800);
valueAnimator.start();
}

  效果如下:

  Android -- 贝塞尔二阶实现饿了么加入购物车效果

  ??这又是什么鬼,两个控制点出问题了??既然控制点出问题了我们来看看我们控制点怎么得到的:view.getLocationInWindow(int[] location)和view.getLocationOnScreen(int[] location),,感觉也没什么问题啊   我们打印一下我们的贝塞尔两个数据点和一个控制点

04-10 02:45:33.179 32056-32056/com.qianmo.beziershopcart I/wangjtiao: startF:1014.0,242.0
04-10 02:45:33.183 32056-32056/com.qianmo.beziershopcart I/wangjtiao: endF:125.0,1656.0
04-10 02:45:33.183 32056-32056/com.qianmo.beziershopcart I/wangjtiao: ControllF:125.0,242.0

  ok,这里我们看不到我们的效果,我们将view.getLocationInWindow(int[] location)替换成view.getLocationOnScreen(int[] location)

04-10 02:40:44.161 27735-27735/com.qianmo.beziershopcart I/wangjtiao: startF:1014.0,242.0
04-10 02:40:44.161 27735-27735/com.qianmo.beziershopcart I/wangjtiao: endF:125.0,1656.0
04-10 02:45:33.183 32056-32056/com.qianmo.beziershopcart I/wangjtiao: ControllF:125.0,242.0

  ???为什么两次获取的坐标都是一样的,还有我们的上面动画为什么会偏差这个多,感觉这个高度貌似是我们的状态栏和标题栏的高度啊  ,为了验证我们的猜想,我们隐藏掉状态栏和标题栏试试

1 //获取当前窗体
2 final Window window = getWindow();
3 //隐藏状态栏
4 window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
5 //隐藏标题栏
6 requestWindowFeature(Window.FEATURE_NO_TITLE);

  效果如下:

Android -- 贝塞尔二阶实现饿了么加入购物车效果

  效果实现了,没问题,说明真的是这个问题,那view.getLocationInWindow(int[] location)按照别人博客里面说的就是取的相对坐标啊,我们获取一下我们主界面的mRecyclerView的坐标看看

04-10 02:45:33.183 32056-32056/com.qianmo.beziershopcart I/wangjtiao: ControllF:0,210

  可以看到我们的标题栏+状态栏高度是210,但是我们起始点的坐标是(1014,242)这个有问题啊,应该是(1014,32)这样我们的动画才会在正确的起始位置和结束位置啊。带着我们的疑问,查了半天资料,终于在一个论坛发现了

因为你的代码显示的界面  contentView < window = screen
为什么会相等呢,因为此时的Window就是包含状态栏+contentView的大小
不是你认为的 contentView = window < screen
除非你的view是从dialog 或者 popupWindow 上显示,
这时候getLocationInWindow获得的值就是相对的坐标。

  所以getLocationInWindow方法获取的是状态栏+contentView的大小,而不是我们以为的contentView的大小,知道了这里我们基本上就知道怎么解决这个问题了,只需要减去这个高度就可以了,我们可以直接减去mRecyclerView的Y坐标即可,代码如下:

 @Override
public void add(final View view, int position) {
//贝塞尔起始数据点
int[] startPosition = new int[2];
//贝塞尔结束数据点
int[] endPosition = new int[2];
//控制点
int[] recyclerPosition = new int[2]; view.getLocationInWindow(startPosition);
mImageViewShopCat.getLocationInWindow(endPosition);
mRecyclerView.getLocationInWindow(recyclerPosition); PointF startF = new PointF();
PointF endF = new PointF();
PointF controllF = new PointF(); startF.x = startPosition[0];
startF.y = startPosition[1] - recyclerPosition[1];
endF.x = endPosition[0];
endF.y = endPosition[1] - recyclerPosition[1];
controllF.x = endF.x;
controllF.y = startF.y; final ImageView imageView = new ImageView(this);
main_layout.addView(imageView);
imageView.setImageResource(R.mipmap.ic_add_circle_blue_700_36dp);
imageView.getLayoutParams().width = getResources().getDimensionPixelSize(R.dimen.item_dish_circle_size);
imageView.getLayoutParams().height = getResources().getDimensionPixelSize(R.dimen.item_dish_circle_size);
imageView.setVisibility(View.VISIBLE);
imageView.setX(startF.x);
imageView.setY(startF.y); ValueAnimator valueAnimator = ValueAnimator.ofObject(new BezierTypeEvaluator(controllF), startF, endF);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
PointF pointF = (PointF) animation.getAnimatedValue();
imageView.setX(pointF.x);
imageView.setY(pointF.y);
Log.i("wangjtiao", "viewF:" + view.getX() + "," + view.getY());
}
}); valueAnimator.setDuration(800);
valueAnimator.start();
}

  看一下效果:

  Android -- 贝塞尔二阶实现饿了么加入购物车效果

  没问题了,这里的位置也没什么问题,再添加动画完成监听,移除刚刚new的ImageView

valueAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) { } @Override
public void onAnimationEnd(Animator animation) {
imageView.setVisibility(View.GONE);
main_layout.removeView(imageView);
} @Override
public void onAnimationCancel(Animator animation) { } @Override
public void onAnimationRepeat(Animator animation) { }
});
  • 购物车图标从缩小到放大的实现

  这个就很简单了,就是一个简单的属性动画,然后将上面这几个动画放在集合中,代码如下:

@Override
public void add(final View view, int position) {
//贝塞尔起始数据点
int[] startPosition = new int[2];
//贝塞尔结束数据点
int[] endPosition = new int[2];
//控制点
int[] recyclerPosition = new int[2]; view.getLocationInWindow(startPosition);
mImageViewShopCat.getLocationInWindow(endPosition);
mRecyclerView.getLocationInWindow(recyclerPosition); PointF startF = new PointF();
PointF endF = new PointF();
PointF controllF = new PointF(); startF.x = startPosition[0];
startF.y = startPosition[1] - recyclerPosition[1];
endF.x = endPosition[0];
endF.y = endPosition[1] - recyclerPosition[1];
controllF.x = endF.x;
controllF.y = startF.y; final ImageView imageView = new ImageView(this);
main_layout.addView(imageView);
imageView.setImageResource(R.mipmap.ic_add_circle_blue_700_36dp);
imageView.getLayoutParams().width = getResources().getDimensionPixelSize(R.dimen.item_dish_circle_size);
imageView.getLayoutParams().height = getResources().getDimensionPixelSize(R.dimen.item_dish_circle_size);
imageView.setVisibility(View.VISIBLE);
imageView.setX(startF.x);
imageView.setY(startF.y); ValueAnimator valueAnimator = ValueAnimator.ofObject(new BezierTypeEvaluator(controllF), startF, endF);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
PointF pointF = (PointF) animation.getAnimatedValue();
imageView.setX(pointF.x);
imageView.setY(pointF.y);
Log.i("wangjtiao", "viewF:" + view.getX() + "," + view.getY());
}
}); ObjectAnimator objectAnimatorX = new ObjectAnimator().ofFloat(mImageViewShopCat, "scaleX", 0.6f, 1.0f);
ObjectAnimator objectAnimatorY = new ObjectAnimator().ofFloat(mImageViewShopCat, "scaleY", 0.6f, 1.0f);
objectAnimatorX.setInterpolator(new AccelerateInterpolator());
objectAnimatorY.setInterpolator(new AccelerateInterpolator());
AnimatorSet set = new AnimatorSet();
set.play(objectAnimatorX).with(objectAnimatorY).after(valueAnimator);
set.setDuration(800);
set.start();
}

  最后的效果:

  Android -- 贝塞尔二阶实现饿了么加入购物车效果

  ok,这样就实现了我们的效果了,有没有很简单,二阶且三个点都是固定的贝塞尔还是很简单的嘛,github代码下载

  但是这里还有个遗留的问题还是没搞懂,为什么RecyclerView的item的ImageView的动画效果不显示!!!!知道的同学请留言或者私信一下,感激(抱拳)了各位。。。

Android -- 贝塞尔二阶实现饿了么加入购物车效果的更多相关文章

  1. Android 贝塞尔曲线解析

    相信很多同学都知道"贝塞尔曲线"这个词,我们在很多地方都能经常看到.利用"贝塞尔曲线"可以做出很多好看的UI效果,本篇博客就让我们一起学习"贝塞尔曲线 ...

  2. css贝塞尔曲线模仿饿了么购物车小球动画

    在线观看贝塞尔曲线值:传送门 在线观看动画效果:传送门 代码: <!DOCTYPE html> <html> <head> <meta charset=&qu ...

  3. Android自定义View 画弧形&comma;文字&comma;并增加动画效果

    一个简单的Android自定义View的demo,画弧形,文字,开启一个多线程更新ui界面,在子线程更新ui是不允许的,但是View提供了方法,让我们来了解下吧. 1.封装一个抽象的View类   B ...

  4. Android 聊天表情输入、表情翻页带效果、下拉刷新聊天记录

    经过一个星期的折腾,最终做完了这个Android 聊天表情输入.表情翻页带效果.下拉刷新聊天记录.这仅仅是一个单独聊天表情的输入,以及聊天的效果实现.由于我没有写server,所以没有两方聊天的效果. ...

  5. 搭建Android&plus;QT&plus;OpenCV环境,实现&OpenCurlyDoubleQuote;单色图片着色”效果

               OpenCV是我们大家非常熟悉的图像处理开源类库:在其新版本将原本在Contrib分库中的DNN模块融合到了主库中,并且更新了相应文档.这样我们就能够非常方便地利用OpenCV实 ...

  6. Android -- 贝塞尔曲线公式的推导

    1,最近看了几个不错的自定义view,发现里面都会涉及到贝塞尔曲线知识,深刻的了解到贝塞尔曲线是进阶自定义view的一座大山,so,今天先和大家来了解了解. 2,贝塞尔曲线作用十分广泛,简单举几个的栗 ...

  7. Android -- 贝塞尔实现水波纹动画(划重点!!)

    1,昨天看到了一个挺好的ui效果,是使用贝塞尔曲线实现的,就和大家来分享分享,还有,在写博客的时候我经常会把自己在做某种效果时的一些问题给写出来,而不是像很多文章直接就给出了解决方法,这里给大家解释一 ...

  8. Android -- 贝塞尔曲线公式的推导和简单使用

    1,最近看了几个不错的自定义view,发现里面都会涉及到贝塞尔曲线知识,深刻的了解到贝塞尔曲线是进阶自定义view的一座大山,so,今天先和大家来了解了解. 2,贝塞尔曲线作用十分广泛,简单举几个的栗 ...

  9. Android 贝塞尔曲线 折线图

    1.贝塞尔曲线:http://baike.baidu.com/view/60154.htm,在这里理解什么是贝塞尔曲线 2.直接上图: 3.100多行代码就可以画出贝塞尔曲线,直接上代码 packag ...

随机推荐

  1. &lbrack;LeetCode&rsqb; Self Crossing 自交

    You are given an array x of n positive numbers. You start at point (0,0) and moves x[0] metres to th ...

  2. C&sol;C&plus;&plus;&colon;提升&lowbar;头文件的使用

    C/C++:提升_头文件的使用 ◇写在前面 学到现在,很多人编写程序时只会使用一个文件.这样在代码量较小的时候,更利于表达程序,但是随着代码量的逐步增加,程序的思维逻辑不是我们一下子就可以完全理清的, ...

  3. Java学习笔记(四)&mdash&semi;&mdash&semi;流程控制语句

    一.条件语句 1.if条件语句 (1)语法: if(条件) { 条件成立时的代码 } (2)执行过程 2.if else语句 if(条件) { 条件成立时的代码 } else { 不成立的代码 } 3 ...

  4. Tiny Rss简明安装与配置笔记

    对于某些自己喜欢的却没有RSS订阅功能的网站,可以通过Feed43定制抓取规则来获取所需信息.但由于其服务器在国外,抓取时常失败,且免费账户抓取频率太低,遂仿造他自行实现了一个并挂到服务器上,然后通过 ...

  5. 关于结构化BOM的思考

    参加了今天的"自主生产音箱类产品BOM结构问题"(即非采购而是制造的音箱)会议,我发现大家在会议上呈现的产品结构对生产计划的层级需求已上升到5层的需求了,又找段会胜要了各位前期就此 ...

  6. Swift - 使用UI Dynamics给UIKit组件添加重力和碰撞行为

    UI Dynamics是UIKit的一个新组成部分,它向iOS中的视图提供了与物理学有关的功能和动画.可以让你向视图中引入力和物理属性,可以让你的视图弹跳,舞动,受重力影响等等. 下面通过样例,演示使 ...

  7. iOS 调试 之 打印

    参考:http://m.blog.csdn.net/blog/HookyStudent/42964317 参考:http://m.blog.csdn.net/blog/laencho/25190639 ...

  8. 一个字母引发的血案 java&period;io&period;File中mkdir&lpar;&rpar;和mkdirs&lpar;&rpar;

    一个字母引发的血案 明天开始放年假了,临放假前有个爬虫的任务,其中需要把网络图片保存到本地,很简单,马上写完了代码: //省略部分代码... Long fileId= (Long) data.get( ...

  9. Python学习之MySQLdb模块

    摘要: MySQLdb模块用于操作mysql数据库.1.安装MySQLdb模块 yum install MySQL-python -y2.操作流程①.导入模块: import MySQLdb②.连接数 ...

  10. mfc视类中错误:IntelliSense&colon; declaration is incompatible with。。。解决方案

    基本情况是我自己写了一个类: class CDib {....} 然后在mfc自动生成的“工程名Dlg.cpp”中使用类CDib,我的工程名是MfcPictureProcessing,所以类是clas ...