zoukankan      html  css  js  c++  java
  • 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 、须要用到的父容器。

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#00ffe1"
        android:orientation="vertical"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        tools:context=".MainActivity">
    
        <RelativeLayout
            android:id="@+id/main_container"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <ListView
                android:id="@+id/main_lv"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:divider="#0011ff"
                android:dividerHeight="2dp"/>
            <!-- shop img-->
            <ImageView
                android:id="@+id/main_img"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentBottom="true"
                android:layout_marginBottom="20dp"
                android:layout_marginLeft="20dp"
                android:src="@mipmap/shop"/>
        </RelativeLayout>
    
    </LinearLayout>
    

    7. 给出ListView Item 布局:

    <?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"
                    android:background="#FFF"
                    android:padding="30dp">
    
        <TextView
            android:id="@+id/item_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:textColor="#F00"
            android:textSize="20sp"/>
    
        <ImageView
            android:id="@+id/item_img"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:src="@mipmap/add"/>
    
    </RelativeLayout>

    8.给出ListView Adapter代码 不过点击时添加了回调接口:

    public class ItemAdapter extends BaseAdapter implements View.OnClickListener {
        List<String> 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> {
            /**
             * 每一个估值器相应一个属性动画。每一个属性动画仅相应唯一一个控制点
             */
            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的补间动画:

    <?xml version="1.0" encoding="utf-8"?

    > <set xmlns:android="http://schemas.android.com/apk/res/android" android:duration="200" android:repeatCount="1" android:repeatMode="reverse"> <scale android:fromXScale="1.0" android:fromYScale="1.0" android:pivotX="50%" android:pivotY="50%" android:toXScale="1.2" android:toYScale="1.2"/> </set>


    12.供參考~完~~


  • 相关阅读:
    “无法更新EntitySet“*****”,因为它有一个DefiningQuery,而元素中没有支持当前操作的元素”问题的解决方法
    Web.Config全攻略
    C#常用的正则
    Asp.Net MVC2 Json
    webservice+Jquery返回Json格式【原创】
    JAVA线程池介绍以及简单实例
    从程序员到项目经理(17):你不是一个人在战斗思维一换天地宽
    SELECT INTO 和 INSERT INTO SELECT 两种表复制语句
    多表对应更新(跨服务器).sql
    sql导出excel.sql
  • 原文地址:https://www.cnblogs.com/mthoutai/p/7399533.html
Copyright © 2011-2022 走看看