Android利用二階貝塞爾曲線實(shí)現(xiàn)添加購(gòu)物車動(dòng)畫詳解
一、引入
- 其實(shí)之前一直以為像餓了么或者是美團(tuán)外賣那種把商品添加到購(gòu)物車的動(dòng)畫會(huì)很難做,但是實(shí)際做起來(lái)好像并沒有想象中的那么難哈哈。
- 布局主要使用CoordinatorLayout+AppBarLayout+CollapsingToolbarLayout+TabLayout+ViewPager
- 動(dòng)畫主要使用二階貝塞爾曲線與屬性動(dòng)畫
- 消息傳遞使用EventBus普通事件
二、大致思路
1、如圖所示主要有三個(gè)點(diǎn),起點(diǎn)、終點(diǎn)、以及貝塞爾曲線的控制點(diǎn)
2、起點(diǎn)即點(diǎn)擊的View的位置,一般來(lái)說(shuō)用如下方式即可取得。startPosition[0]為x軸開始坐標(biāo),startPosition[1]為Y軸終點(diǎn)坐標(biāo),兩點(diǎn)可以看作對(duì)角線上面的兩個(gè)端點(diǎn)(左上角x坐標(biāo),右下角y坐標(biāo))
//貝塞爾起始數(shù)據(jù)點(diǎn) int[] startPosition = new int[2]; view.getLocationOnScreen(startPosition);
3、終點(diǎn)即購(gòu)物車籃子的位置,與起點(diǎn)類似
mShoppingCart.getLocationInWindow(endPosition);
4、控制點(diǎn),我選的控制點(diǎn)為上圖的C點(diǎn),即A點(diǎn)的y坐標(biāo),B點(diǎn)的X坐標(biāo)
controlPosition[0] = endPosition[0]; controlPosition[1] = startPosition[1];
5、需要注意的地方,我不清楚是不是因?yàn)槲业牟季值膯栴},獲取到的點(diǎn)擊的A點(diǎn)總是會(huì)有一個(gè)偏移,后來(lái)經(jīng)同事提醒,減去了TabLayout的坐標(biāo)的y軸坐標(biāo)即位置才可以。
// 起點(diǎn) int[] startPosition; // 終點(diǎn) int[] endPosition = new int[2]; // 貝塞爾控制點(diǎn) int[] controlPosition = new int[2]; // tablayout位置 int[] tablayoutPosition = new int[2]; startPosition = data.getStartPosition(); mShoppingCart.getLocationInWindow(endPosition); mTabLayout.getLocationInWindow(tablayoutPosition); // 處理起點(diǎn)y坐標(biāo)偏移的問題 startPosition[1] = startPosition[1] - tablayoutPosition[1] - mTabLayout.getHeight(); // 終點(diǎn)進(jìn)行一下居中處理 endPosition[0] = endPosition[0] + (mShoppingCart.getWidth() / 2); controlPosition[0] = endPosition[0]; controlPosition[1] = startPosition[1];
6、通過(guò)Path的quadTo方法繪制貝塞爾曲線,使用PathMeasure獲取點(diǎn)的坐標(biāo)(借助ValueAnimator.ofFloat()配合getPosTan()來(lái)獲取坐標(biāo))
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是一個(gè)生成器 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、下面是用屬性動(dòng)畫給購(gòu)物車籃子做了一個(gè)放大縮小的動(dòng)畫效果
// 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) { // 起點(diǎn) int[] startPosition; // 終點(diǎn) int[] endPosition = new int[2]; // 貝塞爾控制點(diǎn) int[] controlPosition = new int[2]; // 當(dāng)前位置 float[] currentPosition = new float[2]; // tablayout位置 int[] tablayoutPosition = new int[2]; startPosition = data.getStartPosition(); mShoppingCart.getLocationInWindow(endPosition); mTabLayout.getLocationInWindow(tablayoutPosition); // 處理起點(diǎn)y坐標(biāo)偏移的問題 startPosition[1] = startPosition[1] - tablayoutPosition[1] - mTabLayout.getHeight(); // 終點(diǎn)進(jìn)行一下居中處理 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是一個(gè)生成器 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) { } //當(dāng)動(dòng)畫結(jié)束后: @Override public void onAnimationEnd(Animator animation) { goodsChange(data); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); }
四、大致寫下布局(同時(shí)也算留做備份)
<?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 ... ...> 最下面的購(gòu)物車一欄 </LinearLayout> </LinearLayout>
五、推薦資源
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
- Android把商品添加到購(gòu)物車的動(dòng)畫效果(貝塞爾曲線)
- Android實(shí)現(xiàn)代碼畫虛線邊框背景效果
- Android編程實(shí)現(xiàn)ImageView圖片拋物線動(dòng)畫效果的方法
- Android實(shí)現(xiàn)在map上畫出路線的方法
- Android App中使用SurfaceView制作多線程動(dòng)畫的實(shí)例講解
- Android仿天貓商品拋物線加入購(gòu)物車動(dòng)畫
- Android補(bǔ)間動(dòng)畫基本使用(位移、縮放、旋轉(zhuǎn)、透明)
- Android動(dòng)畫之漸變動(dòng)畫(Tween Animation)詳解 (漸變、縮放、位移、旋轉(zhuǎn))
- Android開發(fā)之圖形圖像與動(dòng)畫(二)Animation實(shí)現(xiàn)圖像的漸變/縮放/位移/旋轉(zhuǎn)
- Android利用Canvas標(biāo)點(diǎn)畫線并加入位移動(dòng)畫(1)
相關(guān)文章
Android高級(jí)圖片滾動(dòng)控件實(shí)現(xiàn)3D版圖片輪播器
這篇文章主要介紹了Android高級(jí)圖片滾動(dòng)控件實(shí)現(xiàn)3D版圖片輪播器,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-05-05android利用ContentResolver訪問者獲取手機(jī)聯(lián)系人信息
這篇文章主要介紹了android利用ContentResolver訪問者獲取手機(jī)聯(lián)系人信息,非常具有實(shí)用價(jià)值,需要的朋友可以參考下。2017-02-02Android Flutter基于WebSocket實(shí)現(xiàn)即時(shí)通訊功能
WebSocket是一種在單個(gè)TCP連接上進(jìn)行全雙工通信的協(xié)議。本文將利用Flutter WebSocket實(shí)現(xiàn)即時(shí)通訊功能,文中示例代碼講解詳細(xì),感興趣的可以了解一下2022-03-03Android編程開發(fā)之seekBar采用handler消息處理操作的方法
這篇文章主要介紹了Android編程開發(fā)之seekBar采用handler消息處理操作的方法,結(jié)合實(shí)例分析了Android實(shí)現(xiàn)進(jìn)度條功能的相關(guān)技巧,需要的朋友可以參考下2015-12-12Android 仿京東商城底部布局的選擇效果(Selector 選擇器的實(shí)現(xiàn))
這篇文章主要介紹了Android 仿京東商城底部布局的選擇效果(Selector 選擇器的實(shí)現(xiàn)),需要的朋友可以參考下2017-04-04Android Studio通過(guò)Artifactory搭建本地倉(cāng)庫(kù)優(yōu)化編譯速度的方法
這篇文章主要介紹了Android Studio通過(guò)Artifactory搭建本地倉(cāng)庫(kù)優(yōu)化編譯速度的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03