Android中模仿抖音加載框之兩顆小球轉(zhuǎn)動(dòng)效果
安卓版抖音v2.5加載框:
效果圖如下所示:
本控件效果圖:
使用方法
源碼地址:Android仿抖音加載框之兩顆小球轉(zhuǎn)動(dòng)控件
1、xml引用:
<com.douyinloadingview.DYLoadingView android:id="@+id/dy3" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#000000" app:color1="#FF00EEEE" app:color2="#FFFF4040" app:....(其他可選屬性) />
2、java使用:
@BindView(R.id.dy1) DYLoadingView dy1; @OnClick(R.id.b1) void start() { dy1.setXXXXX; //設(shè)置屬性(可選) dy1.start(); //開始動(dòng)畫 } @OnClick(R.id.b2) void stop() { dy1.stop(); //停止動(dòng)畫 }
就醬。
可用屬性
名稱 | 對應(yīng)xml屬性 | 對應(yīng)java方法 | 默認(rèn)值 |
---|---|---|---|
球1半徑 | radius1 | setRadius() | 6dp |
球2半徑 | radius2 | setRadius() | 6dp |
兩球間隔 | gap | setRadius() | 0.8dp |
球1顏色 | color1 | setColors() | 0XFFFF4040 |
球2顏色 | color2 | setColors() | 0XFF00EEEE |
疊加色 | mixColor | setColors() | 0XFF000000 |
從右往左移動(dòng)時(shí)小球最大縮放倍數(shù) | rtlScale | setScales() | 0.7f |
從左往右移動(dòng)時(shí)小球最大縮放倍數(shù) | ltrScale | setScales() | 1.3f |
一次移動(dòng)動(dòng)畫時(shí)長 | duration | setDuration() | 350ms |
一次移動(dòng)動(dòng)畫后停頓時(shí)長 | pauseDuration | setDuration() | 80ms |
動(dòng)畫進(jìn)度在[0,scaleStartFraction]期間,小球大小逐漸縮放 | scaleStartFraction | setStartEndFraction() | 0.2f |
動(dòng)畫進(jìn)度在[scaleEndFraction,1]期間,小球大小逐漸恢復(fù) | scaleEndFraction | setStartEndFraction() | 0.8f |
(rtl = right to left, ltr = left to right)
部分屬性說明:
•color格式為32位ARGB
•scaleStartFraction范圍[0,0.5];scaleEndFraction范圍[0.5,1]
•假設(shè)ltrScale = 1.3,scaleStartFraction = 0.2,scaleEndFraction = 0.8;那么實(shí)際效果就是一顆小球從左邊開始向右移動(dòng)
期間,進(jìn)度在0%~20%時(shí)半徑逐漸從1倍放大到1.3倍,在20%~80%期間大小保持1.3倍,在80%~100%時(shí)半徑逐漸從1.3倍恢復(fù)至1倍
實(shí)現(xiàn)思路
要讓小球動(dòng),當(dāng)然要有一個(gè)動(dòng)畫,通過動(dòng)畫來獲得一個(gè)進(jìn)度百分比fraction,然后小球在動(dòng)畫過程中的坐標(biāo)、大小就可以通過這個(gè)值來計(jì)算:
private void initAnim() { fraction = 0.0f; stop(); anim = ValueAnimator.ofFloat(0.0f, 1.0f); anim.setDuration(duration); if (pauseDuration > 0) { anim.setStartDelay(pauseDuration); anim.setInterpolator(new AccelerateDecelerateInterpolator()); } else { anim.setRepeatCount(ValueAnimator.INFINITE); anim.setRepeatMode(ValueAnimator.RESTART); anim.setInterpolator(new LinearInterpolator()); } anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { fraction = animation.getAnimatedFraction(); invalidate(); } }); anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { isLtr = !isLtr; } @Override public void onAnimationRepeat(Animator animation) { isLtr = !isLtr; } @Override public void onAnimationCancel(Animator animation) { isAnimCanceled = true; } @Override public void onAnimationEnd(Animator animation) { if (!isAnimCanceled) { anim.start(); } } }); }
代碼中看到,如果小球一次移動(dòng)后不需要停頓(pauseDuration = 0),那么直接通過anim.setRepeatCount(ValueAnimator.INFINITE)讓動(dòng)畫無限循環(huán),否則的話就要通過anim.setStartDelay(pauseDuration)
來設(shè)置停頓時(shí)間,然后在監(jiān)聽的onAnimationEnd里重啟動(dòng)畫,以此實(shí)現(xiàn)每一次移動(dòng)后小球能停頓一定時(shí)間。在onAnimationUpdate里,我們記錄了當(dāng)前動(dòng)畫百分比fraction,然后通過invalidate()重繪,在之后的onDraw里將通過該值畫出小球。另外,每次動(dòng)畫開始時(shí)(或是重復(fù)時(shí)),會(huì)將isLtr取反,這個(gè)標(biāo)志位的作用是標(biāo)明當(dāng)前哪顆球在【從左往右】移動(dòng),因?yàn)閮深w球的顏色、初始半徑是不一樣的嘛,onDraw里畫小球時(shí)是需要這個(gè)標(biāo)志位協(xié)助的。
有了動(dòng)畫進(jìn)度fraction和標(biāo)志位isLtr后,就可以在onDraw里畫出小球了。
**首先要計(jì)算小球當(dāng)前的坐標(biāo)**。y坐標(biāo)永遠(yuǎn)是固定的,不談,x坐標(biāo)隨著`fraction`的變化而變化。兩顆球之間最遠(yuǎn)距離為球1半徑+球2半徑+兩球間隔,即`distance = gap + radius1 + radius2;`,這個(gè)值就是兩顆球的移動(dòng)范圍,由此可計(jì)算出,當(dāng)前【從左往右】移動(dòng)的小球的x坐標(biāo)和當(dāng)前【從右往左】移動(dòng)的小球x坐標(biāo)分別為:
float ltrX = getMeasuredWidth() / 2.0f - distance / 2.0f; ltrX = ltrX + (distance * fraction); float rtlX = getMeasuredWidth() / 2.0f + distance / 2.0f; rtlX = rtlX - (distance * fraction);
**接下來要計(jì)算小球當(dāng)前的大小**。小球的大小也是隨著動(dòng)畫進(jìn)度變化的,上面已經(jīng)說明了`scaleStartFraction`和`scaleEndFraction`屬性的含義。 以當(dāng)前小球【從左往右】移動(dòng)為例,當(dāng)動(dòng)畫進(jìn)度為0時(shí),小球大小為初始1倍大??;當(dāng)動(dòng)畫進(jìn)度到scaleStartFraction時(shí),小球大小將縮放到`ltrScale`倍,當(dāng)動(dòng)畫進(jìn)度為[scaleStartFraction,scaleEndFraction]范圍時(shí),小球大小保持`ltrScale`倍,當(dāng)動(dòng)畫進(jìn)度到[scaleEndFraction,1]范圍時(shí),小球則從`ltrScale`倍逐漸恢復(fù)至1倍。 為了便于計(jì)算,首先將`[0,scaleStartFraction]`轉(zhuǎn)換為`[0,1.0]`的真實(shí)百分比,根據(jù)`y = kx + b`(就這么個(gè)入門公式。。我都要在紙上算一遍T-T),可以得出:`float scaleFraction = 1.0f / scaleStartFraction * fraction; `,有了這個(gè)真實(shí)百分比,那么該區(qū)間里小球當(dāng)前半徑就好計(jì)算了:
ltrBallRadius = ltrInitRadius * (1 + (ltrScale - 1) * scaleFraction); rtlBallRadius = rtlInitRadius * (1 + (rtlScale - 1) * scaleFraction);
應(yīng)該好懂的吧。另外[scaleEndFraction,1]區(qū)間里小球當(dāng)前半徑計(jì)算思路是一樣的,不談。
最后就是要畫出小球。這里主要是如何畫出實(shí)現(xiàn)兩球疊加的部分的顏色。
思路1:通過xfermode方式實(shí)現(xiàn):
從上圖可以看出用Darken、Lighten、Screen模式可以做到讓疊加處上色,但是顏色不能自定義。
思路2:通過path的OP操作實(shí)現(xiàn)(API19):
(圖片摘自http://www.gcssloop.com/customview/Path_Over)
因此,可以先構(gòu)造兩個(gè)小球的Path,然后再將這兩個(gè)Path進(jìn)行INTERSECT操作,即可獲得疊加處的Path,這樣就可以做到自定義疊加處的顏色了。
onDraw完整代碼:
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); float centerY = getMeasuredHeight() / 2.0f; float ltrInitRadius, rtlInitRadius; Paint ltrPaint, rtlPaint; //確定當(dāng)前【從左往右】移動(dòng)的是哪顆小球 if (isLtr) { ltrInitRadius = radius1; rtlInitRadius = radius2; ltrPaint = paint1; rtlPaint = paint2; } else { ltrInitRadius = radius2; rtlInitRadius = radius1; ltrPaint = paint2; rtlPaint = paint1; } float ltrX = getMeasuredWidth() / 2.0f - distance / 2.0f; ltrX = ltrX + (distance * fraction);//當(dāng)前從左往右的球的X坐標(biāo) float rtlX = getMeasuredWidth() / 2.0f + distance / 2.0f; rtlX = rtlX - (distance * fraction);//當(dāng)前從右往左的球的X坐標(biāo) //計(jì)算小球移動(dòng)過程中的大小變化 float ltrBallRadius, rtlBallRadius; if (fraction <= scaleStartFraction) { //動(dòng)畫進(jìn)度[0,scaleStartFraction]時(shí),球大小由1倍逐漸縮放至ltrScale/rtlScale倍 float scaleFraction = 1.0f / scaleStartFraction * fraction; //百分比轉(zhuǎn)換 [0,scaleStartFraction]] -> [0,1] ltrBallRadius = ltrInitRadius * (1 + (ltrScale - 1) * scaleFraction); rtlBallRadius = rtlInitRadius * (1 + (rtlScale - 1) * scaleFraction); } else if (fraction >= scaleEndFraction) { //動(dòng)畫進(jìn)度[scaleEndFraction,1],球大小由ltrScale/rtlScale倍逐漸恢復(fù)至1倍 float scaleFraction = (fraction - 1) / (scaleEndFraction - 1); //百分比轉(zhuǎn)換,[scaleEndFraction,1] -> [1,0] ltrBallRadius = ltrInitRadius * (1 + (ltrScale - 1) * scaleFraction); rtlBallRadius = rtlInitRadius * (1 + (rtlScale - 1) * scaleFraction); } else { //動(dòng)畫進(jìn)度[scaleStartFraction,scaleEndFraction],球保持縮放后的大小 ltrBallRadius = ltrInitRadius * ltrScale; rtlBallRadius = rtlInitRadius * rtlScale; } ltrPath.reset(); ltrPath.addCircle(ltrX, centerY, ltrBallRadius, Path.Direction.CW); rtlPath.reset(); rtlPath.addCircle(rtlX, centerY, rtlBallRadius, Path.Direction.CW); mixPath.op(ltrPath, rtlPath, Path.Op.INTERSECT); canvas.drawPath(ltrPath, ltrPaint); canvas.drawPath(rtlPath, rtlPaint); canvas.drawPath(mixPath, mixPaint); }
總結(jié)
以上所述是小編給大家介紹的Android中模仿抖音加載框之兩顆小球轉(zhuǎn)動(dòng)控件效果,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關(guān)文章
Android中Listview下拉刷新和上拉加載更多的多種實(shí)現(xiàn)方案
本文大概通過三種方案給大家介紹了Android中Listview下拉刷新和上拉加載更多知識,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下2016-12-12分享Android 藍(lán)牙4.0(ble)開發(fā)的解決方案
這篇文章主要為大家分享了Android 藍(lán)牙4.0(ble)開發(fā)的解決方案,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-03-03Android Room數(shù)據(jù)庫容易遇到的問題以及解決方法
這篇文章給大家介紹了我們在Android Room數(shù)據(jù)庫容易遇到的坑以及解決方法,文中有詳細(xì)的代碼示例供我們參考,具有一定的參考價(jià)值,需要的朋友可以參考下2023-09-09Android實(shí)現(xiàn)ListView分頁自動(dòng)加載數(shù)據(jù)的方法
這篇文章主要介紹了Android實(shí)現(xiàn)ListView分頁自動(dòng)加載數(shù)據(jù)的方法,涉及Android生成listview列表的相關(guān)技巧,需要的朋友可以參考下2015-12-12Android手機(jī)開發(fā)設(shè)計(jì)之記事本功能
這篇文章主要為大家詳細(xì)介紹了Android手機(jī)開發(fā)設(shè)計(jì)之記事本功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05Android 使用Vitamio打造自己的萬能播放器(1)——準(zhǔn)備
本文主要介紹Android Vitamio,在Android開發(fā)視頻播放器的時(shí)候,大家經(jīng)常會(huì)遇到系統(tǒng)版本和不同的Android手機(jī)不同導(dǎo)致開發(fā)的軟件不能完美適用,這里給大家介紹個(gè)播放器插件可以適應(yīng)所有Android設(shè)備2016-07-07