自定义动画

更新时间:2023-03-02 05:42:24 阅读: 评论:0

婴幼儿早期教育-可以修图的软件

自定义动画
2023年3月2日发(作者:幼儿园个人总结)

Android简单实现⾃定义的动画效果

我⾃⼰公司的业务需求上是基本不会涉及动画相关的,也可以说基本两年没怎么做过动画相关的开发,这次写这篇⽂章是因为读到⼀篇⽐较好的⽂

章,想把它记录下来,以后有做动画效果的需求的话就能快速上⼿。

参考《Android开发艺术探索》7.3.4对任意属性做动画

众所周知Android⾥⾯动画有3种,View动画、帧动画和属性动画。不同的场景使⽤不同的动画,对控件⽽⾔,你想实现⼀个⾃定义动画的效果,

也就是随⼼,那就使⽤属性动画会⽐较好,因为View动画只提供了简单的平移、翻转、缩放、透明度。

当然这⾥也不会去介绍⼀些基础的内容。为什么单独说这⼀⼩节,因为我觉得这个地⽅是这章(第七章)最经典的地⽅。

⼀.对任意属性做动画

按照书上的逻辑来讲。

⼀开始举了⼀个栗⼦,给Button加个动画,让这个Button的宽度增加500px。

直接写属性动画的代码:

(mButton,"width",500).tDuration(5000).start();

这⾥是对这个Button的width属性做动画,也就是对宽度这个属性做动画,但是并不能实现我们想要的宽度增加500px的效果。这时候就先引出

了⼀个重要的结论:

属性动画要求动画作⽤的对象提供该属性的get和t⽅法

这个很重要,意思就是说,这个mButton对象,没有提供getWidth和tWidth⽅法。其实这说法有点那啥,因为Button是有getWidth和

tWidth的⽅法,但是为什么没⽣效呢?

这是展⽰了tWidth的源码

publicvoidtWidth(intpixels){

mMaxWidth=mMinWidth=pixels;

mMaxWidthMode=mMinWidthMode=PIXELS;

requestLayout();

invalidate();

}

发现这⾥并不是设置Button的宽度,⽽是设置Button的最⼤宽度和最⼩宽度。

也就是说在上⾯的动画中:真的的是随着时间的改变去改变控件的最⼤最⼩宽度,所以视觉看不出效果,因为实际作⽤的属性不是你想要去让它作

⽤的属性

所以想要实现效果,应该要提供该属性正确的get/t⽅法

书上⼜写出了这样的⼀个结论(原话):

针对上诉问题,官⽅⽂档上告诉我们有3种解决⽅法:

给你的对象加上get和t⽅法,如果你有权限的话

⽤⼀个类来包装原始对象,间接为其提供get和t⽅法

采⽤ValueAnimator,监听动画过程,⾃⼰实现属性的变化

第⼀个⽅案肯定不可⾏,系统的View不是我们说改就改的,然后⽤两个Demo分别来描述⽅法2和⽅法3(不能复制要⼿写,好不想写)

⽅法2:

privatestaticclassViewWrapper{

privateViewmTarget;

publicViewWrapper(Viewtarget){

mTarget=target;

}

publicintgetWidth(){

outParams().width;

}

publicvoidtWidth(intwidth){

outParams().width=width;

tLayout();

}

}

调⽤的时候

ViewWrapperwrapper=newViewWrapper(mButton);

(wrapper,"width",500).tDuration(5000).start();

这样就能正常展⽰我们想要的动画效果,对象是wrapper,它确实为width属性提供了get/t⽅法。动画内部在执⾏过程中会拿到传给它的属

性,然后⽤反射去调⽤它的get/st⽅法。但是怎么说呢,具体的属性改变的算法还是要你去⼿动写。

⽅法3:

ValueAnimatorvalueAnimator=(1,100);

ateListener(orUpdateListener(){

privateIntEvaluatormEvaluator=newIntEvaluator();

@Override

publicvoidonAnimationUpdate(ValueAnimatoranimation){

intcurrentValue=(Integer)matedValue();

floatfraction=matedFraction();

outParams().width=te(fraction,start,end);

tLayout();

}

});

