購(gòu)物車(chē)動(dòng)畫(huà)

今天整理一下購(gòu)物車(chē)動(dòng)畫(huà),整理一下,方便以后使用.
咱們定外賣(mài)的的時(shí)候,添加商品,可以看一個(gè)加入購(gòu)物車(chē)的動(dòng)畫(huà),下面就實(shí)現(xiàn)這個(gè)效果,也是從網(wǎng)上找了好多方法,下面是我徐在選擇一種,參考一下.
使用了PathMeasure和Path實(shí)現(xiàn),繪制貝塞爾曲線(xiàn)繪制成功的
上代碼吧:
首先是activity的布局文件,里面包括了一個(gè)relativeLayout,和recycleView和購(gòu)物車(chē)的imageView .

            android:id="@+id/ll_contants"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <android.support.v7.widget.RecyclerView
                android:id="@+id/recycle_view"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_weight="100">

            </android.support.v7.widget.RecyclerView>
            <ImageView
                android:layout_alignParentBottom="true"
                android:id="@+id/img_shop_cart"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentRight="true"
                android:layout_marginRight="10dp"
                android:background="@mipmap/touxiang" />
        </RelativeLayout>```
activity的代碼,主要視為recycleView設(shè)置適配器.
```package com.example.wll.ceshitablayout.animation;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.example.wll.ceshitablayout.R;
import com.example.wll.ceshitablayout.adapter.ShopCartAdapter;
import com.example.wll.ceshitablayout.bean.AnimationBean;

import java.util.ArrayList;
import java.util.List;

import butterknife.BindView;
import butterknife.ButterKnife;

/**
 * 購(gòu)物車(chē)動(dòng)畫(huà)
 */
public class ShopCartActivity extends AppCompatActivity implements View.OnClickListener {

    @BindView(R.id.iv_back)
    ImageView ivBack;
    @BindView(R.id.tv_back)
    TextView tvBack;
    @BindView(R.id.rl_back)
    RelativeLayout rlBack;
    @BindView(R.id.tv_title)
    TextView tvTitle;
    @BindView(R.id.tv_select)
    TextView tvSelect;
    @BindView(R.id.rl_select)
    RelativeLayout rlSelect;
    @BindView(R.id.rl_title_bg)
    RelativeLayout rlTitleBg;
    @BindView(R.id.recycle_view)
    RecyclerView recycleView;
    @BindView(R.id.img_shop_cart)
    ImageView imgShopCart;
    @BindView(R.id.ll_contants)
    RelativeLayout llContants;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_shop_cart);
        ButterKnife.bind(this);
        ivBack.setVisibility(View.VISIBLE);
        rlBack.setVisibility(View.VISIBLE);
        tvTitle.setText("購(gòu)物車(chē)動(dòng)畫(huà)");
        rlBack.setOnClickListener(this);
        List<AnimationBean> mlist = new ArrayList<>();
        for (int i = 0; i < 20; i++) {
            AnimationBean bena = new AnimationBean();
            bena.setName("商品" + i);
            Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.common_tab);
            bena.setBitmap(bitmap);
            mlist.add(bena);
        }
        recycleView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
        ShopCartAdapter shopCartadapter = new ShopCartAdapter(this, mlist, llContants, imgShopCart);
        recycleView.setAdapter(shopCartadapter);
        shopCartadapter.notifyDataSetChanged();
    }

    @Override
    public void onClick(View v) {
        finish();
    }
}

下面是適配器的代碼(重點(diǎn))

package com.example.wll.ceshitablayout.adapter;

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.LinearInterpolator;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.example.wll.ceshitablayout.R;
import com.example.wll.ceshitablayout.bean.AnimationBean;

import java.util.List;

import butterknife.BindView;
import butterknife.ButterKnife;

/**
 * Created by wll on 2018/7/4.
 */

public class ShopCartAdapter extends RecyclerView.Adapter {
    private Context mContext;
    private List<AnimationBean> mList;
    private PathMeasure mPathMeasure;
    private RelativeLayout mRootRl;
    private ImageView mCarImageView;
    private float[] mCurrentPosition = new float[2];

