您好,登錄后才能下訂單哦!
本文實例為大家分享了Android添加商品到購物車的具體代碼,供大家參考,具體內容如下
實現需求
在商品列表頁面中,從列表item添加商品時,實現一個動畫,給人感覺像是在添加商品到購物車。
思路
1、獲取各個動畫執行對象的起點和終點的坐標,利用PathMeasure繪制繪制貝塞爾曲線;
2、為商品圖片設置屬性動畫;
3、為動畫設置addUpdateListene監聽器,更新view的坐標。
效果圖:
MainActivity.java
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
<?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
<?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
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
<?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>
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。