Android如何自定義View實(shí)現(xiàn)橫向的雙水波紋進(jìn)度條
網(wǎng)上垂直的水波紋進(jìn)度條很多,但橫向的很少,將垂直的水波紋改為水平的還遇到了些麻煩,現(xiàn)在完善后發(fā)布出來(lái),希望遇到的人少躺點(diǎn)坑。
思路分析
整體效果可分為三個(gè),繪制圓角背景和圓角矩形,繪制第一條和第二條水波浪,根據(jù)自定義進(jìn)度變化效果。
功能實(shí)現(xiàn)
1.繪制圓角背景和圓角矩形邊框
圓角矩形邊框:
private RectF rectBorder; if (rectBorder == null) { rectBorder = new RectF(0.5f * dp1, 0.5f * dp1, waveActualSizeWidth - 0.5f * dp1, waveActualSizeHeight - 0.5f * dp1); } canvas.drawRoundRect(rectBorder, dp27, dp27, borderPaint);
我們創(chuàng)建一個(gè)新的畫布,然后在畫布里畫上圓角矩形背景和第一條和第二條水波浪:
//這里用到了緩存 根據(jù)參數(shù)創(chuàng)建新位圖 if (circleBitmap == null) { circleBitmap = Bitmap.createBitmap(waveActualSizeWidth, waveActualSizeHeight, Bitmap.Config.ARGB_8888); } //以該bitmap為底創(chuàng)建一塊畫布 if (bitmapCanvas == null) { bitmapCanvas = new Canvas(circleBitmap); } // 圓角矩形背景,為了能讓波浪填充完整個(gè)圓形背景 if (rectBg == null) { rectBg = new RectF(0, 0, waveActualSizeWidth, waveActualSizeHeight); } bitmapCanvas.drawRoundRect(rectBg, dp27, dp27, backgroundPaint); //裁剪圖片 canvas.drawBitmap(circleBitmap, 0, 0, null);
2.通過(guò)貝塞爾曲線實(shí)現(xiàn)雙水波
1)實(shí)現(xiàn)第一條水波
/** * 繪制波浪線 */ private Path canvasWavePath() { //要先清掉路線 wavePath.reset(); //起始點(diǎn)移至(0,0) p0 -p1 的高度隨著進(jìn)度的變化而變化 wavePath.moveTo((currentPercent) * waveActualSizeWidth, -moveDistance); //最多能繪制多少個(gè)波浪 //其實(shí)也可以用 i < getWidth() ;i+=waveLength來(lái)判斷 這個(gè)沒(méi)那么完美 //繪制p0 - p1 繪制波浪線 這里有一段是超出View的,在View右邊距的右邊 所以是* 2 for (int i = 0; i < waveNumber * 2; i++) { wavePath.rQuadTo(waveHeight, waveLength / 2, 0, waveLength); wavePath.rQuadTo(-waveHeight, waveLength / 2, 0, waveLength); } //連接p1 - p2 wavePath.lineTo(0, waveActualSizeHeight); //連接p2 - p0 wavePath.lineTo(0, 0); //封閉起來(lái)填充 wavePath.close(); return wavePath; }
moveDistance為水波垂直方向移動(dòng)的距離。
waveLength為水波長(zhǎng)度,一個(gè)上弧加一個(gè)下弧為一個(gè)波長(zhǎng)。
path的起始點(diǎn)為(0,0)可根據(jù)進(jìn)度動(dòng)態(tài)改變,然后循環(huán)畫曲線,長(zhǎng)度是有幾個(gè)波浪就是多長(zhǎng),然后連接到view高度的位置,最后到(0,0),形成一個(gè)封閉的區(qū)域,這樣就實(shí)現(xiàn)了一個(gè)填充的水波效果。
2)繪制第二條水波,第二條水波和第一條類似,只是起始點(diǎn)變了:
/** * 繪制第二層波浪 */ private Path canvasSecondPath() { secondWavePath.reset(); //初始點(diǎn)移動(dòng)到下方 secondWavePath.moveTo((currentPercent) * waveActualSizeWidth, waveActualSizeHeight + moveDistance); for (int i = 0; i < waveNumber * 2; i++) { secondWavePath.rQuadTo(waveHeight, -waveLength / 2, 0, -waveLength); secondWavePath.rQuadTo(-waveHeight, -waveLength / 2, 0, -waveLength); } secondWavePath.lineTo(0, 0); secondWavePath.lineTo(0, waveActualSizeHeight); secondWavePath.close(); return secondWavePath; }
3.設(shè)置動(dòng)畫使進(jìn)度和水波紋變化
/** * 設(shè)置進(jìn)度 * * @param currentProgress 進(jìn)度 * @param duration 達(dá)到進(jìn)度需要的時(shí)間 */ public void setProgress(int currentProgress, long duration, AnimatorListenerAdapter listenerAdapter) { float percent = currentProgress * 1f / maxProgress; this.currentProgress = currentProgress; //從0開始變化 currentPercent = 0; moveDistance = 0; mProgressAnimator = ValueAnimator.ofFloat(0, percent); //設(shè)置動(dòng)畫時(shí)間 mProgressAnimator.setDuration(duration); //讓動(dòng)畫勻速播放,避免出現(xiàn)波浪平移停頓的現(xiàn)象 mProgressAnimator.setInterpolator(new LinearInterpolator()); mProgressAnimator.addUpdateListener(listener); mProgressAnimator.addListener(listenerAdapter); mProgressAnimator.start(); // 波浪線 startWaveAnimal(); } /** * 波浪動(dòng)畫 */ private void startWaveAnimal() { //動(dòng)畫實(shí)例化 if (waveProgressAnimator == null) { waveProgressAnimator = new WaveProgressAnimal(); //設(shè)置動(dòng)畫時(shí)間 waveProgressAnimator.setDuration(2000); //設(shè)置循環(huán)播放 waveProgressAnimator.setRepeatCount(Animation.INFINITE); //讓動(dòng)畫勻速播放,避免出現(xiàn)波浪平移停頓的現(xiàn)象 waveProgressAnimator.setInterpolator(new LinearInterpolator()); //當(dāng)前視圖開啟動(dòng)畫 this.startAnimation(waveProgressAnimator); } }
其中波浪動(dòng)畫是通過(guò)改變moveDistance的值改變縱坐標(biāo)達(dá)到,進(jìn)度主要是通過(guò)改變百分比currentPercent改變波浪的橫坐標(biāo)達(dá)到。
結(jié)語(yǔ)
通過(guò)這個(gè)項(xiàng)目,主要可以學(xué)到貝塞爾曲線,也可以搭配上不同的動(dòng)畫,搞定產(chǎn)品的各種交互效果。對(duì)應(yīng)的文件:HorizontalWaveProgressView.java
到此這篇關(guān)于Android如何自定義View實(shí)現(xiàn)橫向的雙水波紋進(jìn)度條的文章就介紹到這了,更多相關(guān)Android自定義進(jìn)度條內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Android實(shí)現(xiàn)水波紋效果實(shí)例代碼
- Android實(shí)現(xiàn)漸變色水波紋效果
- Android 通過(guò)自定義view實(shí)現(xiàn)水波紋效果案例詳解
- Android 自定義球型水波紋帶圓弧進(jìn)度效果(實(shí)例代碼)
- Android實(shí)現(xiàn)水波紋擴(kuò)散效果
- Android實(shí)現(xiàn)水波紋特效
- android實(shí)現(xiàn)簡(jiǎn)單底部導(dǎo)航欄
- Android實(shí)現(xiàn)底部導(dǎo)航欄效果
- Android自定義水波紋底部導(dǎo)航的實(shí)現(xiàn)
相關(guān)文章
Android 實(shí)現(xiàn)仿網(wǎng)絡(luò)直播彈幕功能詳解及實(shí)例
這篇文章主要介紹了Android 實(shí)現(xiàn)仿網(wǎng)絡(luò)直播彈幕功能詳解的相關(guān)資料,并附實(shí)例代碼及實(shí)現(xiàn)效果圖,需要的朋友可以參考下2016-11-11Flutter實(shí)現(xiàn)單選,復(fù)選和開關(guān)組件的示例代碼
在App開發(fā)過(guò)程中,選擇交互是非常常見的,今天主要介紹下關(guān)于選擇的三個(gè)組件的使用:開關(guān)、單選和復(fù)選,感興趣的小伙伴可以了解一下2022-04-04Android onLoadFinished與onLoaderReset回調(diào)詳解及實(shí)例
這篇文章主要介紹了Android onLoadFinished與onLoaderReset回調(diào)詳解及實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-03-03Android Flutter實(shí)現(xiàn)興趣標(biāo)簽選擇功能
我們?cè)谑状问褂脙?nèi)容類 App 的時(shí)候,不少都會(huì)讓我們選擇個(gè)人偏好,通過(guò)這些標(biāo)簽選擇可以預(yù)先知道用戶的偏好信息。我們本篇就來(lái)看看 Flutter 如何實(shí)現(xiàn)興趣標(biāo)簽的選擇,需要的可以參考一下2022-11-11Android底部導(dǎo)航欄的三種風(fēng)格實(shí)現(xiàn)
這篇文章主要介紹了Android底部導(dǎo)航欄的三種風(fēng)格實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06實(shí)例詳解Android Selector和Shape的用法
shape和selector是Android UI設(shè)計(jì)中經(jīng)常用到的,比如我們要自定義一個(gè)圓角Button,點(diǎn)擊Button有些效果的變化,就要用到shape和selector,通過(guò)本文結(jié)合代碼實(shí)例給大家詳解Android Selector和Shape的用法,感興趣的朋友一起學(xué)習(xí)吧2016-01-01Android開發(fā)之時(shí)間日期操作實(shí)例
這篇文章主要介紹了Android開發(fā)之時(shí)間日期操作,是Android程序開發(fā)中常見的一個(gè)功能,需要的朋友可以參考下2014-08-08Android編程實(shí)現(xiàn)調(diào)用相冊(cè)、相機(jī)及拍照后直接裁剪的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)調(diào)用相冊(cè)、相機(jī)及拍照后直接裁剪的方法,涉及Android拍照及圖形處理相關(guān)操作技巧,需要的朋友可以參考下2017-02-02