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

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

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

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 免费高清在线视频色yeye | 日韩一区在线播放 | 国产一卡2卡3卡四卡高清 | 成人高清视频在线观看 | 香蕉精品视频 | 免费人成在线观看 | 完整秽淫刺激长篇小说 | 日本无遮挡吸乳视频看看 | 风间由美一区二区播放合集 | 国产大胆歌舞团网站 | 精品国产剧情在线观看 | 国产一卡二卡3卡4卡四卡在线视频 | 高清在线免费 | 香艳69xxxxx有声小说 | 免费看男女做好爽好硬视频 | 国产在线观看99 | 小兰被扒开内裤露出p | 日韩在线成人 | 亚洲欧美日韩天堂 | 奇米白色 | 乳环贵妇堕落开发调教番号 | 91动漫在线观看 | 午夜片神马影院福利 | 91香蕉在线 | 国产午夜免费视频 | 暖暖 免费 高清 日本 在线 | 亚洲精品成人在线 | 亚洲一卡2卡三卡4卡5卡组 | 99这里只有精品66视频 | 800精品国产导航 | 美女视频一区二区三区在线 | 白丝憋尿 | 青青青视频免费观看 | 国产剧情麻豆刘玥视频 | 日韩永久在线观看免费视频 | 87影院在线观看视频在线观看 | 国产99久久精品 | 好吊色视频988gao在线观看 | 久久视频这里只精品99热在线观看 | 日本 视频 在线 | aaa毛片在线 |