这个⽅法就是对动画进⾏监听,每监听到⼀帧的时候再做具体的操作。可以看到这⾥也没写get/t⽅法,⼤概就那么⼀个意思就得了,也不是⾮

得说⼀定要这样写,或者不⽤这两种⽅法也还有其它⽅法能实现。

总之,属性的具体的变化,是需要我们⾃⼰去写逻辑(如果要实现⾃定义的效果),⽽ObjectAnimator、ValueAnimator这些属性⼴告的类,

能告诉我们这个动画执⾏的整个过程,简单的说就是我们知道播放到哪⼀帧,就能做到在这⼀帧做什么效果。

这两个Demo也是我觉得这章最有意思的地⽅。

⼆.⾃⼰实现⼀个⾃定义动画

光看肯定是3天忘,⾃⼰动⼿写个简单的⾃定义动画效果。

我弄⼀个圆形View,可以拖动它,点击下去的时候圆会放⼤并变成正⽅形,然后移动,松开时正⽅形会缩⼩并变回圆,⽽且我们要让圆变成正⽅

形不是秒变,要有个渐变的效果(只实现了简单的渐变效果,因为懒得去计算)。

最终的效果也没有做成gif,想看效果的直接复制代码去试就⾏,不多。

1.绘制view的初始位置

我这Demo再补充⼀些细节的东西,还是《Android开发艺术探索》3.1.2View的位置参数

不是什么难点,就是有些细节要注意,这个view的位置参数列举了3套

(1)Left,Right,Top,Bottom,当成⼀个矩形看(圆也是矩形),就是4条边距离⽗容器xy轴的距离。

(2)x,y左上点的坐标

(3)translationX,translationY距离⽗View坐标的偏移量

我就直接贴代码讲吧。

先看Activity布局

xmlns:android="/apk/res/android"

android:id="@+id/fl_content"

android:padding="100px"

android:layout_width="match_parent"

android:layout_height="match_parent">

这⾥padding⼀个100px,是为了⽅便介绍这些⽅位,单位相同容易看,实际开发中肯定最好⽤dp做单位。

然后看Activity代码

publicclassRsActivityextendsActivity{

privateFrameLayoutflContent;

privateRoundSquareViewroundSquareView;

@Override

protectedvoidonCreate(BundlesavedInstanceState){

te(savedInstanceState);

tContentView(ty_rs);

initView();

}

privatevoidinitView(){

flContent=findViewById(_content);

roundSquareView=newRoundSquareView(this);

Paramslp=Params(150,150);

outParams(lp);

w(roundSquareView);

(100);

(100);

}

@Override

protectedvoidonResume(){

me();

Handlerhandler=newHandler();

layed(newRunnable(){

@Override

publicvoidrun(){

Log.v("mmp","getLeft"+t());

Log.v("mmp","getTranslationX"+nslationX());

Log.v("mmp","getX"+());

(0);

}

},1000);

}

}

RoundSquareView是⼀个⾃定义View,先不⽤管它。

第⼀个问题来了,为什么要写个延迟,重点啊,因为getLeft,就是获取第⼀套的那4个属性,要在view绘制完成之后才能获取到,不然获取到的

会是0,加个延迟是为了保证绘制完成(这只是Demo,真实开发中判断绘制完成肯定不能⽤延时这样玩)

看看显⽰结果

再看看打印结果

距离顶部和左边都是200px,但是left打印是100,如果把⽗布局中的padding去掉的话,left会打印0,这就是第⼆个地⽅,使⽤Left的时候要注

意padding,并且它不算translationX偏移的部分。

然后translationX也打印的是100,他也不算是padding的部分。

最后getX是200,他是正常的相对于⽗布局的部分距离,不受任何影响。

也证实了x=left+translationX

这⾥只是为了介绍⼀些细节上的东西⽽已,onResume⾥⾯的代码对我们要实现的功能没有任何意义,所以使⽤时请屏蔽掉。

2.⾃定义View

看看代码

