網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
Android貝塞爾曲線實(shí)現(xiàn)加入購(gòu)物車拋物線動(dòng)畫_Android
作者:簡(jiǎn)簡(jiǎn)單單_zz ? 更新時(shí)間: 2022-08-21 編程語(yǔ)言本文實(shí)例為大家分享了Android貝塞爾曲線實(shí)現(xiàn)加入購(gòu)物車拋物線動(dòng)畫的具體代碼,供大家參考,具體內(nèi)容如下
先上圖看效果
步驟:
a.確定動(dòng)畫的起終點(diǎn)
b.在起終點(diǎn)之間使用二次貝塞爾曲線填充起終點(diǎn)之間的點(diǎn)的軌跡
c.設(shè)置屬性動(dòng)畫,ValueAnimator插值器,獲取中間點(diǎn)的坐標(biāo)
d.將執(zhí)行動(dòng)畫的控件的x、y坐標(biāo)設(shè)為上面得到的中間點(diǎn)坐標(biāo)
e.開啟屬性動(dòng)畫
f.當(dāng)動(dòng)畫結(jié)束時(shí)的操作
獲取控件在屏幕中的絕對(duì)坐標(biāo):
int[] parentLocation = new int[2];
mRLayout.getLocationInWindow(parentLocation);
計(jì)算開始坐標(biāo)和結(jié)束坐標(biāo):
//開始掉落的商品的起始點(diǎn):商品起始點(diǎn)-父布局起始點(diǎn)+該商品圖片的一半
float startX = startLoc[0] - parentLocation[0] + iv.getWidth() / 2;
float startY = startLoc[1] - parentLocation[1] + iv.getHeight() / 2;
//商品掉落后的終點(diǎn)坐標(biāo):購(gòu)物車起始點(diǎn)-父布局起始點(diǎn)+購(gòu)物車圖片的1/5
float toX = endLoc[0] - parentLocation[0] + mCart.getWidth() / 5;
float toY = endLoc[1] - parentLocation[1];
貝塞爾曲線以及屬性動(dòng)畫:
Path path = new Path();
? ? ? ? //移動(dòng)到起始點(diǎn)(貝塞爾曲線的起點(diǎn))
? ? ? ? path.moveTo(startX, startY);
? ? ? ? //使用二次薩貝爾曲線:注意第一個(gè)起始坐標(biāo)越大,貝塞爾曲線的橫向距離就會(huì)越大,一般按照下面的式子取即可
? ? ? ? path.quadTo((startX + toX) / 2, startY, toX, toY);
? ? ? ? //mPathMeasure用來計(jì)算貝塞爾曲線的曲線長(zhǎng)度和貝塞爾曲線中間插值的坐標(biāo),
? ? ? ? // 如果是true,path會(huì)形成一個(gè)閉環(huán)
? ? ? ? mPathMeasure = new PathMeasure(path, false);
? ? ? ? //屬性動(dòng)畫實(shí)現(xiàn)(從0到貝塞爾曲線的長(zhǎng)度之間進(jìn)行插值計(jì)算,獲取中間過程的距離值)
? ? ? ? ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, mPathMeasure.getLength());
? ? ? ? valueAnimator.setDuration(1000);
? ? ? ? // 勻速線性插值器
? ? ? ? valueAnimator.setInterpolator(new LinearInterpolator());
? ? ? ? valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onAnimationUpdate(ValueAnimator animation) {
? ? ? ? ? ? ? ? // 當(dāng)插值計(jì)算進(jìn)行時(shí),獲取中間的每個(gè)值,
? ? ? ? ? ? ? ? // 這里這個(gè)值是中間過程中的曲線長(zhǎng)度(下面根據(jù)這個(gè)值來得出中間點(diǎn)的坐標(biāo)值)
? ? ? ? ? ? ? ? float value = (Float) animation.getAnimatedValue();
? ? ? ? ? ? ? ? // 獲取當(dāng)前點(diǎn)坐標(biāo)封裝到mCurrentPosition
? ? ? ? ? ? ? ? // boolean getPosTan(float distance, float[] pos, float[] tan) :
? ? ? ? ? ? ? ? // 傳入一個(gè)距離distance(0<=distance<=getLength()),然后會(huì)計(jì)算當(dāng)前距
? ? ? ? ? ? ? ? // 離的坐標(biāo)點(diǎn)和切線,pos會(huì)自動(dòng)填充上坐標(biāo),這個(gè)方法很重要。
? ? ? ? ? ? ? ? mPathMeasure.getPosTan(value, mCurrentPosition, null);//mCurrentPosition此時(shí)就是中間距離點(diǎn)的坐標(biāo)值
? ? ? ? ? ? ? ? // 移動(dòng)的商品圖片(動(dòng)畫圖片)的坐標(biāo)設(shè)置為該中間點(diǎn)的坐標(biāo)
? ? ? ? ? ? ? ? goods.setTranslationX(mCurrentPosition[0]);
? ? ? ? ? ? ? ? goods.setTranslationY(mCurrentPosition[1]);
? ? ? ? ? ? }
? ? ? ? });
// ? ? ?五、 開始執(zhí)行動(dòng)畫
? ? ? ? valueAnimator.start();
// ? ? ?六、動(dòng)畫結(jié)束后的處理
? ? ? ? valueAnimator.addListener(new Animator.AnimatorListener() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onAnimationStart(Animator animation) {
? ? ? ? ? ? }
? ? ? ? ? ? //當(dāng)動(dòng)畫結(jié)束后:
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onAnimationEnd(Animator animation) {
? ? ? ? ? ? ? ? // 購(gòu)物車的數(shù)量加1
? ? ? ? ? ? ? ? i++;
? ? ? ? ? ? ? ? mCount.setText(String.valueOf(i));
? ? ? ? ? ? ? ? // 把移動(dòng)的圖片imageView從父布局里移除
? ? ? ? ? ? ? ? mRLayout.removeView(goods);
? ? ? ? ? ? }
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onAnimationCancel(Animator animation) {
? ? ? ? ? ? }
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onAnimationRepeat(Animator animation) {
? ? ? ? ? ? }
? ? ? ? });
xml里的寫法:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout ? ? xmlns:android="http://schemas.android.com/apk/res/android" ? ? android:layout_width="match_parent" ? ? android:layout_height="match_parent" ? ? android:orientation="vertical" ? ? > ? ? <RelativeLayout ? ? ? ? ? ? android:id="@+id/rl" ? ? ? ? ? ? android:layout_width="match_parent" ? ? ? ? ? ? android:layout_height="wrap_content"> ? ? ? ? ? ? <Button ? ? ? ? ? ? ? ? android:id="@+id/add" ? ? ? ? ? ? ? ? android:layout_width="wrap_content" ? ? ? ? ? ? ? ? android:layout_height="wrap_content" ? ? ? ? ? ? ? ? android:text="加入購(gòu)物車"/> ? ? ? ? ? ? <ImageView ? ? ? ? ? ? ? ? android:layout_toRightOf="@id/add" ? ? ? ? ? ? ? ? android:id="@+id/goods" ? ? ? ? ? ? ? ? android:src="@mipmap/ic_launcher" ? ? ? ? ? ? ? ? android:layout_width="50dp" ? ? ? ? ? ? ? ? android:layout_height="50dp" ? ? ? ? ? ? ? ? /> ? ? ? ? ? ? <TextView ? ? ? ? ? ? ? ? android:id="@+id/count" ? ? ? ? ? ? ? ? android:layout_marginLeft="300dp" ? ? ? ? ? ? ? ? android:layout_marginTop="70dp" ? ? ? ? ? ? ? ? android:layout_width="wrap_content" ? ? ? ? ? ? ? ? android:layout_height="wrap_content" ? ? ? ? ? ? ? ? android:text="0"/> ? ? ? ? ? ? <ImageView ? ? ? ? ? ? ? ? android:id="@+id/cart" ? ? ? ? ? ? ? ? android:layout_width="60dp" ? ? ? ? ? ? ? ? android:layout_height="60dp" ? ? ? ? ? ? ? ? android:layout_marginLeft="300dp" ? ? ? ? ? ? ? ? android:layout_marginTop="240dp" ? ? ? ? ? ? ? ? android:src="@drawable/ic_shopping_cart" ? ? ? ? ? ? ? ? /> ? ? ? ? </RelativeLayout> </LinearLayout>
使用了Butterknife和自己封裝的BaseActivity,要使用的話需要自行修改代碼。
完整代碼:
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.xp.baseapp.R;
import com.xp.baseapp.base.BaseActivity;
import butterknife.BindView;
import butterknife.OnClick;
public class ShoppingCartAnimationActivity extends BaseActivity {
? ? @BindView(R.id.add)
? ? Button mAdd;
? ? @BindView(R.id.rl)
? ? RelativeLayout mRLayout;
? ? @BindView(R.id.count)
? ? TextView mCount;
? ? @BindView(R.id.cart)
? ? ImageView mCart;
? ? @BindView(R.id.goods)
? ? ImageView mGoods;
? ? private PathMeasure mPathMeasure;
? ? /**
? ? ?* 貝塞爾曲線中間過程的點(diǎn)的坐標(biāo)
? ? ?*/
? ? private float[] mCurrentPosition = new float[2];
? ? /**
? ? ?* 購(gòu)物車中的商品數(shù)量
? ? ?*/
? ? private int i = 0;
? ? @Override
? ? protected int getContentViewId() {
? ? ? ? return R.layout.activity_shopping_cart_animation;
? ? }
? ? @Override
? ? protected void init() {
? ? }
? ? @OnClick(R.id.add)
? ? public void addGood(View v) {
? ? ? ? addCart(mGoods);
? ? }
? ? /**
? ? ?* 把商品添加到購(gòu)物車的動(dòng)畫效果
? ? ?*
? ? ?* @param iv
? ? ?*/
? ? private void addCart(ImageView iv) {
// ? ? ?一、創(chuàng)造出執(zhí)行動(dòng)畫的主題---imageview
? ? ? ? //代碼new一個(gè)imageview,圖片資源是上面的imageview的圖片
? ? ? ? // (這個(gè)圖片就是執(zhí)行動(dòng)畫的圖片,從開始位置出發(fā),經(jīng)過一個(gè)拋物線(貝塞爾曲線),移動(dòng)到購(gòu)物車?yán)?
? ? ? ? final ImageView goods = new ImageView(ShoppingCartAnimationActivity.this);
? ? ? ? goods.setImageDrawable(iv.getDrawable());
? ? ? ? RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(100, 100);
? ? ? ? mRLayout.addView(goods, params);
// ? ? ? ?二、計(jì)算動(dòng)畫開始/結(jié)束點(diǎn)的坐標(biāo)的準(zhǔn)備工作
? ? ? ? //得到父布局的起始點(diǎn)坐標(biāo)(用于輔助計(jì)算動(dòng)畫開始/結(jié)束時(shí)的點(diǎn)的坐標(biāo))
? ? ? ? int[] parentLocation = new int[2];
? ? ? ? mRLayout.getLocationInWindow(parentLocation);
? ? ? ? //得到商品圖片的坐標(biāo)(用于計(jì)算動(dòng)畫開始的坐標(biāo))
? ? ? ? int startLoc[] = new int[2];
? ? ? ? iv.getLocationInWindow(startLoc);
? ? ? ? //得到購(gòu)物車圖片的坐標(biāo)(用于計(jì)算動(dòng)畫結(jié)束后的坐標(biāo))
? ? ? ? int endLoc[] = new int[2];
? ? ? ? mCart.getLocationInWindow(endLoc);
// ? ? ? ?三、正式開始計(jì)算動(dòng)畫開始/結(jié)束的坐標(biāo)
? ? ? ? //開始掉落的商品的起始點(diǎn):商品起始點(diǎn)-父布局起始點(diǎn)+該商品圖片的一半
? ? ? ? float startX = startLoc[0] - parentLocation[0] + iv.getWidth() / 2;
? ? ? ? float startY = startLoc[1] - parentLocation[1] + iv.getHeight() / 2;
? ? ? ? //商品掉落后的終點(diǎn)坐標(biāo):購(gòu)物車起始點(diǎn)-父布局起始點(diǎn)+購(gòu)物車圖片的1/5
? ? ? ? float toX = endLoc[0] - parentLocation[0] + mCart.getWidth() / 5;
? ? ? ? float toY = endLoc[1] - parentLocation[1];
// ? ? ? ?四、計(jì)算中間動(dòng)畫的插值坐標(biāo)(貝塞爾曲線)(其實(shí)就是用貝塞爾曲線來完成起終點(diǎn)的過程)
? ? ? ? //開始繪制貝塞爾曲線
? ? ? ? Path path = new Path();
? ? ? ? //移動(dòng)到起始點(diǎn)(貝塞爾曲線的起點(diǎn))
? ? ? ? path.moveTo(startX, startY);
? ? ? ? //使用二次薩貝爾曲線:注意第一個(gè)起始坐標(biāo)越大,貝塞爾曲線的橫向距離就會(huì)越大,一般按照下面的式子取即可
? ? ? ? path.quadTo((startX + toX) / 2, startY, toX, toY);
? ? ? ? //mPathMeasure用來計(jì)算貝塞爾曲線的曲線長(zhǎng)度和貝塞爾曲線中間插值的坐標(biāo),
? ? ? ? // 如果是true,path會(huì)形成一個(gè)閉環(huán)
? ? ? ? mPathMeasure = new PathMeasure(path, false);
? ? ? ? //屬性動(dòng)畫實(shí)現(xiàn)(從0到貝塞爾曲線的長(zhǎng)度之間進(jìn)行插值計(jì)算,獲取中間過程的距離值)
? ? ? ? ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, mPathMeasure.getLength());
? ? ? ? valueAnimator.setDuration(1000);
? ? ? ? // 勻速線性插值器
? ? ? ? valueAnimator.setInterpolator(new LinearInterpolator());
? ? ? ? valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onAnimationUpdate(ValueAnimator animation) {
? ? ? ? ? ? ? ? // 當(dāng)插值計(jì)算進(jìn)行時(shí),獲取中間的每個(gè)值,
? ? ? ? ? ? ? ? // 這里這個(gè)值是中間過程中的曲線長(zhǎng)度(下面根據(jù)這個(gè)值來得出中間點(diǎn)的坐標(biāo)值)
? ? ? ? ? ? ? ? float value = (Float) animation.getAnimatedValue();
? ? ? ? ? ? ? ? // 獲取當(dāng)前點(diǎn)坐標(biāo)封裝到mCurrentPosition
? ? ? ? ? ? ? ? // boolean getPosTan(float distance, float[] pos, float[] tan) :
? ? ? ? ? ? ? ? // 傳入一個(gè)距離distance(0<=distance<=getLength()),然后會(huì)計(jì)算當(dāng)前距
? ? ? ? ? ? ? ? // 離的坐標(biāo)點(diǎn)和切線,pos會(huì)自動(dòng)填充上坐標(biāo),這個(gè)方法很重要。
? ? ? ? ? ? ? ? mPathMeasure.getPosTan(value, mCurrentPosition, null);//mCurrentPosition此時(shí)就是中間距離點(diǎn)的坐標(biāo)值
? ? ? ? ? ? ? ? // 移動(dòng)的商品圖片(動(dòng)畫圖片)的坐標(biāo)設(shè)置為該中間點(diǎn)的坐標(biāo)
? ? ? ? ? ? ? ? goods.setTranslationX(mCurrentPosition[0]);
? ? ? ? ? ? ? ? goods.setTranslationY(mCurrentPosition[1]);
? ? ? ? ? ? }
? ? ? ? });
// ? ? ?五、 開始執(zhí)行動(dòng)畫
? ? ? ? valueAnimator.start();
// ? ? ?六、動(dòng)畫結(jié)束后的處理
? ? ? ? valueAnimator.addListener(new Animator.AnimatorListener() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onAnimationStart(Animator animation) {
? ? ? ? ? ? }
? ? ? ? ? ? //當(dāng)動(dòng)畫結(jié)束后:
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onAnimationEnd(Animator animation) {
? ? ? ? ? ? ? ? // 購(gòu)物車的數(shù)量加1
? ? ? ? ? ? ? ? i++;
? ? ? ? ? ? ? ? mCount.setText(String.valueOf(i));
? ? ? ? ? ? ? ? // 把移動(dòng)的圖片imageView從父布局里移除
? ? ? ? ? ? ? ? mRLayout.removeView(goods);
? ? ? ? ? ? }
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onAnimationCancel(Animator animation) {
? ? ? ? ? ? }
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onAnimationRepeat(Animator animation) {
? ? ? ? ? ? }
? ? ? ? });
? ? }
}
原文鏈接:https://blog.csdn.net/SilenceOO/article/details/73499149
相關(guān)推薦
- 2022-03-22 C++計(jì)算圓形、矩形和三角形的面積_C 語(yǔ)言
- 2023-06-18 C#最小二乘法擬合曲線成直線的實(shí)例_C#教程
- 2022-09-02 一文詳解Python中的重試機(jī)制_python
- 2023-02-27 python定時(shí)任務(wù)timeloop庫(kù)用法實(shí)例詳解_python
- 2022-05-09 Python的Pandas時(shí)序數(shù)據(jù)詳解_python
- 2023-12-13 Excel統(tǒng)計(jì)某個(gè)關(guān)鍵字出現(xiàn)的次數(shù)
- 2022-06-19 教你用vbs實(shí)現(xiàn)微信自動(dòng)發(fā)送消息功能_vbs
- 2022-05-22 關(guān)于VS2022不能使用<bits/stdc++.h>的解決方案(萬能頭文件)_C 語(yǔ)言
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支