一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Android - Android實現添加商品到購物車動畫效果

Android實現添加商品到購物車動畫效果

2022-03-07 14:57lytx1121 Android

這篇文章主要為大家詳細介紹了Android實現添加商品到購物車的動畫效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下

本文實例為大家分享了Android添加商品到購物車的具體代碼,供大家參考,具體內容如下

實現需求

在商品列表頁面中,從列表item添加商品時,實現一個動畫,給人感覺像是在添加商品到購物車。

思路

1、獲取各個動畫執行對象的起點和終點的坐標,利用PathMeasure繪制繪制貝塞爾曲線;
2、為商品圖片設置屬性動畫;
3、為動畫設置addUpdateListene監聽器,更新view的坐標。

效果圖:

Android實現添加商品到購物車動畫效果

MainActivity.java

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
package com.zlw.yzm.demo;
 
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.LinearInterpolator;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
 
import com.zlw.yzm.demo.view.AmountView;
 
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
public class MainActivity extends AppCompatActivity {
 
  private RecyclerView rvGoodsList;
  private ImageView ivGotoGouWuChe;
  private RelativeLayout llContainer;
 
  private List<Product> productList;
  private PathMeasure mPathMeasure;
  /**
   * 存儲商品列表中對應position的商品數量
   */
  private Map<Integer, Integer> amountMap = new HashMap<>();
  /**
   * 貝塞爾曲線中間過程的點的坐標
   */
  private float[] mCurrentPosition = new float[2];
 
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    initView();
    initData();
    bindData();
  }
 
  private void initView() {
    rvGoodsList = findViewById(R.id.main_rv_goods_list);
    ivGotoGouWuChe = findViewById(R.id.main_iv_goto_gwche);
    llContainer = findViewById(R.id.rlContainer);
  }
 
  private void initData() {
 
    productList = new ArrayList<>();
    Product product = null;
    for (int i = 0; i < 30; i++) {
      product = new Product();
      product.productId = 10000L + i;
      product.productName = "Product-" + i;
      product.productDesc = "productDesc-" + i;
      productList.add(product);
    }
  }
  private void bindData() {
    rvGoodsList.setLayoutManager(new LinearLayoutManager(this));
    MyAdapter myAdapter = new MyAdapter();
    rvGoodsList.setAdapter(myAdapter);
  }
  class MyAdapter extends RecyclerView.Adapter {
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
      View itemView = View.inflate(MainActivity.this, R.layout.rv_item, null);
      return new ViewHolder(itemView);
    }
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
      final ViewHolder viewHolder = (ViewHolder) holder;
      Product product = productList.get(position);
      viewHolder.tvProductName.setText(product.productName);
      viewHolder.tvProductDesc.setText(product.productDesc);
      viewHolder.amountView.setGoods_storage(5);
      viewHolder.amountView.setPosition(position);
      final Integer amount = amountMap.get(position);
      if (amount == null) {
        viewHolder.amountView.setAmount(0);
        viewHolder.amountView.playCloseAnim(0);
      } else {
        viewHolder.amountView.setAmount(amount.intValue());
        if (amount.intValue() == 0) {
          viewHolder.amountView.playCloseAnim(0);
        } else if (amount.intValue() >= 1) {
          viewHolder.amountView.playOpenAnim(0);
        }
      }
 
      Log.e("tag1", "amount===" + amount);
      viewHolder.amountView.setOnAmountChangedListener(new AmountView.OnAmountChangedListener() {
        @Override
        public void onAmountChanged(View view, int amount, int position, boolean increase) {
 
          amountMap.put(position, amount);
 
          if (increase) {
 
            add2Cart(viewHolder.ivProductIcon);
          }
        }
      });
    }
 
    @Override
    public int getItemCount() {
      return productList != null ? productList.size() : 0;
    }
 
    class ViewHolder extends RecyclerView.ViewHolder {
 
      ImageView ivProductIcon;
      TextView tvProductName;
      TextView tvProductDesc;
      AmountView amountView;
 
      public ViewHolder(View itemView) {
        super(itemView);
        ivProductIcon = itemView.findViewById(R.id.rv_item_iv_product_Icon);
        tvProductName = itemView.findViewById(R.id.rv_item_tv_product_name);
        tvProductDesc = itemView.findViewById(R.id.rv_item_tv_product_desc);
        amountView = itemView.findViewById(R.id.rv_item_amountview);
      }
    }
  }
 
  /**
   * 添加到購物車
   *
   * @param ivProductIcon
   */
  private void add2Cart(ImageView ivProductIcon) {
 
    // 一、創建執行動畫的主題---ImageView(該圖片就是執行動畫的圖片,從開始位置出發,經過一個拋物線(貝塞爾曲線)。)
    final ImageView imageView = new ImageView(MainActivity.this);
    imageView.setImageDrawable(ivProductIcon.getDrawable());
    // 將執行動畫的圖片添加到開始位置。
    RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
    llContainer.addView(imageView, params);
 
 
    // 二、計算動畫開始/結束點的坐標的準備工作
    // 得到父布局的起始點坐標(用于輔助計算動畫開始/結束時的點的坐標)
    int[] parentLocation = new int[2];
    llContainer.getLocationInWindow(parentLocation);
    // 得到商品圖片的坐標(用于計算動畫開始的坐標)
    int[] startLoc = new int[2];
    ivProductIcon.getLocationInWindow(startLoc);
    // 得到購物車圖片的坐標(用于計算動畫結束后的坐標)
    int[] endLoc = new int[2];
    ivGotoGouWuChe.getLocationInWindow(endLoc);
 
    // 三、計算動畫開始結束的坐標
    // 開始掉落的商品的起始點:商品起始點-父布局起始點+該商品圖片的一半
    float startX = startLoc[0] - parentLocation[0] + ivProductIcon.getWidth() / 2;
    float startY = startLoc[1] - parentLocation[1] + ivProductIcon.getHeight() / 2;
    //商品掉落后的終點坐標:購物車起始點-父布局起始點+購物車圖片的1/5
    float toX = endLoc[0] - parentLocation[0] + ivGotoGouWuChe.getWidth() / 5;
    float toY = endLoc[1] - parentLocation[1];
 
    // 四、計算中間動畫的插值坐標(貝塞爾曲線)(其實就是用貝塞爾曲線來完成起終點的過程)
    //開始繪制貝塞爾曲線
    Path path = new Path();
    //移動到起始點(貝塞爾曲線的起點)
    path.moveTo(startX, startY);
    //使用二次薩貝爾曲線:注意第一個起始坐標越大,貝塞爾曲線的橫向距離就會越大,一般按照下面的式子取即可
    path.quadTo((startX + toX) / 2, startY, toX, toY);
    //mPathMeasure用來計算貝塞爾曲線的曲線長度和貝塞爾曲線中間插值的坐標,
    // 如果是true,path會形成一個閉環
    mPathMeasure = new PathMeasure(path, false);
 
    //★★★屬性動畫實現(從0到貝塞爾曲線的長度之間進行插值計算,獲取中間過程的距離值)
    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) {
        // 當插值計算進行時,獲取中間的每個值,
        // 這里這個值是中間過程中的曲線長度(下面根據這個值來得出中間點的坐標值)
        float value = (Float) animation.getAnimatedValue();
        // ★★★★★獲取當前點坐標封裝到mCurrentPosition
        // boolean getPosTan(float distance, float[] pos, float[] tan) :
        // 傳入一個距離distance(0<=distance<=getLength()),然后會計算當前距
        // 離的坐標點和切線,pos會自動填充上坐標,這個方法很重要。
        mPathMeasure.getPosTan(value, mCurrentPosition, null);//mCurrentPosition此時就是中間距離點的坐標值
        // 移動的商品圖片(動畫圖片)的坐標設置為該中間點的坐標
        imageView.setTranslationX(mCurrentPosition[0]);
        imageView.setTranslationY(mCurrentPosition[1]);
      }
    });
    //  五、 開始執行動畫
    valueAnimator.start();
    //  六、動畫結束后的處理
    valueAnimator.addListener(new Animator.AnimatorListener() {
      @Override
      public void onAnimationStart(Animator animation) {
      }
      //當動畫結束后:
      @Override
      public void onAnimationEnd(Animator animation) {
        // 購物車的數量加1
        // 把移動的圖片imageview從父布局里移除
        llContainer.removeView(imageView);
      }
      @Override
      public void onAnimationCancel(Animator animation) {
      }
      @Override
      public void onAnimationRepeat(Animator animation) {
      }
    });
  }
}

