属性动画:如何自定义View

来源:互联网 发布:淘宝空间已满怎么删除 编辑:程序博客网 时间:2024/06/05 07:47

道长今天说一下自定义View的实现,因为有很大一部分自定义View都带有动画,所以把自定义View放到属性动画里聊聊,let’s go……

一、需求明确

首先明确需求,根据需求创建布局,这里道长就自己定需求了:
1.拖动红色方块,红色沿着中心旋转并且颜色便为绿色,蓝色沿着Y轴旋转
2.拖动蓝色方块,红色沿着中心旋转并且颜色便为绿色,蓝色沿着Y轴旋转
3.拖动绿色方块,红色沿着中心旋转并且颜色便为绿色,蓝色沿着Y轴旋转
4.三种方块只可在屏幕内拖动

这里需求对布局的要求不明显,咱们把整个界面作为自定义View,代码如下:

<?xml version="1.0" encoding="utf-8"?><com.yushan.animdemo.DragLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="com.yushan.animdemo.MainActivity">    <TextView        android:layout_width="50dp"        android:layout_height="50dp"        android:background="#ff0000" />    <TextView        android:layout_width="50dp"        android:layout_height="50dp"        android:background="#0000ff" />    <TextView        android:layout_width="50dp"        android:layout_height="50dp"        android:background="#00ff00" /></com.yushan.animdemo.DragLayout>


效果如下:
这里写图片描述

