demo效果:
l 购物车特效原理:
1.从添加按钮获取开始坐标
2.从购物车图标获取结束坐标
3.打气一个视图,添加属性动画ObjectAnimator(缩小),ValueAnimator(路线)
4.动画开始时添加该视图,动画结束删除该视图
5.运动路径使用TypeEvaluator与贝塞尔函数计算
activity布局:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <!--贝塞尔曲线动画自定义控件--> <custom.BezierAnim android:id="@+id/bezier_anim" android:layout_width="match_parent" android:layout_height="match_parent" /> <!--添加按钮--> <Button android:id="@+id/bt_good" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_margin="10dp" android:text="+" /> <!--购物车--> <ImageView android:id="@+id/iv_cart" android:layout_width="50dp" android:layout_height="50dp" android:layout_alignParentBottom="true" android:src="@drawable/cart91" /> </RelativeLayout>
移动控件布局:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="50dp" android:layout_height="50dp" android:orientation="vertical"> <ImageView android:layout_width="50dp" android:layout_height="50dp" android:src="@drawable/coin91" /> </RelativeLayout>
贝塞尔曲线动画的自定义控件编写:
public class BezierAnim extends FrameLayout { public BezierAnim(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public BezierAnim(Context context) { this(context, null, 0); } public BezierAnim(Context context, AttributeSet attrs) { this(context, attrs, 0); } //PointF等价于float[]数组,里面存放的x和y的值 (point相当于int[]) private PointF mLocation = new PointF();//这样创建出来,里面还没有值 @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); //获取当前父布局在界面的屏幕坐标(也就是父布局左上角坐标) int[] layoutLoc = new int[2]; getLocationInWindow(layoutLoc); mLocation.set(layoutLoc[0], layoutLoc[1]);//将父布局左上角的值赋值给mLocation } /** * 开始贝塞尔动画 * * @param startView 动画从哪个view开始(+号) * @param endView 动画在哪个view结束(购物车) * @param layoutIdMove 动画作用的移动控件(钱袋子的布局) */ public void startCartAnim(View startView, View endView, int layoutIdMove) { //1,开始位置 int[] startLoc = new int[2]; startView.getLocationInWindow(startLoc);//获取当前view在屏幕上的坐标 PointF startF = new PointF(startLoc[0] - mLocation.x, startLoc[1] - mLocation.y);//得到当前view相对于父布局左上角位置的坐标 // 2,结束位置 int[] endLoc = new int[2]; endView.getLocationInWindow(endLoc); final PointF endF = new PointF(endLoc[0] - mLocation.x, endLoc[1] - mLocation.y); //3.移动控件。inflate()参数:作用布局,参考布局,false final View moveView = LayoutInflater.from(getContext()).inflate(layoutIdMove, this, false); //开始动画 使用属性动画合集 AnimatorSet set = new AnimatorSet(); //缩小动画 ObjectAnimator scaleXAnim = ObjectAnimator.ofFloat(moveView, "scaleX", 1.0f, 0.1f); ObjectAnimator scaleYAnim = ObjectAnimator.ofFloat(moveView, "scaleY", 1.0f, 0.1f); //路径动画(baisaier曲线路径,开始坐标,结束坐标) ValueAnimator pathAnim = ObjectAnimator.ofObject(beisaier, startF, endF); pathAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { //更新坐标 PointF newPointF = (PointF) animation.getAnimatedValue(); moveView.setX(newPointF.x); moveView.setY(newPointF.y); } }); //将这些动画放入集合中 set.playTogether(scaleXAnim, scaleYAnim, pathAnim); Animator.AnimatorListener listener = new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { BezierAnim.this.addView(moveView);//加入动画作用的控件 } @Override public void onAnimationEnd(Animator animation) { BezierAnim.this.removeView(moveView);//移除动画作用的控件 } @Override public void onAnimationCancel(Animator animation) {} @Override public void onAnimationRepeat(Animator animation) {} }; set.addListener(listener);//动画播放监听器 set.setDuration(1000); //运动时间 set.start(); //时间 } //路径计算器 private TypeEvaluator<PointF> beisaier = new TypeEvaluator<PointF>() { @Override public PointF evaluate(float fraction, PointF startValue, PointF endValue) { //返回变化的轨迹坐标 PointF newF = new PointF((startValue.x + endValue.x) / 2, 0); return BezierCurve.bezier(fraction, startValue, newF, endValue); } }; }
路径计算器:
public class BezierCurve { /** * 二次贝塞尔曲线插值 * t:值范围from = 0, to = 1 */ public static PointF bezier(float t, PointF point0, PointF point1, PointF point2) { float oneMinusT = 1.0f - t; PointF point = new PointF(); point.x = oneMinusT * oneMinusT * point0.x + 2 * t * oneMinusT * point1.x + t * t * point2.x; point.y = oneMinusT * oneMinusT * point0.y + 2 * t * oneMinusT * point1.y + t * t * point2.y; return point; } /** * 三次贝塞尔曲线插值 * t:值范围from = 0, to = 1 */ public static PointF bezier(float t, PointF point0, PointF point1, PointF point2, PointF point3) { float oneMinusT = 1.0f - t; PointF point = new PointF(); point.x = oneMinusT * oneMinusT * oneMinusT * (point0.x) + 3 * oneMinusT * oneMinusT * t * (point1.x) + 3 * oneMinusT * t * t * (point2.x) + t * t * t * (point3.x); point.y = oneMinusT * oneMinusT * oneMinusT * (point0.y) + 3 * oneMinusT * oneMinusT * t * (point1.y) + 3 * oneMinusT * t * t * (point2.y) + t * t * t * (point3.y); return point; } }
activity开始动画:
public class CartBazierActivity extends Activity { @InjectView(R.id.bt_good) Button btGood; @InjectView(R.id.iv_cart) ImageView ivCart; @InjectView(R.id.bezier_anim) BezierAnim bezierAnim; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_cart_bezier); ButterKnife.inject(this); } @OnClick(R.id.bt_good) public void bezierMove() { Toast.makeText(this, "添加了一件商品", Toast.LENGTH_SHORT).show(); bezierAnim.startCartAnim(btGood, ivCart, R.layout.moveview); } }
若是startView和endView不在一个界面,可以用EventBus传值
更多:http://www.jb51.net/article/95991.htm
时间: 2024-10-20 20:50:03