activity_main.xml

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:id="@+id/rlContainer"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  tools:context="com.zlw.yzm.demo.MainActivity">
 
  <ImageView
    android:id="@+id/main_iv_goto_gwche"
    android:layout_width="30dp"
    android:layout_height="30dp"
    android:layout_alignParentBottom="true"
    android:layout_alignParentRight="true"
    android:src="@drawable/gouwuche" />
 
  <android.support.v7.widget.RecyclerView
    android:id="@+id/main_rv_goods_list"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_above="@id/main_iv_goto_gwche"></android.support.v7.widget.RecyclerView>
</RelativeLayout>

// 商品列表中item的布局
rl_item.xml

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="wrap_content">
 
  <ImageView
    android:id="@+id/rv_item_iv_product_Icon"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@mipmap/ic_launcher" />
 
 
  <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignBottom="@id/rv_item_iv_product_Icon"
    android:layout_alignTop="@id/rv_item_iv_product_Icon"
    android:layout_marginLeft="20dp"
    android:layout_toRightOf="@id/rv_item_iv_product_Icon"
    android:gravity="center_vertical"
    android:orientation="vertical">
 
    <TextView
      android:id="@+id/rv_item_tv_product_name"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="@string/product_name" />
 
    <TextView
      android:id="@+id/rv_item_tv_product_desc"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginTop="5dp"
      android:text="@string/product_desc" />
  </LinearLayout>
 
  <com.zlw.yzm.demo.view.AmountView
    android:id="@+id/rv_item_amountview"
    android:layout_alignParentRight="true"
    android:layout_centerVertical="true"
    android:layout_marginRight="20dp"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">
 
  </com.zlw.yzm.demo.view.AmountView>