    public ShopCartAdapter(Context mContext, List<AnimationBean> mList, RelativeLayout mRootRl, ImageView mCarImageView) {
        this.mContext = mContext;
        this.mList = mList;
        this.mRootRl = mRootRl;
        this.mCarImageView = mCarImageView;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        ViewHolder viewHolder = null;
        View view = LayoutInflater.from(mContext).inflate(R.layout.shop_cart_itream, null);
        viewHolder = new ViewHolder(view);
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i) {
        final ViewHolder holder = (ViewHolder) viewHolder;
        AnimationBean animationBean = mList.get(i);
        holder.tvName.setText(animationBean.getName() + "");
        holder.imgCommondity.setImageBitmap(animationBean.getBitmap());
        holder.imgCommondity.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                addGoodToCar(holder.imgCommondity);
            }
        });
    }

    @Override
    public int getItemCount() {
        return mList != null ? mList.size() : 0;
    }

    static class ViewHolder extends RecyclerView.ViewHolder {
        @BindView(R.id.img_commondity)
        ImageView imgCommondity;
        @BindView(R.id.tv_name)
        TextView tvName;

        ViewHolder(View view) {
            super(view);
            ButterKnife.bind(this, view);
        }
    }

    private void addGoodToCar(final ImageView imageView) {
        //拿到商品的圖片
        final ImageView mview = new ImageView(mContext);
        //獲取到圖片,用于繪制貝塞爾曲線(xiàn)(一定要設(shè)置,不然顯示不出來(lái))
        mview.setImageDrawable(imageView.getDrawable());
        //設(shè)置這個(gè)承載圖片的容器,以及設(shè)置大小
        RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(50, 50);
        //添加進(jìn)去
        mRootRl.addView(mview, layoutParams);

        //二斑唬、計(jì)算動(dòng)畫(huà)開(kāi)始/結(jié)束點(diǎn)的坐標(biāo)的準(zhǔn)備工作
        //得到父布局的起始點(diǎn)坐標(biāo)(用于輔助計(jì)算動(dòng)畫(huà)開(kāi)始/結(jié)束時(shí)的點(diǎn)的坐標(biāo))
        int[] parentLoc = new int[2];
        mRootRl.getLocationInWindow(parentLoc);

        //得到商品圖片的坐標(biāo)(用于計(jì)算動(dòng)畫(huà)開(kāi)始的坐標(biāo))
        int startLoc[] = new int[2];
        imageView.getLocationInWindow(startLoc);

        //得到購(gòu)物車(chē)圖片的坐標(biāo)(用于計(jì)算動(dòng)畫(huà)結(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):購(gòu)物車(chē)起始點(diǎn)-父布局起始點(diǎn)+購(gòu)物車(chē)圖片的1/5
        float toX = endLoc[0] - parentLoc[0] + mCarImageView.getWidth() / 5;
        float toY = endLoc[1] - parentLoc[1];

        //開(kāi)始繪制貝塞爾曲線(xiàn)
        Path path = new Path();
        path.moveTo(startX, startY);
        //使用二次薩貝爾曲線(xiàn):注意第一個(gè)起始坐標(biāo)越大市埋,貝塞爾曲線(xiàn)的橫向距離就會(huì)越大黎泣,一般按照下面的式子取即可
        path.quadTo((startX + toX) / 2, startY, toX, toY);
        mPathMeasure = new PathMeasure();
        mPathMeasure.setPath(path, false);

        //屬性動(dòng)畫(huà)
        float length = mPathMeasure.getLength();
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, length);
        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);
                //給圖片設(shè)置位移坐標(biāo)
                mview.setTranslationX(mCurrentPosition[0]);
                mview.setTranslationY(mCurrentPosition[1]);
            }
        });
        valueAnimator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {

            }

            @Override
            public void onAnimationEnd(Animator animation) {
                // 購(gòu)物車(chē)的數(shù)量加1

                // 把移動(dòng)的圖片imageview從父布局里移除
                mRootRl.removeView(mview);

                //shopImg 開(kāi)始一個(gè)放大動(dòng)畫(huà)
                Animation scaleAnim = AnimationUtils.loadAnimation(mContext, R.anim.shop_car_scale);
                mCarImageView.startAnimation(scaleAnim);
            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
        valueAnimator.start();
    }
}

可以看到,使用

 //開(kāi)始繪制貝塞爾曲線(xiàn)
        Path path = new Path();
        path.moveTo(startX, startY);
        //使用二次薩貝爾曲線(xiàn):注意第一個(gè)起始坐標(biāo)越大,貝塞爾曲線(xiàn)的橫向距離就會(huì)越大缤谎,一般按照下面的式子取即可
        path.quadTo((startX + toX) / 2, startY, toX, toY);
        mPathMeasure = new PathMeasure();
        mPathMeasure.setPath(path, false);

mPathMeasure.setPath(path, false);第二個(gè)參數(shù)設(shè)置成true,則繪制成閉合的曲線(xiàn),可以看到繪制這個(gè)動(dòng)畫(huà)代碼量不大,主要是交給PathMeasure來(lái)進(jìn)行繪制路線(xiàn)了.
下面是對(duì)PathMeasure的一些知識(shí)點(diǎn),可以了解一下
PathMeasure是用來(lái)操作Path的抒倚,初始化