二、实现自定义View

  • 首先重写构造方法:
    public DragLayout(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        init();    }    public DragLayout(Context context, AttributeSet attrs) {        super(context, attrs);        init();    }    public DragLayout(Context context) {        super(context);        init();    }    private void init(){        scroller = new Scroller(getContext());        viewDragHelper = ViewDragHelper.create(this, callback);    }
  • 获取子View
    /**     * 当加载完布局xml的时候会执行该方法,所以执行该方法 的时候就能够知道当前的ViewGroup     * 有多少个子View,但是此时并不知道子View的宽高是多少,因为还没有测量     */    @Override    protected void onFinishInflate() {        super.onFinishInflate();        redView = getChildAt(0);        blueView = getChildAt(1);        yellowView = getChildAt(2);    }
  • 测量自己和子控件的宽高(这个不是必须的)
    /**     * 测量自己和子控件的宽高     * MeasureSpec: 测量规则,由size和mode组成     * size:表示的是具体的大小值     * mode:测量模式      封装的是我们在布局xml中的宽高参数     *     * MeasureSpec.AT_MOST: 对应的是wrap_content;     * MeasureSpec.EXACTLY: 对应的是具体的dp值,match_parent;     * MeasureSpec.UNSPECIFIED: 未定义的,一般只在adapter的测量中用到     */    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        //构建测量规则        //测量红孩子        int measureSpec = MeasureSpec.makeMeasureSpec(redView.getLayoutParams().width,MeasureSpec.EXACTLY);//      redView.measure(measureSpec, measureSpec);//      //测量蓝精灵//      blueView.measure(measureSpec, measureSpec);        //更加简单的测量子View的方法是这样的:        measureChild(redView, widthMeasureSpec, heightMeasureSpec);        measureChild(blueView, widthMeasureSpec, heightMeasureSpec);    }
  • 放置控件(这个是控件的初始位置,必须要有)
    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        int left = 0;        int top = 0;        redView.layout(left,top,left+redView.getMeasuredWidth(), top+redView.getMeasuredHeight());        blueView.layout(left,redView.getBottom(),left+blueView.getMeasuredWidth(), redView.getBottom()+blueView.getMeasuredHeight());        yellowView.layout(left,blueView.getBottom(),left+yellowView.getMeasuredWidth(), blueView.getBottom()+yellowView.getMeasuredHeight());        //将某个子View提到最上面//      bringChildToFront(redView);    }
  • 拦截事件(这里道长集成了NineOldAndroid并重写了Callback类中的方法)
  @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        // 让viewDragHelper帮助我们判断是否应该拦截        boolean result = viewDragHelper.shouldInterceptTouchEvent(ev);        return result;    }    @Override    public boolean onTouchEvent(MotionEvent event) {        //将TouchEvent传递给viewDragHelper来处理        viewDragHelper.processTouchEvent(event);        return true;    }    private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {        /**         * 是否捕获view的触摸         * child: 表示当前所触摸的子VIew         * return: true:会捕获      false:不会捕获,即忽略         */        @Override        public boolean tryCaptureView(View child, int pointerId) {            return child==blueView || child==redView || child==yellowView;        }        /**         * 当View被捕获的时候会回调         */        @Override        public void onViewCaptured(View capturedChild, int activePointerId) {            super.onViewCaptured(capturedChild, activePointerId);//          Log.e("tag", "onViewCaptured");        }        /**         * 获取view水平方向拖拽范围,但是目前并不起作用,但是最好还要实现下,不要         * 返回0,它目前返回的值会用在计算view释放移动的动画时间计算上面         */        @Override        public int getViewHorizontalDragRange(View child) {            return DragLayout.this.getMeasuredWidth()-blueView.getMeasuredWidth();        }        /**         * 控制child在水平方向的移动         * child:当前所触摸的子View         * left:表示ViewDragHelper帮你计算好的child的最终要变成的left值, left=child.getLeft()+dx         * dx:表示本次水平移动的距离         * return: 表示我们真正想让child的left变成的值         */        @Override        public int clampViewPositionHorizontal(View child, int left, int dx) {            if(left<0){                left = 0;            }            return left;        }        /**         * 控制child在垂直方向的移动         * child:当前所触摸的子View         * top:表示ViewDragHelper帮你计算好的child的最终要变成的top值, top=child.getTop()+dy         * dy:表示本次垂直移动的距离         * return: 表示我们真正想让child的top变成的值         */        public int clampViewPositionVertical(View child, int top, int dy) {            if(top<0){                top = 0;            }            return top;        }        /**         * 当view位置改变的回调,一般用来实现view的伴随移动         * changedView:表示当前位置改变了的view         * left:changedView的最新的left         * top:changedView的最新的top         * dx:changedView本次水平移动距离         * dy:changedView本次垂直移动距离         */        @Override        public void onViewPositionChanged(View changedView, int left, int top,                                          int dx, int dy) {            super.onViewPositionChanged(changedView, left, top, dx, dy);            if(changedView==blueView){                //让redView跟随移动                redView.layout(redView.getLeft()+dx,redView.getTop()+dy, redView.getRight()+dx, redView.getBottom()+dy);            }else if (changedView==redView) {                //让blueView跟随移动                blueView.layout(blueView.getLeft()+dx,blueView.getTop()+dy, blueView.getRight()+dx, blueView.getBottom()+dy);            } else if (changedView==yellowView){                //让redView跟随移动                redView.layout(redView.getLeft()+dx,redView.getTop()+dy, redView.getRight()+dx, redView.getBottom()+dy);                //让blueView跟随移动                blueView.layout(blueView.getLeft()+dx,blueView.getTop()+dy, blueView.getRight()+dx, blueView.getBottom()+dy);            }            //1.计算移动的百分比            int maxLeft = DragLayout.this.getMeasuredWidth()-blueView.getMeasuredWidth();            float fraction = changedView.getLeft()*1f/maxLeft;            //2.根据移动的百分比执行很多的伴随动画            executeAnim(fraction);        }        /**         * 当View释放的时候执行,就是touch_up         * releasedChild:当前抬起的子VIew         * xvel:x方向移动的速度         * yvel:y方向移动的速度         */        @Override        public void onViewReleased(View releasedChild, float xvel, float yvel) {            super.onViewReleased(releasedChild, xvel, yvel);//          Log.e("tag", "xvel:"+xvel  +  "  yvel: "+yvel);            //首先算出在正中间的left            int centerLeft = getMeasuredWidth()/2-releasedChild.getMeasuredWidth()/2;            if(releasedChild.getLeft()<centerLeft){                //说明在左半边                viewDragHelper.smoothSlideViewTo(releasedChild,0,releasedChild.getTop());                ViewCompat.postInvalidateOnAnimation(DragLayout.this);//              scroller.startScroll(startX, startY, dx, dy, duration);//              invalidate();            }else {                //说明在右半边                int finalLeft = getMeasuredWidth()-releasedChild.getMeasuredWidth();                viewDragHelper.smoothSlideViewTo(releasedChild,finalLeft,releasedChild.getTop());                ViewCompat.postInvalidateOnAnimation(DragLayout.this);            }        }    };
  • 执行动画方法
    /**     * 执行动画     * @param fraction     */    private void executeAnim(float fraction){        //旋转//        blueView.setRotation(360*fraction);//设置旋转的角度//      blueView.setRotationX(360*fraction);//设置围绕x轴旋转的角度        blueView.setRotationY(360*fraction);//设置围绕Y轴旋转的角度        //使用NineOldAndroid中的方法        ViewHelper.setRotation(redView, 360*fraction);//设置旋转的角度//        ViewHelper.setScaleX(redView, 1+fraction*0.5f);//        ViewHelper.setScaleY(redView, 1+fraction*0.5f);        //进行颜色的过度变化        redView.setBackgroundColor((Integer) ColorUtil.evaluateColor(fraction, Color.RED,Color.GREEN));    }

到这里就把View定义好了,当然这个View可以放到其他界面中,通过动画自定义View的好处是很明显的,就是实现简单灵活。希望这篇博客可以给你一些帮助。

源码下载

AnimDemo


0 0
原创粉丝点击