購物車特效-貝塞爾曲線動畫(點選新增按鈕,產生拋物線動畫效果)
阿新 • • 發佈:2019-02-13
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傳值