超好看的下拉刷新动画Android代码实现
程序员文章站
2024-02-21 12:25:10
最近看到了好多高端、大气、上档次的动画效果,如果给你的项目中加上这些动画,相信你的app一定很优秀,今天给大家分析一下来自yalantis的一个超好看的下拉刷新动画。
首...
最近看到了好多高端、大气、上档次的动画效果,如果给你的项目中加上这些动画,相信你的app一定很优秀,今天给大家分析一下来自yalantis的一个超好看的下拉刷新动画。
首先我们看一下效果如何:
怎么样?是不是很高大上?接下来我们看一下代码:
一、首先我们需要自定义刷新的动态refreshview(也就是下拉时候的头)
1.初始化头所占用的dimens
private void initiatedimens() { mscreenwidth = mcontext.getresources().getdisplaymetrics().widthpixels; mjettopoffset = mparent.gettotaldragdistance() * 0.5f; mtop = -mparent.gettotaldragdistance(); }
2.为头填充图片,设置图片的大小
分别为左边的云彩,右边的云彩,中间的云彩还有中间的飞机,飞机是带有动画的,下面会介绍飞机的动画
private void createbitmaps() { mleftclouds = bitmapfactory.decoderesource(mcontext.getresources(), r.drawable.clouds_left); mrightclouds = bitmapfactory.decoderesource(mcontext.getresources(), r.drawable.clouds_right); mfrontclouds = bitmapfactory.decoderesource(mcontext.getresources(), r.drawable.clouds_center); mjet = bitmapfactory.decoderesource(mcontext.getresources(), r.drawable.airplane); mjetwidthcenter = mjet.getwidth() / 2; mjetheightcenter = mjet.getheight() / 2; mfrontcloudwidthcenter = mfrontclouds.getwidth() / 2; mfrontcloudheightcenter = mfrontclouds.getheight() / 2; mrightcloudswidthcenter = mrightclouds.getwidth() / 2; mrightcloudsheightcenter = mrightclouds.getheight() / 2; mleftcloudswidthcenter = mleftclouds.getwidth() / 2; mleftcloudsheightcenter = mleftclouds.getheight() / 2; }
3.然后我们来画这个头
public void draw(@nonnull canvas canvas) { final int savecount = canvas.save(); // draw background. canvas.drawcolor(mcontext.getresources().getcolor(r.color.sky_background)); if (isrefreshing) { // set up new set of wind while (mwinds.size() < wind_set_amount) { float y = (float) (mparent.gettotaldragdistance() / (math.random() * random_y_coefficient)); float x = random(min_wind_x_offset, max_wind_x_offset); // magic with checking interval between winds if (mwinds.size() > 1) { y = 0; while (y == 0) { float tmp = (float) (mparent.gettotaldragdistance() / (math.random() * random_y_coefficient)); for (map.entry<float, float> wind : mwinds.entryset()) { // we want that interval will be greater than fifth part of draggable distance if (math.abs(wind.getkey() - tmp) > mparent.gettotaldragdistance() / random_y_coefficient) { y = tmp; } else { y = 0; break; } } } } mwinds.put(y, x); drawwind(canvas, y, x); } // draw current set of wind if (mwinds.size() >= wind_set_amount) { for (map.entry<float, float> wind : mwinds.entryset()) { drawwind(canvas, wind.getkey(), wind.getvalue()); } } // we should to create new set of winds if (minversedirection && mnewwindset) { mwinds.clear(); mnewwindset = false; mwindlinewidth = random(min_wind_line_width, max_wind_line_width); } // needed for checking direction mlastanimationtime = mloadinganimationtime; } drawjet(canvas); drawsideclouds(canvas); drawcenterclouds(canvas); canvas.restoretocount(savecount); }
/** * draw wind on loading animation * * @param canvas - area where we will draw * @param y - y position fot one of lines * @param xoffset - x offset for on of lines */ private void drawwind(canvas canvas, float y, float xoffset) { /* we should multiply current animation time with this coefficient for taking all screen width in time removing slowing of animation with dividing on {@link #slow_down_animation_coefficient} and we should don't forget about distance that should "fly" line that depend on screen of device and x offset */ float cof = (mscreenwidth + xoffset) / (loading_animation_coefficient / slow_down_animation_coefficient); float time = mloadinganimationtime; // horrible hack for revers animation that should work like restart animation if (mlastanimationtime - mloadinganimationtime > 0) { minversedirection = true; // take time from 0 to end of animation time time = (loading_animation_coefficient / slow_down_animation_coefficient) - mloadinganimationtime; } else { mnewwindset = true; minversedirection = false; } // taking current x position of drawing wind // for fully disappearing of line we should subtract wind line width float x = (mscreenwidth - (time * cof)) + xoffset - mwindlinewidth; float xend = x + mwindlinewidth; canvas.drawline(x, y, xend, y, mwindpaint); } private void drawsideclouds(canvas canvas) { matrix matrixleftclouds = mmatrix; matrix matrixrightclouds = madditionalmatrix; matrixleftclouds.reset(); matrixrightclouds.reset(); // drag percent will newer get more then 1 here float dragpercent = math.min(1f, math.abs(mpercent)); boolean overdrag = false; // but we check here for overdrag if (mpercent > 1.0f) { overdrag = true; } float scale; float scalepercentdelta = dragpercent - scale_start_percent; if (scalepercentdelta > 0) { float scalepercent = scalepercentdelta / (1.0f - scale_start_percent); scale = side_clouds_initial_scale + (side_clouds_final_scale - side_clouds_initial_scale) * scalepercent; } else { scale = side_clouds_initial_scale; } // current y position of clouds float dragyoffset = mparent.gettotaldragdistance() * (1.0f - dragpercent); // position where clouds fully visible on screen and we should drag them with content of listview int cloudsvisibleposition = mparent.gettotaldragdistance() / 2 - mleftcloudsheightcenter; boolean needmovecloudswithcontent = false; if (dragyoffset < cloudsvisibleposition) { needmovecloudswithcontent = true; } float offsetrightx = mscreenwidth - mrightclouds.getwidth(); float offsetrighty = (needmovecloudswithcontent ? mparent.gettotaldragdistance() * dragpercent - mleftclouds.getheight() : dragyoffset) + (overdrag ? mtop : 0); float offsetleftx = 0; float offsetlefty = (needmovecloudswithcontent ? mparent.gettotaldragdistance() * dragpercent - mleftclouds.getheight() : dragyoffset) + (overdrag ? mtop : 0); // magic with animation on loading process if (isrefreshing) { if (checkcurrentanimationpart(animationpart.first)) { offsetlefty += getanimationpartvalue(animationpart.first) / y_side_clouds_slow_down_cof; offsetrightx -= getanimationpartvalue(animationpart.first) / x_side_clouds_slow_down_cof; } else if (checkcurrentanimationpart(animationpart.second)) { offsetlefty += getanimationpartvalue(animationpart.second) / y_side_clouds_slow_down_cof; offsetrightx -= getanimationpartvalue(animationpart.second) / x_side_clouds_slow_down_cof; } else if (checkcurrentanimationpart(animationpart.third)) { offsetlefty -= getanimationpartvalue(animationpart.third) / y_side_clouds_slow_down_cof; offsetrightx += getanimationpartvalue(animationpart.third) / x_side_clouds_slow_down_cof; } else if (checkcurrentanimationpart(animationpart.fourth)) { offsetlefty -= getanimationpartvalue(animationpart.fourth) / x_side_clouds_slow_down_cof; offsetrightx += getanimationpartvalue(animationpart.fourth) / y_side_clouds_slow_down_cof; } } matrixrightclouds.postscale(scale, scale, mrightcloudswidthcenter, mrightcloudsheightcenter); matrixrightclouds.posttranslate(offsetrightx, offsetrighty); matrixleftclouds.postscale(scale, scale, mleftcloudswidthcenter, mleftcloudsheightcenter); matrixleftclouds.posttranslate(offsetleftx, offsetlefty); canvas.drawbitmap(mleftclouds, matrixleftclouds, null); canvas.drawbitmap(mrightclouds, matrixrightclouds, null); } private void drawcenterclouds(canvas canvas) { matrix matrix = mmatrix; matrix.reset(); float dragpercent = math.min(1f, math.abs(mpercent)); float scale; float overdragpercent = 0; boolean overdrag = false; if (mpercent > 1.0f) { overdrag = true; // here we want know about how mach percent of over drag we done overdragpercent = math.abs(1.0f - mpercent); } float scalepercentdelta = dragpercent - scale_start_percent; if (scalepercentdelta > 0) { float scalepercent = scalepercentdelta / (1.0f - scale_start_percent); scale = center_clouds_initial_scale + (center_clouds_final_scale - center_clouds_initial_scale) * scalepercent; } else { scale = center_clouds_initial_scale; } float parallaxpercent = 0; boolean parallax = false; // current y position of clouds float dragyoffset = mparent.gettotaldragdistance() * dragpercent; // position when should start parallax scrolling int startparallaxheight = mparent.gettotaldragdistance() - mfrontcloudheightcenter; if (dragyoffset > startparallaxheight) { parallax = true; parallaxpercent = dragyoffset - startparallaxheight; } float offsetx = (mscreenwidth / 2) - mfrontcloudwidthcenter; float offsety = dragyoffset - (parallax ? mfrontcloudheightcenter + parallaxpercent : mfrontcloudheightcenter) + (overdrag ? mtop : 0); float sx = overdrag ? scale + overdragpercent / 4 : scale; float sy = overdrag ? scale + overdragpercent / 2 : scale; if (isrefreshing && !overdrag) { if (checkcurrentanimationpart(animationpart.first)) { sx = scale - (getanimationpartvalue(animationpart.first) / loading_animation_coefficient) / 8; } else if (checkcurrentanimationpart(animationpart.second)) { sx = scale - (getanimationpartvalue(animationpart.second) / loading_animation_coefficient) / 8; } else if (checkcurrentanimationpart(animationpart.third)) { sx = scale + (getanimationpartvalue(animationpart.third) / loading_animation_coefficient) / 6; } else if (checkcurrentanimationpart(animationpart.fourth)) { sx = scale + (getanimationpartvalue(animationpart.fourth) / loading_animation_coefficient) / 6; } sy = sx; } matrix.postscale(sx, sy, mfrontcloudwidthcenter, mfrontcloudheightcenter); matrix.posttranslate(offsetx, offsety); canvas.drawbitmap(mfrontclouds, matrix, null); } private void drawjet(canvas canvas) { matrix matrix = mmatrix; matrix.reset(); float dragpercent = mpercent; float rotateangle = 0; // check overdrag if (dragpercent > 1.0f && !mendofrefreshing) { rotateangle = (dragpercent % 1) * 10; dragpercent = 1.0f; } float offsetx = ((mscreenwidth * dragpercent) / 2) - mjetwidthcenter; float offsety = mjettopoffset + (mparent.gettotaldragdistance() / 2) * (1.0f - dragpercent) - mjetheightcenter; if (isrefreshing) { if (checkcurrentanimationpart(animationpart.first)) { offsety -= getanimationpartvalue(animationpart.first); } else if (checkcurrentanimationpart(animationpart.second)) { offsety -= getanimationpartvalue(animationpart.second); } else if (checkcurrentanimationpart(animationpart.third)) { offsety += getanimationpartvalue(animationpart.third); } else if (checkcurrentanimationpart(animationpart.fourth)) { offsety += getanimationpartvalue(animationpart.fourth); } } matrix.settranslate(offsetx, offsety); if (dragpercent == 1.0f) { matrix.prerotate(rotateangle, mjetwidthcenter, mjetheightcenter); } canvas.drawbitmap(mjet, matrix, null); }
动画效果已经画好了,下面我们来看看怎么结合下拉刷新来调用吧?
二、我们还需要自定义一个pulltorefreshview(下拉刷新)
1.我们的pulltorefreshview这里需要继承viewgroup
我们先把刚才定义的刷新时的动画加进来
private refreshview mrefreshview; <pre name="code" class="java">private imageview mrefreshimageview; <pre name="code" class="java">mrefreshimageview = new imageview(context); mrefreshview = new refreshview(getcontext(), this); mrefreshimageview.setimagedrawable(mrefreshview); addview(mrefreshimageview);
2.然后我们设置ontouchevent()事件
@override public boolean ontouchevent(@nonnull motionevent ev) { if (!misbeingdragged) { return super.ontouchevent(ev); } final int action = motioneventcompat.getactionmasked(ev); switch (action) { case motionevent.action_move: { final int pointerindex = motioneventcompat.findpointerindex(ev, mactivepointerid); if (pointerindex < 0) { return false; } final float y = motioneventcompat.gety(ev, pointerindex); final float ydiff = y - minitialmotiony; final float scrolltop = ydiff * drag_rate; mcurrentdragpercent = scrolltop / mtotaldragdistance; if (mcurrentdragpercent < 0) { return false; } float boundeddragpercent = math.min(1f, math.abs(mcurrentdragpercent)); float extraos = math.abs(scrolltop) - mtotaldragdistance; float slingshotdist = mtotaldragdistance; float tensionslingshotpercent = math.max(0, math.min(extraos, slingshotdist * 2) / slingshotdist); float tensionpercent = (float) ((tensionslingshotpercent / 4) - math.pow( (tensionslingshotpercent / 4), 2)) * 2f; float extramove = (slingshotdist) * tensionpercent / 2; int targety = (int) ((slingshotdist * boundeddragpercent) + extramove); mrefreshview.setpercent(mcurrentdragpercent); settargetoffsettop(targety - mcurrentoffsettop, true); break; } case motioneventcompat.action_pointer_down: final int index = motioneventcompat.getactionindex(ev); mactivepointerid = motioneventcompat.getpointerid(ev, index); break; case motioneventcompat.action_pointer_up: onsecondarypointerup(ev); break; case motionevent.action_up: case motionevent.action_cancel: { if (mactivepointerid == invalid_pointer) { return false; } final int pointerindex = motioneventcompat.findpointerindex(ev, mactivepointerid); final float y = motioneventcompat.gety(ev, pointerindex); final float overscrolltop = (y - minitialmotiony) * drag_rate; misbeingdragged = false; if (overscrolltop > mtotaldragdistance) { setrefreshing(true, true); } else { mrefreshing = false; animateoffsettoposition(manimatetostartposition); } mactivepointerid = invalid_pointer; return false; } } return true; }
三、最后我们看怎样在activity中使用这个下拉刷新控件
1.先看一下布局文件
这里是我们的下拉刷新空间嵌套着我们的listview,然后我们再给listview填充数据即可
<relativelayout 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" tools:context=".pulltorefreshactivity"> <com.hankkin.animationpulltorefreshdemo.pulltorefreshview android:id="@+id/pull_to_refresh" android:layout_width="match_parent" android:layout_height="match_parent"> <listview android:id="@+id/list_view" android:divider="@null" android:dividerheight="0dp" android:fadingedge="none" android:layout_width="match_parent" android:layout_height="match_parent" /> </com.hankkin.animationpulltorefreshdemo.pulltorefreshview> </relativelayout>
2.为listview填充数据
为了我们的效果比较好看,这里我们给listview的每一个item填充不同的颜色,看起来会比较高大上。
map<string, integer> map; list<map<string, integer>> samplelist = new arraylist<map<string, integer>>(); int[] colors = { r.color.saffron, r.color.eggplant, r.color.sienna}; int[] tripnames = { r.string.trip_to_india, r.string.trip_to_italy, r.string.trip_to_indonesia}; for (int i = 0; i < tripnames.length; i++) { map = new hashmap<string, integer>(); map.put(sampleadapter.key_name, tripnames[i]); map.put(sampleadapter.key_color, colors[i]); samplelist.add(map); } listview listview = (listview) findviewbyid(r.id.list_view); listview.setadapter(new sampleadapter(this, r.layout.list_item, samplelist));
3.最后,我们再设置一下下拉刷新的监听事件就ok了
mpulltorefreshview = (pulltorefreshview) findviewbyid(r.id.pull_to_refresh); mpulltorefreshview.setonrefreshlistener(new pulltorefreshview.onrefreshlistener() { @override public void onrefresh() { mpulltorefreshview.postdelayed(new runnable() { @override public void run() { mpulltorefreshview.setrefreshing(false); } }, refresh_delay); } });
怎么样?有没有很高大上啊?
大家可以动手实践一下,希望对大家的学习有所帮助。