仿网易云音乐均衡器调节UI效果
介紹
轉(zhuǎn)載請(qǐng)說明出處!網(wǎng)易云音樂大家都很熟悉,今天主要是來模仿以下他的音效均衡器的調(diào)節(jié)UI 效果如圖:
? ? ? ?? ? ? ? ? ?
若想實(shí)現(xiàn)這個(gè)效果我們需要做什么呢?我們現(xiàn)分析一下:
1. 首先定好我們需要畫圖的固定點(diǎn)的坐標(biāo)(圖中藍(lán)色圈)。
2. 過固定點(diǎn)用’平滑曲線‘把這些坐標(biāo)連接起來?,必須是平滑的哦。
3. 看到效果我們首先想到的就是貝塞爾曲線。
4. 首先我們先把固定點(diǎn)鏈接起來,不讓他動(dòng),然后再去實(shí)現(xiàn)ontouch()事件,修改坐標(biāo)點(diǎn)。
我們先繪制過定點(diǎn)的曲線
讓指定的坐標(biāo)點(diǎn)使用平滑的曲線鏈接起來,這個(gè)問題之前看過很多文章,去年都已經(jīng)做了這樣的需求,只是一直沒時(shí)間記錄,過了這么就也忘了差不多,及時(shí)把它記下來,如圖:
由圖藍(lán)色點(diǎn)是給的坐標(biāo)點(diǎn),然后我們根據(jù)給的坐標(biāo)點(diǎn)把,她們平滑的鏈接起來。這里就用到了貝塞爾曲線,如果對(duì)自定義view還不是很熟悉的同事,可以去看我的其他文章關(guān)于自定義view的專欄。
具體什么是貝塞爾曲線,這里就不說了,推薦大家去看一個(gè)文章寫的很好:自帶美感的貝塞爾曲線原理與實(shí)戰(zhàn)——Android高級(jí)UI
之前我也參考了很多文章,都講了很多的數(shù)學(xué)公式。
其實(shí)我們只要記住1點(diǎn)就行:
任意相鄰的三個(gè)點(diǎn)之間第一個(gè)點(diǎn)和第三個(gè)點(diǎn)的連線和第二個(gè)點(diǎn)的切線的斜率是相等的
如圖我們?nèi)稳∑渲幸欢吻€:
注釋:藍(lán)色是給的定點(diǎn),綠色是我們求得的貝塞爾曲線控制點(diǎn),具體怎么求接下來我們會(huì)說,先理解我們的概念
求解控制點(diǎn)
1. 我們知道藍(lán)色的點(diǎn)是我們已知的坐標(biāo)點(diǎn)(x,y),初中學(xué)過 y = kx + b; 既然我們定點(diǎn)坐標(biāo)已知,那我們把這個(gè)定點(diǎn)的斜率求出來,然后再根據(jù) (已知斜率 過定點(diǎn) 求一條 直線)這個(gè)應(yīng)該比較容易懂。
2. 通過1 我們能把直線求出來,具體取直線上那一點(diǎn)我們根據(jù)曲線的彎曲程度,我們可以定一個(gè)比率rate,比如我把比率調(diào)的大一點(diǎn)如圖:
?
明顯我們能夠看出綠色的坐標(biāo)點(diǎn)變長(zhǎng),曲線變得更加彎曲。
所以我們開始計(jì)算我們的控制點(diǎn)坐標(biāo):
private List<PointF> getControlPoints(PointF[] points){// 計(jì)算斜率 y = kx + b => k = (y - b)/xif (points.length < 3){return null;}List<PointF> pointFList = new ArrayList<>();float rate = 0.7f;//從第一個(gè)控制點(diǎn)開始計(jì)算float cx1 = points[0].x + (points[1].x - points[0].x)*rate;float cy1 = points[0].y;pointFList.add(new PointF(cx1,cy1));for (int i =1;i<points.length-1;i++){//第二個(gè)點(diǎn)float k = (points[i+1].y - points[i-1].y)/(points[i+1].x - points[i-1].x);float b = points[i].y - k*points[i].x;//左邊控制點(diǎn)float cxLeft = points[i].x - (points[i].x - points[i-1].x)*rate;float cyLeft = k*cxLeft + b;pointFList.add(new PointF(cxLeft,cyLeft));//右邊控制點(diǎn)float cxRight = points[i].x + (points[i+1].x - points[i].x)*rate;float cyRight = k*cxRight + b;pointFList.add(new PointF(cxRight,cyRight));}//最后一個(gè)點(diǎn)float cxLast = points[points.length - 1].x - (points[points.length - 1].x - points[points.length - 2].x)*rate;float cyLast = points[points.length - 1].y;pointFList.add(new PointF(cxLast,cyLast));return pointFList;}得到控制點(diǎn)的坐標(biāo)然后繪制
這里用到了 path 路徑不理解的可以去看看我以前的文章?Android自定義view --Path 的高級(jí)用法之-搜索按鈕動(dòng)畫
繪制:
for (int i=0;i<mPoints.length-1;i++){mPath.moveTo(mPoints[i].x,mPoints[i].y);mPath.cubicTo(fList.get(i*2).x,fList.get(i*2).y,fList.get(i*2+1).x,fList.get(i*2+1).y,mPoints[i+1].x,mPoints[i+1].y);}到這里平滑曲線我們已經(jīng)繪制好了,接下來就是ontouch()事件處理拉
onTouch()事件處理
要處理onTouch()事件首先了解的是:
1. View事件分發(fā)
2. View 攔截
3. 手指?Action_down 的時(shí)候 事件檢測(cè)
4. View 事件滑動(dòng)沖突(這里不存在這個(gè)問題)
5. View 消費(fèi)事件
?不熟悉的同學(xué)可以去搜索相關(guān)博文,這里就不作詳細(xì)的講解
事件分發(fā)
事件分發(fā)我們首先要想到的方法是 dispatchTouchEvent(MotionEvent event) 在 action_down 的時(shí)候 判斷我們的事件該由那個(gè)View來消費(fèi)
@Overridepublic boolean dispatchTouchEvent(MotionEvent event) {int a = event.getAction();switch (a) {case MotionEvent.ACTION_DOWN:x = event.getX();y = event.getY();// 檢測(cè)是否需要攔截事件if (!checkClickPosition(x,y)){return false;}}return super.dispatchTouchEvent(event);}記住一句話:任何事件都是從 ACTION_DOWN 開始的,所以不管手指 滑動(dòng)到何處 ,事件的作用對(duì)象 始終是 action_down 處?的 View(如果被當(dāng)前View攔截的話)
如何檢測(cè)(3)
檢測(cè)其實(shí)就是檢測(cè)我們的 ACTION_DOWN 位置 是否在我們事件處理的范圍,比如圖
我們手指點(diǎn)的位置是否在我們的圓環(huán)位置內(nèi)即:手指ACTION_DOWN的坐標(biāo)(x,y)是否在圓環(huán)當(dāng)前左上角和右下角的(x,y)坐標(biāo)內(nèi),如何計(jì)算:
private boolean checkClickPosition(float x,float y){for (int i =0;i<mPoints.length;i++){if ((x < mPoints[i].x +20 && x >mPoints[i].x-20) && (y < mPoints[i].y +20 && y > mPoints[i].y-20)){index = i;return true;}}return false;}這段代碼是我們這個(gè)例子的坐標(biāo)檢測(cè),好好理解理解
View事件消費(fèi)
消費(fèi)就其實(shí)就是我們調(diào)用onTouchEnvent()方法 return true,事件不往下傳遞。
@Overridepublic boolean onTouchEvent(MotionEvent event) {int a = event.getAction();switch (a) {case MotionEvent.ACTION_DOWN:case MotionEvent.ACTION_MOVE://處理消費(fèi)事件的地方mPoints[index].y = event.getY();invalidate();break;case MotionEvent.ACTION_UP:break;}// return true 才會(huì)消費(fèi)return true;}消費(fèi)比較好理解,就不多說了,到此就是我們仿照網(wǎng)易云音樂的均衡器效果了
看似簡(jiǎn)單100多行代碼,其實(shí)還是藏著很多重要的知識(shí)點(diǎn)的。也是面試容易問到的。
希望大家根據(jù)自己的理解去繪制,自己的圖形,直接抄來的就失去他原有的意義。
?
?
?
總結(jié)
以上是生活随笔為你收集整理的仿网易云音乐均衡器调节UI效果的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android仿网易云鲸云音效动效
- 下一篇: 论文常用图表四:Bland-Altman