Android实现跳动的小球加载动画效果
程序员文章站
2024-03-06 20:23:56
先来看看效果图
跳动的小球做这个动画,需掌握:
1、属性动画
&nb...
先来看看效果图
跳动的小球做这个动画,需掌握:
1、属性动画
2、path类、canvas类
3、贝塞尔曲线
4、surfaceview用法
5、自定义attr属性
6 、架构: 状态模式,控制器
7 、*落体,抛物线等概念
不多说了,直接上码
1.dancingview.java
public class dancingview extends surfaceview implements surfaceholder.callback { public static final int state_down = 1;//向下状态 public static final int state_up = 2;//向上状态 public static final int default_point_radius = 10; public static final int default_ball_radius = 13; public static final int default_line_width = 200; public static final int default_line_height = 2; public static final int default_line_color = color.parsecolor("#ff9800"); public static final int default_point_color = color.parsecolor("#9c27b0"); public static final int default_ball_color = color.parsecolor("#ff4081"); public static final int default_down_duration = 600;//ms public static final int default_up_duration = 600;//ms public static final int default_freedown_duration = 1000;//ms public static final int max_offset_y = 50;//水平下降最大偏移距离 public int ponit_radius = default_point_radius;//小球半径 public int ball_radius = default_ball_radius;//小球半径 private paint mpaint; private path mpath; private int mlinecolor; private int mponitcolor; private int mballcolor; private int mlinewidth; private int mlineheight; private float mdowndistance; private float mupdistance; private float freeballdistance; private valueanimator mdowncontroller;//下落控制器 private valueanimator mupcontroller;//上弹控制器 private valueanimator mfreedowncontroller;//*落体控制器 private animatorset animatorset; private int state; private boolean ismupcontrollerdied = false; private boolean isanimationshowing = false; private boolean isbounced = false; private boolean isballfreeup = false; public dancingview(context context) { super(context); init(context, null); } public dancingview(context context, attributeset attrs) { super(context, attrs); init(context, attrs); } public dancingview(context context, attributeset attrs, int defstyleattr) { super(context, attrs, defstyleattr); init(context, attrs); } private void init(context context, attributeset attrs) { initattributes(context, attrs); mpaint = new paint(); mpaint.setantialias(true); mpaint.setstrokewidth(mlineheight); mpaint.setstrokecap(paint.cap.round); mpath = new path(); getholder().addcallback(this); initcontroller(); } private void initattributes(context context, attributeset attrs) { typedarray typearray = context.obtainstyledattributes(attrs, r.styleable.dancingview); mlinecolor = typearray.getcolor(r.styleable.dancingview_linecolor, default_line_color); mlinewidth = typearray.getdimensionpixeloffset(r.styleable.dancingview_linewidth, default_line_width); mlineheight = typearray.getdimensionpixeloffset(r.styleable.dancingview_lineheight, default_line_height); mponitcolor = typearray.getcolor(r.styleable.dancingview_pointcolor, default_point_color); mballcolor = typearray.getcolor(r.styleable.dancingview_ballcolor, default_ball_color); typearray.recycle(); } private void initcontroller() { mdowncontroller = valueanimator.offloat(0, 1); mdowncontroller.setduration(default_down_duration); mdowncontroller.setinterpolator(new decelerateinterpolator()); mdowncontroller.addupdatelistener(new valueanimator.animatorupdatelistener() { @override public void onanimationupdate(valueanimator animation) { mdowndistance = max_offset_y * (float) animation.getanimatedvalue(); postinvalidate(); } }); mdowncontroller.addlistener(new animator.animatorlistener() { @override public void onanimationstart(animator animation) { state = state_down; } @override public void onanimationend(animator animation) { } @override public void onanimationcancel(animator animation) { } @override public void onanimationrepeat(animator animation) { } }); mupcontroller = valueanimator.offloat(0, 1); mupcontroller.setduration(default_up_duration); mupcontroller.setinterpolator(new dancinginterpolator()); mupcontroller.addupdatelistener(new valueanimator.animatorupdatelistener() { @override public void onanimationupdate(valueanimator animation) { mupdistance = max_offset_y * (float) animation.getanimatedvalue(); if (mupdistance >= max_offset_y) { //进入*落体状态 isbounced = true; if (!mfreedowncontroller.isrunning() && !mfreedowncontroller.isstarted() && !isballfreeup) { mfreedowncontroller.start(); } } postinvalidate(); } }); mupcontroller.addlistener(new animator.animatorlistener() { @override public void onanimationstart(animator animation) { state = state_up; } @override public void onanimationend(animator animation) { ismupcontrollerdied = true; } @override public void onanimationcancel(animator animation) { } @override public void onanimationrepeat(animator animation) { } }); mfreedowncontroller = valueanimator.offloat(0, 8f); mfreedowncontroller.setduration(default_freedown_duration); mfreedowncontroller.setinterpolator(new decelerateinterpolator()); mfreedowncontroller.addupdatelistener(new valueanimator.animatorupdatelistener() { @override public void onanimationupdate(valueanimator animation) { //该公式解决上升减速 和 下降加速 float t = (float) animation.getanimatedvalue(); freeballdistance = 40 * t - 5 * t * t; if (ismupcontrollerdied) {//往上抛,到临界点 postinvalidate(); } } }); mfreedowncontroller.addlistener(new animator.animatorlistener() { @override public void onanimationstart(animator animation) { isballfreeup = true; } @override public void onanimationend(animator animation) { isanimationshowing = false; //循环第二次 startanimations(); } @override public void onanimationcancel(animator animation) { } @override public void onanimationrepeat(animator animation) { } }); animatorset = new animatorset(); animatorset.play(mdowncontroller).before(mupcontroller); animatorset.addlistener(new animator.animatorlistener() { @override public void onanimationstart(animator animation) { isanimationshowing = true; } @override public void onanimationend(animator animation) { } @override public void onanimationcancel(animator animation) { } @override public void onanimationrepeat(animator animation) { } }); } /** * 启动动画,外部调用 */ public void startanimations() { if (isanimationshowing) { return; } if (animatorset.isrunning()) { animatorset.end(); animatorset.cancel(); } isbounced = false; isballfreeup = false; ismupcontrollerdied = false; animatorset.start(); } @override protected void ondraw(canvas canvas) { super.ondraw(canvas); // 一条绳子用左右两部分的二阶贝塞尔曲线组成 mpaint.setcolor(mlinecolor); mpath.reset(); //起始点 mpath.moveto(getwidth() / 2 - mlinewidth / 2, getheight() / 2); if (state == state_down) {//下落 /**************绘制绳子开始*************/ //左部分 的贝塞尔 mpath.quadto((float) (getwidth() / 2 - mlinewidth / 2 + mlinewidth * 0.375), getheight() / 2 + mdowndistance, getwidth() / 2, getheight() / 2 + mdowndistance); //右部分 的贝塞尔 mpath.quadto((float) (getwidth() / 2 + mlinewidth / 2 - mlinewidth * 0.375), getheight() / 2 + mdowndistance, getwidth() / 2 + mlinewidth / 2, getheight() / 2); mpaint.setstyle(paint.style.stroke); canvas.drawpath(mpath, mpaint); /**************绘制绳子结束*************/ /**************绘制弹跳小球开始*************/ mpaint.setstyle(paint.style.fill); mpaint.setcolor(mballcolor); canvas.drawcircle(getwidth() / 2, getheight() / 2 + mdowndistance - ball_radius, ball_radius, mpaint); /**************绘制弹跳小球结束*************/ } else if (state == state_up) { //向上弹 /**************绘制绳子开始*************/ //左部分的贝塞尔 mpath.quadto((float) (getwidth() / 2 - mlinewidth / 2 + mlinewidth * 0.375), getheight() / 2 + 50 - mupdistance, getwidth() / 2, getheight() / 2 + (50 - mupdistance)); //右部分的贝塞尔 mpath.quadto((float) (getwidth() / 2 + mlinewidth / 2 - mlinewidth * 0.375), getheight() / 2 + 50 - mupdistance, getwidth() / 2 + mlinewidth / 2, getheight() / 2); mpaint.setstyle(paint.style.stroke); canvas.drawpath(mpath, mpaint); /**************绘制绳子结束*************/ mpaint.setstyle(paint.style.fill); mpaint.setcolor(mballcolor); //弹性小球,*落体 if (!isbounced) { //上升 canvas.drawcircle(getwidth() / 2, getheight() / 2 + (max_offset_y - mupdistance) - ball_radius, ball_radius, mpaint); } else { //*落体 canvas.drawcircle(getwidth() / 2, getheight() / 2 - freeballdistance - ball_radius, ball_radius, mpaint); } } mpaint.setcolor(mponitcolor); mpaint.setstyle(paint.style.fill); canvas.drawcircle(getwidth() / 2 - mlinewidth / 2, getheight() / 2, ponit_radius, mpaint); canvas.drawcircle(getwidth() / 2 + mlinewidth / 2, getheight() / 2, ponit_radius, mpaint); } @override public void surfacecreated(surfaceholder holder) { canvas canvas = holder.lockcanvas();//锁定整个surfaceview对象,获取该surface上的canvas. draw(canvas); holder.unlockcanvasandpost(canvas);//释放画布,提交修改 } @override public void surfacechanged(surfaceholder holder, int format, int width, int height) { } @override public void surfacedestroyed(surfaceholder holder) { } }
2.dancinginterpolator.java
public class dancinginterpolator implements interpolator { @override public float getinterpolation(float input) { return (float) (1 - math.exp(-3 * input) * math.cos(10 * input)); } }
3.自定义属性 styles.xml
<declare-styleable name="dancingview"> <attr name="linewidth" format="dimension" /> <attr name="lineheight" format="dimension" /> <attr name="pointcolor" format="reference|color" /> <attr name="linecolor" format="reference|color" /> <attr name="ballcolor" format="reference|color" /> </declare-styleable>
注意:颜色、尺寸、参数可以自己测试,调整。
以上就是本文的全部内容,希望对大家的学习和工作能有所帮助哦。