</RelativeLayout>

// 自定義更新商品數量view
AmountView.java

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
package com.zlw.yzm.demo.view;
 
import android.animation.ValueAnimator;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.RelativeLayout;
import android.widget.TextView;
 
import com.zlw.yzm.demo.R;
 
/**
 * Created by 13198 on 2018/6/28.
 * 對商品的添加和刪除進行封裝
 */
 
public class AmountView extends RelativeLayout implements View.OnClickListener {
 
  private Context context;
 
  private TextView tvDecrease;
  private TextView tvAmount;
  private TextView tvIncrease;
  private RelativeLayout rlContainer;
 
  private float leftStartX;
  private float centerStartX;
  private float finalX;
 
  // 商品位置
  private int position = -1;
  // 商品庫存
  private int goods_storage = 10;
 
  // 購買數量
  private int amount = 0;
  private int rlContainerMeasuredWidth;
 
  public AmountView(Context context) {
    this(context, null);
  }
 
  public AmountView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
  }
 
  public AmountView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    this.context = context;
    initView();
    initListener();
  }
 
  private void initView() {
 
    LayoutInflater.from(context).inflate(R.layout.rl_add2cart, this, true);
    tvDecrease = findViewById(R.id.tv_decrease);
    tvAmount = findViewById(R.id.tv_amount);
    tvIncrease = findViewById(R.id.tv_increase);
    rlContainer = findViewById(R.id.rlContainer);
  }
 
  private void initListener() {
    tvDecrease.setOnClickListener(this);
    tvIncrease.setOnClickListener(this);
  }
 
  public void setVisiable(boolean b) {
    tvDecrease.setVisibility(!b ? View.GONE : View.VISIBLE);
    tvAmount.setVisibility(!b ? View.GONE : View.VISIBLE);
  }
 
  @Override
  public void onClick(View v) {
 
    int id = v.getId();
    if (id == R.id.tv_increase) {
      // 添加商品
      if (amount < goods_storage) {
        amount++;
        setAmount(amount);
        if (getAmount() == 1) {
          playOpenAnim(500);
        }
        if (onAmountChangedListener != null) {
          onAmountChangedListener.onAmountChanged(this, amount, position, true);
        }
      }
    } else {
      //刪除商品
      if (amount > 0) {
        amount--;
        setAmount(amount);
        playCloseAnim(500);
        if (onAmountChangedListener != null) {
 
          onAmountChangedListener.onAmountChanged(this, amount, position, false);
        }
      }
    }
  }
 
  public void setPosition(int position) {
 
    this.position = position;
  }
 
  public void setAmount(int amount) {
    this.amount = amount;
    tvAmount.setText(String.valueOf(amount));
  }
 
  public int getAmount() {
 
    return amount;
  }
 
  public void setGoods_storage(int goods_storage) {
    this.goods_storage = goods_storage;
  }
 
 
  public void playOpenAnim(int duration) {
 
    rlContainer.measure(0, 0);
    rlContainerMeasuredWidth = tvDecrease.getMeasuredWidth();
    startAnim(tvDecrease, 0, -rlContainerMeasuredWidth / 3, duration);
    startAnim(tvAmount, 0, -rlContainerMeasuredWidth / 3, duration);
  }
 
  public void playCloseAnim(int duration) {
    rlContainer.measure(0, 0);
    rlContainerMeasuredWidth = rlContainer.getMeasuredWidth();
    if (getAmount() == 0) {
 
      startAnim(tvDecrease, 0, rlContainerMeasuredWidth / 3, duration);
      startAnim(tvAmount, 0, rlContainerMeasuredWidth / 3, duration);
    }
  }
 
  /**
   * 添加、移除購物車中商品的動畫
   *
   * @param view
   * @param startX
   * @param endX
   * @param duration
   */
  private void startAnim(final View view, final float startX, final float endX, int duration) {
    ValueAnimator animator = ValueAnimator.ofFloat(0, endX - startX);
    animator.setDuration(duration);
    animator.setInterpolator(new LinearInterpolator());
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
      @Override
      public void onAnimationUpdate(ValueAnimator animation) {
 
        float currentX = (float) animation.getAnimatedValue();
        view.setTranslationX(currentX);
 
        float alpha = 0;
        float hudu = 0;
        float lenth = Math.abs(endX - startX);
        if (endX - startX > 0) {
          // 向右滑動=====>1--0
          alpha = (lenth - currentX) / lenth;
          hudu = currentX * 360 / lenth;
        } else {
          // 向左滑動====>0-1
          alpha = Math.abs(currentX) / lenth;
          hudu = 360 - (lenth - Math.abs(currentX)) * 360 / lenth;
        }
 
        view.setAlpha(alpha);
        view.setRotation(hudu);
        Log.e("tag", "view=====" + view.getId() + "=======currentX==========" + currentX);
      }
    });
    animator.start();
  }
 
  /**
   * 定義一個接口,監聽數量變化
   */
  public interface OnAmountChangedListener {
    void onAmountChanged(View view, int amount, int position, boolean increase);
  }
 
  private OnAmountChangedListener onAmountChangedListener;
 
  public void setOnAmountChangedListener(OnAmountChangedListener onAmountChangedListener) {
    this.onAmountChangedListener = onAmountChangedListener;
  }
 
}