publicclassRoundSquareViewextendsView{

privatefloatoldRawX=0;

privatefloatoldRawY=0;

privateintcurrentValue=0;

privatebooleanisExpand;

publicRoundSquareView(Contextcontext){

super(context);

}

publicRoundSquareView(Contextcontext,AttributeSetattrs){

super(context,attrs);

}

publicRoundSquareView(Contextcontext,AttributeSetattrs,intdefStyleAttr){

super(context,attrs,defStyleAttr);

}

@Override

publicvoiddraw(Canvascanvas){

(canvas);

Paintpaint=newPaint();

or();

le();

okeWidth(1);

intw=getWidth()/2;

inth=getHeight()/2;

if(!isExpand){

if(!isExpand){

//⾮扩⼤状态下的绘制

rcle(w,h,w+currentValue,paint);

}el{

//扩⼤状态下的绘制

rcle(w,h,w+(100-currentValue),paint);

}

}

@Override

publicbooleanonTouchEvent(MotionEventevent){

switch(ion()){

_DOWN:

isExpand=fal;

expand();

oldRawX=X();

oldRawY=Y();

returntrue;

_MOVE:

moveView(X(),Y());

returntrue;

_UP:

oldRawX=0;

oldRawX=0;

isExpand=true;

narrow();

returntrue;

}

hEvent(event);

}

@Override

publicbooleanperformClick(){

mClick();

}

privatevoidmoveView(floatrawX,floatrawY){

//计算偏移量

floatofftX=rawX-oldRawX;

floatofftY=rawY-oldRawY;

//更改位置

tX(getX()+offtX);

tY(getY()+offtY);

//更新位置

oldRawX=rawX;

oldRawY=rawY;

}

/**

*扩⼤

*/

privatevoidexpand(){

intw=getWidth();

inth=getHeight();

Paramslp=outParams();

ValueAnimatorvalueAnimator=(1,100);

ateListener(orUpdateListener(){

@Override

publicvoidonAnimationUpdate(ValueAnimatoranimation){

currentValue=(int)matedValue();

Log.v("mmp","动画进度"+currentValue);

=w+(w/100*currentValue);

=h+(h/100*currentValue);

outParams(lp);

}

});

ation(300).start();

}

/**

*缩⼩

*/

privatevoidnarrow(){

intw=getWidth();

inth=getHeight();

Paramslp=outParams();

ValueAnimatorvalueAnimator=(1,100);

ateListener(orUpdateListener(){

@Override

publicvoidonAnimationUpdate(ValueAnimatoranimation){

currentValue=(int)matedValue();

Log.v("mmp","动画进度"+currentValue);

=w-(w/200*currentValue);

=h-(h/200*currentValue);

outParams(lp);

}

});

ation(300).start();

}

}

⾃定义View,在draw中⽤Paint画个圆,这个,这个没什么好说的,都能看懂,最后

if(!isExpand){

//⾮扩⼤状态下的绘制

rcle(w,h,w+currentValue,paint);

}el{

//扩⼤状态下的绘制

rcle(w,h,w+(100-currentValue),paint);

}

先不⽤管,先看成

rcle(w,h,w,paint);

这样就画成⼀个圆了。

然后写onTouchEvent让view随⼿指移动⽽移动。记录改变前的⼿指的点击位置oldRawX,oldRawY,去获取当前的位置减去旧的位置就能得到

⼀个偏移量,然后再⽤这个偏移量去改变x和y属性,这是最简单的view随⼿指移动的⽅式。

然后在按下时调⽤expand()做放⼤的属性动画,再抬起时调⽤narrow()做缩⼩的属性动画。

这⾥是⽤了上⾯的⽅法3去实现属性动画,这⾥离就不多说了,定义了⼀个变量isExpand来记录当前是否处于放⼤的状态。

最后,这个Demo只是简单的实现动画的效果,会存在⼀些问题,⽐如说在放⼤动画的执⾏过程中抬起,这些都没做处理,时间问题,就不打算花

太多时间在这个Demo上。

本文发布于:2023-03-02 05:42:24,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/zhishi/a/16777069445623.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

本文word下载地址:自定义动画.doc

本文 PDF 下载地址:自定义动画.pdf

上一篇:学习说说
下一篇:返回列表
标签:自定义动画
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 实用文体写作网旗下知识大全大全栏目是一个全百科类宝库! 优秀范文|法律文书|专利查询|