mPathMeasure = new PathMeasure();
mPathMeasure.setPath(path, false);
//forceClosed 就是Path最終是否需要閉合,如果為T(mén)rue的話(huà)坷澡,則不管關(guān)聯(lián)的Path是否是閉合的托呕,都會(huì)被閉合 PathMeasure的計(jì)算就會(huì)包含最后一段閉合的路徑

得到path長(zhǎng)度,可以這樣理解频敛,不管實(shí)際 Path 多么的復(fù)雜项郊,PathMeasure 都相當(dāng)于做了一個(gè)事情,就是把 Path “拉直”斟赚,然后給了我們一個(gè)接口(getLength)告訴我們path的總長(zhǎng)度

 mPathMeasure.getLength()  

注意 這里得到的length是當(dāng)前mPathMeasure指向線(xiàn)段的長(zhǎng)度着降,并不是path總長(zhǎng)度,如果要得到總長(zhǎng)度需要通過(guò)nextContour來(lái)遍歷mPathMeasure拗军,得到每段長(zhǎng)度再加起來(lái)

得到Path中的某一點(diǎn)或某一段
getPosTan(float distance, float[] pos, float[] tan)
getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo)

這里要注意的是得到的線(xiàn)段是添加進(jìn) dst中的任洞,并不是給dst重新賦值,如果想要重新生成一個(gè)線(xiàn)段发侵,可以先重置dst path.reset(). startWithMoveTo
為true:再次截取交掏,起始點(diǎn)為0時(shí),還是原path的起始點(diǎn)刃鳄。
為false:再次截取耀销,起始點(diǎn)為0時(shí),為上次截取的終點(diǎn)铲汪。
startD stopD 是當(dāng)前PathMeasure指向線(xiàn)段的開(kāi)始和起始位置

Path 可以由多條曲線(xiàn)構(gòu)成熊尉,但不論是 getLength , getgetSegment 或者是其它方法,都只會(huì)在其中第一條線(xiàn)段上運(yùn)行掌腰,而這個(gè) nextContour 就是用于跳轉(zhuǎn)到下一條曲線(xiàn)到方法狰住,如果跳轉(zhuǎn)成功,則返回 true齿梁, 如果跳轉(zhuǎn)失敗催植,則返回 false。

這里要注意開(kāi)始時(shí)直接調(diào)用 mPathMeasure.nextContour ()會(huì)跳到第一條線(xiàn)段勺择,但如果你操作了mPathMeasure创南,如getLength,它會(huì)自動(dòng)跳到第一條線(xiàn)段省核。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末稿辙,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子气忠,更是在濱河造成了極大的恐慌邻储,老刑警劉巖赋咽,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異吨娜,居然都是意外死亡脓匿,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)宦赠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)陪毡,“玉大人,你說(shuō)我怎么就攤上這事勾扭≌绷穑” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵尺借,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我精拟,道長(zhǎng)燎斩,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任蜂绎,我火速辦了婚禮栅表,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘师枣。我一直安慰自己怪瓶,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布践美。 她就那樣靜靜地躺著洗贰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪陨倡。 梳的紋絲不亂的頭發(fā)上敛滋,一...
    開(kāi)封第一講書(shū)人閱讀 51,631評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音兴革,去河邊找鬼绎晃。 笑死,一個(gè)胖子當(dāng)著我的面吹牛杂曲,可吹牛的內(nèi)容都是我干的庶艾。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼擎勘,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼咱揍!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起棚饵,我...
    開(kāi)封第一講書(shū)人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤述召,失蹤者是張志新(化名)和其女友劉穎朱转,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體积暖,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡藤为,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了夺刑。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片缅疟。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖遍愿,靈堂內(nèi)的尸體忽然破棺而出存淫,到底是詐尸還是另有隱情,我是刑警寧澤沼填,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布桅咆,位于F島的核電站,受9級(jí)特大地震影響坞笙,放射性物質(zhì)發(fā)生泄漏岩饼。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一薛夜、第九天 我趴在偏房一處隱蔽的房頂上張望籍茧。 院中可真熱鬧,春花似錦梯澜、人聲如沸寞冯。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)吮龄。三九已至,卻和暖如春咆疗,著一層夾襖步出監(jiān)牢的瞬間螟蝙,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工民傻, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留胰默,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓漓踢,卻偏偏與公主長(zhǎng)得像牵署,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子喧半,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容