Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 利用二次貝塞爾曲線模仿購物車添加物品拋物線動畫

Android 利用二次貝塞爾曲線模仿購物車添加物品拋物線動畫

編輯:關於Android編程

0.首先,先給出一張效果gif圖。

\

1.貝塞爾曲線原理及相關公式參考:http://www.jianshu.com/p/c0d7ad796cee 作者:許方鎮。

2.原理:計算被點擊 view、購物車view 以及他們所在父容器相對於屏幕的坐標。

3.在呗點擊View坐標位置 父容器通過addView 增加需要完成動畫的imgview。

4.自定義估值器 通過二次貝塞爾曲線公式(2個數據點,一個控制點)完成拋物線路徑上的點xy坐標計算。

5.利用屬性動畫 +自定義估值器 完成imgview在父容器內部的拋物線動畫。

6.先給布局,其中包含一個ListView、 一個ImageView 、需要用到的父容器。

 




    

        
        
        
    



7. 給出ListView Item 布局:

 

 




    

    


8.給出ListView Adapter代碼 僅僅是點擊時增加了回調接口:

 

 

public class ItemAdapter extends BaseAdapter implements View.OnClickListener {
    List data = new ArrayList<>();
    Context mContext;

    public ItemAdapter(Context context) {
        mContext = context;
        for (int i = 0; i < 30; i++) {
            data.add("item+" + i);
        }
    }

    @Override
    public int getCount() {
        return data.size();
    }

    @Override
    public Object getItem(int position) {
        return data.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if (convertView == null) {
            convertView = LayoutInflater.from(mContext).inflate(R.layout.item, parent, false);
            convertView.setTag(new ViewH(convertView));
        }
        ViewH holder = (ViewH) convertView.getTag();
        holder.tv.setText(data.get(position));
        holder.img.setOnClickListener(this);
        return convertView;
    }

    @Override
    public void onClick(View v) {
        if (mListener != null) {
            mListener.add(v);
        }
    }

    private AddClickListener mListener;

    public void setListener(AddClickListener listener) {
        mListener = listener;
    }

    public interface AddClickListener {
        void add(View v);
    }

    public static class ViewH {
        private ImageView img;
        private TextView tv;

        public ViewH(View view) {
            img = ((ImageView) view.findViewById(R.id.item_img));
            tv = ((TextView) view.findViewById(R.id.item_text));
        }
    }
}
9.其中自定義MoveImageView僅僅是增加了一個set方法方便屬性動畫 update時調用。

 

 

public class MoveImageView extends ImageView {

    public MoveImageView(Context context) {
        super(context);
    }

    public void setMPointF(PointF pointF) {
        setX(pointF.x);
        setY(pointF.y);
    }
}


10.重要的實現在Activity部分:

 

 

public class MainActivity extends AppCompatActivity implements ItemAdapter.AddClickListener, Animator.AnimatorListener {

    private ImageView shopImg;//購物車 IMG
    private RelativeLayout container;//ListView 購物車View的父布局
    private ListView itemLv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViews();
        initViews();
    }

    private void initViews() {
        ItemAdapter adapter = new ItemAdapter(this);
        //當前Activity實現 adapter內部 點擊的回調
        adapter.setListener(this);
        itemLv.setAdapter(adapter);
    }

    /**
     * ListView + 點擊回調方法
     */
    @Override
    public void add(View addV) {
        int[] childCoordinate = new int[2];
        int[] parentCoordinate = new int[2];
        int[] shopCoordinate = new int[2];
        //1.分別獲取被點擊View、父布局、購物車在屏幕上的坐標xy。
        addV.getLocationInWindow(childCoordinate);
        container.getLocationInWindow(parentCoordinate);
        shopImg.getLocationInWindow(shopCoordinate);

        //2.自定義ImageView 繼承ImageView
        MoveImageView img = new MoveImageView(this);
        img.setImageResource(R.mipmap.heart1);
        //3.設置img在父布局中的坐標位置
        img.setX(childCoordinate[0] - parentCoordinate[0]);
        img.setY(childCoordinate[1] - parentCoordinate[1]);
        //4.父布局添加該Img
        container.addView(img);

        //5.利用 二次貝塞爾曲線 需首先計算出 MoveImageView的2個數據點和一個控制點
        PointF startP = new PointF();
        PointF endP = new PointF();
        PointF controlP = new PointF();
        //開始的數據點坐標就是 addV的坐標
        startP.x = childCoordinate[0] - parentCoordinate[0];
        startP.y = childCoordinate[1] - parentCoordinate[1];
        //結束的數據點坐標就是 shopImg的坐標
        endP.x = shopCoordinate[0] - parentCoordinate[0];
        endP.y = shopCoordinate[1] - parentCoordinate[1];
        //控制點坐標 x等於 購物車x;y等於 addV的y
        controlP.x = endP.x;
        controlP.y = startP.y;

        //啟動屬性動畫
        ObjectAnimator animator = ObjectAnimator.ofObject(img, "mPointF",
                new PointFTypeEvaluator(controlP), startP, endP);
        animator.setDuration(1000);
        animator.addListener(this);
        animator.start();
    }

    @Override
    public void onAnimationStart(Animator animation) {
    }

    @Override
    public void onAnimationEnd(Animator animation) {
        //動畫結束後 父布局移除 img
        Object target = ((ObjectAnimator) animation).getTarget();
        container.removeView((View) target);
        //shopImg 開始一個放大動畫
        Animation scaleAnim = AnimationUtils.loadAnimation(this, R.anim.shop_scale);
        shopImg.startAnimation(scaleAnim);
    }

    @Override
    public void onAnimationCancel(Animator animation) {
    }

    @Override
    public void onAnimationRepeat(Animator animation) {
    }

    /**
     * 自定義估值器
     */
    public class PointFTypeEvaluator implements TypeEvaluator {
        /**
         * 每個估值器對應一個屬性動畫,每個屬性動畫僅對應唯一一個控制點
         */
        PointF control;
        /**
         * 估值器返回值
         */
        PointF mPointF = new PointF();

        public PointFTypeEvaluator(PointF control) {
            this.control = control;
        }

        @Override
        public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
            return getBezierPoint(startValue, endValue, control, fraction);
        }

        /**
         * 二次貝塞爾曲線公式
         *
         * @param start   開始的數據點
         * @param end     結束的數據點
         * @param control 控制點
         * @param t       float 0-1
         * @return 不同t對應的PointF
         */
        private PointF getBezierPoint(PointF start, PointF end, PointF control, float t) {
            mPointF.x = (1 - t) * (1 - t) * start.x + 2 * t * (1 - t) * control.x + t * t * end.x;
            mPointF.y = (1 - t) * (1 - t) * start.y + 2 * t * (1 - t) * control.y + t * t * end.y;
            return mPointF;
        }
    }

    private void findViews() {
        shopImg = (ImageView) findViewById(R.id.main_img);
        container = (RelativeLayout) findViewById(R.id.main_container);
        itemLv = (ListView) findViewById(R.id.main_lv);
    }
}

11.購物車有一個Scale的補間動畫:

 

 



    

12.供參考~完~~

 

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved