属性动画:如何自定义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
- 属性动画:如何自定义View
- 自定义view加属性动画
- 自定义view画圆加属性动画
- 自定义view+属性动画实现
- Android自定义view之属性动画初见
- 自定义view实心圆加属性动画
- 自定义view圆加属性动画
- 如何使用View自定义属性画圆
- 详解CoreAnimation中如何自定义动画属性
- COREANIMATION中如何自定义动画属性
- 详解CoreAnimation中如何自定义动画属性
- COREANIMATION中如何自定义动画属性
- 属性动画ValueAnimator在自定义View中的使用
- Android 自定义view及其属性。友情链接底部有动画链接
- android 自定义view+属性动画实现充电进度条
- android 自定义view+属性动画实现充电进度条
- 属性动画绘制者Animator和自定义View
- 自定义View和属性动画ValueAnimator实现圆点指示器
- Java对象锁和类锁全面解析(多线程synchronized关键字)
- liteorm简单封装
- shiro中获取当前user出错
- 大话设计模式Java版前三章总结
- Intellij+Maven踩坑
- 属性动画:如何自定义View
- 通俗理解信息熵
- 链接问题解决办法-LNK2001 无法解析的外部符号 __imp__RegQueryValueExA@24
- Java反射机制详解
- javascript中的原型(prototype)及原型链的继承方式
- 控制流图、圈复杂度
- MUI 页面跳转(传值+接收)
- ESP8266使用
- 【Java】JavaWeb文件上传和下载