Android實(shí)現(xiàn)購物車添加商品動(dòng)畫
本文實(shí)例為大家分享了Android實(shí)現(xiàn)購物車添加商品動(dòng)畫的具體代碼,供大家參考,具體內(nèi)容如下
實(shí)現(xiàn)需求:
在商品列表頁面,從列表Item 添加商品的時(shí)候,需要一個(gè)動(dòng)畫,仿佛是是往購物車?yán)锾砑由唐贰?br />
實(shí)現(xiàn)思路:
- 獲取起始點(diǎn)與終點(diǎn)的坐標(biāo),利用PathMeasure 繪制貝塞爾曲線;
- 為點(diǎn)擊的Item 商品View 設(shè)置屬性動(dòng)畫;
- 監(jiān)聽屬性動(dòng)畫的update,改變View 的坐標(biāo);
實(shí)現(xiàn)效果:
實(shí)現(xiàn)中會(huì)用到 PathMeasure 類:
我們主要使用它兩個(gè)方法:
1、獲取長(zhǎng)度:
/** //獲取弧線的總長(zhǎng)度(周長(zhǎng)) * Return the total length of the current contour, or 0 if no path is * associated with this measure object. */ public float getLength() { return native_getLength(native_instance);//系統(tǒng)調(diào)用native 方法; }
2、獲取坐標(biāo):
/** * Pins distance to 0 <= distance <= getLength(), and then computes the * corresponding position and tangent. Returns false if there is no path, * or a zero-length path was specified, in which case position and tangent * are unchanged. * * @param distance The distance along the current contour to sample * @param pos If not null, eturns the sampled position (x==[0], y==[1]) * @param tan If not null, returns the sampled tangent (x==[0], y==[1]) * @return false if there was no path associated with this measure object */ public boolean getPosTan(float distance, float pos[], float tan[]) { if (pos != null && pos.length < 2 || tan != null && tan.length < 2) { throw new ArrayIndexOutOfBoundsException(); } return native_getPosTan(native_instance, distance, pos, tan); }
方法 getPosTan(float distance, float pos[],float tan[]) - path 為 null ,返回 false
distance 為一個(gè) 0 - getLength() 之間的值,根據(jù)這個(gè)值 PathMeasure 會(huì)計(jì)算出當(dāng)前點(diǎn)的坐標(biāo)封裝到 pos 中。上面這句話我們可以這么來理解,不管實(shí)際 Path 多么的復(fù)雜,PathMeasure 都相當(dāng)于做了一個(gè)事情,就是把 Path “拉直”,然后給了我們一個(gè)接口(getLength)告訴我們path的總長(zhǎng)度,然后我們想要知道具體某一點(diǎn)的坐標(biāo),只需要用相對(duì)的distance去取即可,這樣就省去了自己用函數(shù)模擬path,然后計(jì)算獲取點(diǎn)坐標(biāo)的過程。
代碼如下:
public class GoodsListActivity extends AppCompatActivity { private RelativeLayout mRootRl; private RecyclerView mGoodsRecyclerView; private ImageView mCarImageView; private TextView mCountTv; private List<Bitmap> mBitmapList = new ArrayList<>(); private PathMeasure mPathMeasure; private float[] mCurrentPosition = new float[2]; private int mCount = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_goods_list); initView(); initData(); GoodsAdapter goodsAdapter = new GoodsAdapter(mBitmapList); mGoodsRecyclerView.setLayoutManager(new LinearLayoutManager(this)); mGoodsRecyclerView.setAdapter(goodsAdapter); } private void initView(){ mGoodsRecyclerView = (RecyclerView)findViewById(R.id.recyclerView); mCarImageView = (ImageView)findViewById(R.id.imageview_shop_car); mCountTv = (TextView)findViewById(R.id.tv_count); mRootRl = (RelativeLayout)findViewById(R.id.rl_root); } private void initData(){ mBitmapList.add(BitmapFactory.decodeResource(getResources(), R.drawable.car)); mBitmapList.add(BitmapFactory.decodeResource(getResources(), R.drawable.car)); mBitmapList.add(BitmapFactory.decodeResource(getResources(), R.drawable.car)); } class GoodsAdapter extends RecyclerView.Adapter<GoodsViewHolder>{ private List<Bitmap> mData; public GoodsAdapter(List<Bitmap> data) { mData = data; } @Override public GoodsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(GoodsListActivity.this) .inflate(R.layout.rv_goods_item, parent, false); return new GoodsViewHolder(itemView); } @Override public void onBindViewHolder(final GoodsViewHolder holder, int position) { holder.ivGood.setImageBitmap(mData.get(position)); holder.tvBuy.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { addGoodToCar(holder.ivGood); } }); } @Override public int getItemCount() { return mData != null ? mData.size() : 0; } } private void addGoodToCar(ImageView imageView){ final ImageView view = new ImageView(GoodsListActivity.this); view.setImageDrawable(imageView.getDrawable()); RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(100, 100); mRootRl.addView(view, layoutParams); //二、計(jì)算動(dòng)畫開始/結(jié)束點(diǎn)的坐標(biāo)的準(zhǔn)備工作 //得到父布局的起始點(diǎn)坐標(biāo)(用于輔助計(jì)算動(dòng)畫開始/結(jié)束時(shí)的點(diǎn)的坐標(biāo)) int[] parentLoc = new int[2]; mRootRl.getLocationInWindow(parentLoc); //得到商品圖片的坐標(biāo)(用于計(jì)算動(dòng)畫開始的坐標(biāo)) int startLoc[] = new int[2]; imageView.getLocationInWindow(startLoc); //得到購物車圖片的坐標(biāo)(用于計(jì)算動(dòng)畫結(jié)束后的坐標(biāo)) int endLoc[] = new int[2]; mCarImageView.getLocationInWindow(endLoc); float startX = startLoc[0] - parentLoc[0] + imageView.getWidth()/2; float startY = startLoc[1] - parentLoc[1] + imageView.getHeight()/2; //商品掉落后的終點(diǎn)坐標(biāo):購物車起始點(diǎn)-父布局起始點(diǎn)+購物車圖片的1/5 float toX = endLoc[0] - parentLoc[0] + mCarImageView.getWidth() / 5; float toY = endLoc[1] - parentLoc[1]; //開始繪制貝塞爾曲線 Path path = new Path(); path.moveTo(startX, startY); //使用二次薩貝爾曲線:注意第一個(gè)起始坐標(biāo)越大,貝塞爾曲線的橫向距離就會(huì)越大,一般按照下面的式子取即可 path.quadTo((startX + toX) / 2, startY, toX, toY); mPathMeasure = new PathMeasure(path, false); //屬性動(dòng)畫 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(); mPathMeasure.getPosTan(value, mCurrentPosition, null); view.setTranslationX(mCurrentPosition[0]); view.setTranslationY(mCurrentPosition[1]); } }); valueAnimator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { // 購物車的數(shù)量加1 mCount++; mCountTv.setText(String.valueOf(mCount)); // 把移動(dòng)的圖片imageview從父布局里移除 mRootRl.removeView(view); //shopImg 開始一個(gè)放大動(dòng)畫 Animation scaleAnim = AnimationUtils.loadAnimation(GoodsListActivity.this, R.anim.shop_car_scale); mCarImageView.startAnimation(scaleAnim); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); valueAnimator.start(); } class GoodsViewHolder extends RecyclerView.ViewHolder{ private ImageView ivGood; private TextView tvBuy; public GoodsViewHolder(View itemView) { super(itemView); ivGood = (ImageView)itemView.findViewById(R.id.iv_goods); tvBuy = (TextView) itemView.findViewById(R.id.tv_buy); } } }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android使用動(dòng)畫動(dòng)態(tài)添加商品進(jìn)購物車
- Android仿天貓商品拋物線加入購物車動(dòng)畫
- Android實(shí)現(xiàn)添加商品到購物車動(dòng)畫效果
- Android把商品添加到購物車的動(dòng)畫效果(貝塞爾曲線)
- Android實(shí)現(xiàn)仿淘寶購物車增加和減少商品數(shù)量功能demo示例
- Android實(shí)現(xiàn)購物車功能
- Android實(shí)現(xiàn)的仿淘寶購物車demo示例
- Android中實(shí)現(xiàn)淘寶購物車RecyclerView或LIstView的嵌套選擇的邏輯
- Android實(shí)現(xiàn)購物車添加商品特效
相關(guān)文章
Android中使用Canvas繪制南丁格爾玫瑰圖(Nightingale rose diagram)
這篇文章主要介紹了Android中使用Canvas繪制南丁格爾玫瑰圖(Nightingale rose diagram),本文直接給出實(shí)現(xiàn)代碼和運(yùn)行效果圖,需要的朋友可以參考下2015-03-03Android Studio3.6新特性之視圖綁定ViewBinding使用指南
這篇文章主要介紹了Android Studio3.6新特性之視圖綁定ViewBinding使用指南,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-03-03Android內(nèi)置SQLite的使用詳細(xì)介紹
這篇文章主要介紹了Android內(nèi)置SQLite的使用詳細(xì)介紹,文章通過文章展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09Android開發(fā)筆記之Android中數(shù)據(jù)的存儲(chǔ)方式(二)
我們?cè)趯?shí)際開發(fā)中,有的時(shí)候需要儲(chǔ)存或者備份比較復(fù)雜的數(shù)據(jù)。這些數(shù)據(jù)的特點(diǎn)是,內(nèi)容多、結(jié)構(gòu)大,比如短信備份等,通過本文給大家介紹Android開發(fā)筆記之Android中數(shù)據(jù)的存儲(chǔ)方式(二),對(duì)android數(shù)據(jù)存儲(chǔ)方式相關(guān)知識(shí)感興趣的朋友一起學(xué)習(xí)吧2016-01-01Android RecyclerView區(qū)分視圖類型的Divider的實(shí)現(xiàn)
本篇文章主要介紹了Android RecyclerView區(qū)分視圖類型的Divider的實(shí)現(xiàn),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-04-04Android UI手機(jī)信息頁面設(shè)計(jì)
這篇文章主要為大家詳細(xì)介紹了Android UI手機(jī)信息頁面的設(shè)計(jì)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03Android開發(fā)實(shí)現(xiàn)的圓角按鈕、文字陰影按鈕效果示例
這篇文章主要介紹了Android開發(fā)實(shí)現(xiàn)的圓角按鈕、文字陰影按鈕效果,涉及Android界面布局與屬性設(shè)置相關(guān)操作技巧,需要的朋友可以參考下2019-04-04實(shí)例解析Android中使用Pull解析器解析XML的方法
這篇文章主要介紹了Android中使用Pull解析器解析XML的方法,與DOM和SAX解析方式相比人們更推崇Pull,需要的朋友可以參考下2016-04-04