// 自定義更新商品數量view的布局
rl_amountview.xml

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/rlContainer"
  android:layout_width="60dp"
  android:layout_height="wrap_content">
 
  <TextView
    android:id="@+id/tv_decrease"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerVertical="true"
    android:padding="10dp"
    android:text="-"
    android:textColor="@android:color/black"
    android:textSize="22sp"
    android:textStyle="bold" />
 
  <TextView
    android:id="@+id/tv_amount"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"
    android:gravity="center"
    android:text="0" />
 
  <TextView
    android:id="@+id/tv_increase"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentRight="true"
    android:layout_centerVertical="true"
    android:padding="10dp"
    android:text="+"
    android:textColor="@android:color/black"
    android:textSize="22sp"
    android:textStyle="bold" />
</RelativeLayout>

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:https://blog.csdn.net/lytx1121/article/details/80855227

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 人人揉人人爽五月天视频 | 艹b小说| 欧美综合国产精品日韩一 | 日韩在线视频在线 | 四虎国产一区 | 亚洲男人网| 久久学生精品国产自在拍 | a毛片在线免费观看 | 污网站免费观看在线高清 | 美女隐私部位视频网站 | 欧美精品久久久亚洲 | 18未年禁止免费观看 | 九九精品视频一区二区三区 | 无人在线观看免费高清视频播放 | 美女流白浆| xxx久久 | 2019亚洲男人天堂 | 精品免费久久久久久成人影院 | 98色花堂永久地址国产精品 | 男人肌肌捅女人肌肌 | 深夜激情网站 | 午夜福利理论片在线播放 | 免费看成年视频网页 | 99网站在线观看 | 日韩精品一区二三区中文 | 国偷盗摄自产福利一区在线 | 色多多在线观看视频 | 久久久久嫩草影院精品 | 日韩成人一级 | 国产激情久久久久影院小草 | 国产精品麻豆久久99 | 日本免费观看的视频在线 | 精品AV综合导航 | 99操视频| 韩国一区二区三区 | 91短视频版高清在线观看免费 | 四虎永久成人免费 | 国产成人在线综合 | 97久久精品午夜一区二区 | 吃胸膜奶视频456 | 美女扒下内裤让男人桶的图片 |