FuckingVolumeSlider
源碼GitHub地址,設計來自于該貼內
前幾天看到一個反人類音量滑動條設計的有趣帖子原帖地址,網友設計了各種反人類的滑動條(這些腦洞我是服氣的!),于是我抽空在 android 上面實現了一個其中比較有趣的設計,點擊喇叭彈射出去圓點控制音量。在寫這個控件的時候遇到的一些知識點分享給大家。視頻演示地址
效果圖:
使用方法
mSliderView = (VolumeSliderView) findViewById(R.id.sliderView);
mSliderView.setOnVolumeSlideListener(new VolumeSliderView.OnVolumeSlideListener() {
@Override
public void result(int volume) {
mTextView.setText("音量: " + volume);
}
});
原理
1. 喇叭底座與聲波繪制
由效果圖可以看出,喇叭底座的右上角和右下角為圓角,其余的角都不是圓角。非圓角通過 path.lineTo 可以非常容易的畫出,畫圓角折線有很多種方法,其中最簡單的就是給畫筆 paint 添加 CornerPathEffect 就可以輕松的畫出圓角,我開始也是這么做的(too yang too simple) 。在這個控件的場景中這么做并不合理,因為后面還要在喇叭的區域繪制水波紋,而 PathEffect 只是讓畫筆繪制出的效果看著像圓角,水波紋是繪制在底座的 path 上的,而底座的 path 并不是圓角,所以繪制完水波紋之后圓角被填充回了直角。
還有什么簡單辦法可以繪制圓角折線呢?那就是使用貝塞爾曲線了,可以用二階貝塞爾曲線來作為圓角完美解決,要想完美銜接需要兩個銜接的折線點分別和控制點相對于貝塞爾曲線的起點和終點是對稱的:
整個底座也是相對于一個中軸是對稱的,所以只要選取出上半部或者下半部的點就可以通過中軸來算錯所有的點。
聲波到底怎么畫出來呢?聲波的頭部也是圓角,剛開始的時候我簡單的以為通過 path.addArc()
畫出一個圓弧,然后圓弧的 paint 設置一個寬度和 Paint.Cap.ROUND
就可以完成,其實這種方法也會和后面畫水波紋的時候沖突的,因為圓弧的 path 所圍成的區域并不是一個弧線區域,這都是 paint 寬度造成的視覺效果。
后來我在這篇文章里得到了啟發 Android SmileyRating,聲波可以用四條曲線圍起來啊,左右兩條二階貝塞爾曲線,上下兩條三階貝塞爾曲線,一條聲波通過 10 個點就可以畫出來,而聲波也相對于一個中軸對稱的,所以只要取出上半部或者下半部的 5 個點就可以確定所有的點。

2. 水波紋繪制與旋轉
水波紋的繪制用到 canvas.clipPath()
方法把喇叭的 path 裁剪出來再繪制圓形水波紋就可以了。旋轉是對喇叭的 path 執行 Matrix 旋轉操作。
//clip
canvas.clipPath(mDrawPath);
canvas.drawCircle(mSpeaker.getRotationX(), mSpeaker.getRotationY(), mRippleRadius, mRipplePaint);
//matrix rotate
mRotationMatrix.preRotate(mDegree, mSpeaker.getRotationX(), mSpeaker.getRotationY());
mDrawPath.addPath(mSpeakerPath, mRotationMatrix);
球的運動曲線
其實球的運動曲線不是拋物線,而是一條二階貝塞爾曲線,通過調整控制點的位置來達到和拋物線類似的效果。