Android自定義View控件實(shí)現(xiàn)多種水波紋漣漪擴(kuò)散效果
效果圖

實(shí)現(xiàn)思路
這個(gè)效果實(shí)現(xiàn)起來(lái)并不難,重要的是思路
此View滿(mǎn)足了多種水波紋漣漪擴(kuò)散效果,這要求它能滿(mǎn)足很多的變化
根據(jù)上面的樣式,可以看出此View需要滿(mǎn)足以下變化
- 圓圈從中心可循環(huán)向外擴(kuò)散
- 圓圈之間的擴(kuò)散間距可以改變
- 可控制擴(kuò)散圓的漸變度
- 圓圈可以是線(xiàn)條樣式或者實(shí)心樣式
- 圓圈擴(kuò)散的速度可以控制
- 適配圓圈不同大小下的擴(kuò)散效果
具體實(shí)現(xiàn)
創(chuàng)建自定義屬性
首先為View創(chuàng)建自定義的xml屬性
在工程的values目錄下新建attrs.xml文件
<declare-styleable name="mRippleView"> <attr name="cColor" format="color"/> <attr name="cSpeed" format="integer"/> <attr name="cDensity" format="integer"/> <attr name="cIsFill" format="boolean"/> <attr name="cIsAlpha" format="boolean"/> </declare-styleable>
各個(gè)屬性的作用如下
- cColor:View控件的顏色
- cSpeed:向外擴(kuò)散的速度
- cDensity:圓形波紋擴(kuò)散的間距
- cIsFill:是否開(kāi)啟填充模式,true為實(shí)心圓
- cIsAlpha:是否開(kāi)啟漸變效果,true為開(kāi)啟
創(chuàng)建自定義View控件
新建RippleView類(lèi)繼承View類(lèi),重寫(xiě)它的三個(gè)構(gòu)造方法,獲取用戶(hù)設(shè)置的屬性,同時(shí)指定默認(rèn)值
public RippleView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// 獲取用戶(hù)配置屬性
TypedArray tya = context.obtainStyledAttributes(attrs, R.styleable.mRippleView);
mColor = tya.getColor(R.styleable.mRippleView_cColor, Color.BLUE);
mSpeed = tya.getInt(R.styleable.mRippleView_cSpeed, 1);
mDensity = tya.getInt(R.styleable.mRippleView_cDensity, 10);
mIsFill = tya.getBoolean(R.styleable.mRippleView_cIsFill, false);
mIsAlpha = tya.getBoolean(R.styleable.mRippleView_cIsAlpha, false);
tya.recycle();
init();
}
使用TypedArray讀取完自定義的屬性后一定要記得調(diào)用recycle方法釋放掉
重寫(xiě)onMeasure
測(cè)量onMeasure,首先需要測(cè)量出View的寬和高,并指定View在wrap_content時(shí)的最小范圍,對(duì)于View繪制流程還不熟悉的同學(xué),可以先去了解下具體的繪制流程
http://www.dhdzp.com/article/118775.htm
重寫(xiě)onMeasure方法,其中我們要考慮當(dāng)View的寬高被指定為wrap_content時(shí)的情況,如果我們不對(duì)wrap_content的情況進(jìn)行處理,那么當(dāng)使用者指定View的寬高為wrap_content時(shí)將無(wú)法正常顯示出View
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int myWidthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int myWidthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int myHeightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int myHeightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
// 獲取寬
if (myWidthSpecMode == MeasureSpec.EXACTLY) {
// match_parent/精確值
mWidth = myWidthSpecSize;
} else {
// wrap_content
mWidth = DensityUtil.dip2px(mContext, 120);
}
// 獲取高
if (myHeightSpecMode == MeasureSpec.EXACTLY) {
// match_parent/精確值
mHeight = myHeightSpecSize;
} else {
// wrap_content
mHeight = DensityUtil.dip2px(mContext, 120);
}
// 設(shè)置該view的寬高
setMeasuredDimension(mWidth, mHeight);
}
MeasureSpec的狀態(tài)分為三種EXACTLY、AT_MOST、UNSPECIFIED,這里只要單獨(dú)指定非精確值EXACTLY之外的情況就好了
本文中使用到的DensityUtil類(lèi),是為了將dp轉(zhuǎn)換為px來(lái)使用,以便適配不同的屏幕顯示效果
public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
重寫(xiě)onDraw
設(shè)計(jì)的整體思路如下圖所示

先要實(shí)現(xiàn)圓形向外擴(kuò)散的效果
- 初始化第一個(gè)圓
這里的動(dòng)畫(huà)效果本來(lái)是想使用ValueAnimator屬性動(dòng)畫(huà)的數(shù)值發(fā)生器來(lái)實(shí)現(xiàn),但是我們這里有很多的計(jì)算需求,所以最后還是選擇使用算法來(lái)實(shí)現(xiàn),方便控制圓的一些參數(shù)
想要實(shí)現(xiàn)擴(kuò)散的效果,這里思路是在每次更新View時(shí)動(dòng)態(tài)改變圓的半徑,同時(shí)還需要給圓設(shè)置漸變度數(shù),所以決定用一個(gè)類(lèi)來(lái)保存圓的狀態(tài),所有圓都存在一個(gè)List里
// 添加第一個(gè)圓圈 mRipples = new ArrayList<>(); Circle c = new Circle(0, 255); mRipples.add(c);
傳入Circle類(lèi)里的兩個(gè)參數(shù),第一個(gè)0表示圓的初始寬度,第二個(gè)255表示初始透明度
- 添加新圓
要想實(shí)現(xiàn)不斷有圓向外擴(kuò)散,就需要在第一個(gè)圓擴(kuò)散到一定范圍時(shí)在圓心處再添加一個(gè)圓,這個(gè)的范圍可以由圓的半徑來(lái)控制,當(dāng)List集合中最后一個(gè)圓的半徑增加到某個(gè)值mDensity時(shí),新的圓就從圓心處創(chuàng)建出來(lái)
// 添加圓
if (mRipples.size() > 0) {
// 控制第二個(gè)圓出來(lái)的間距
if (mRipples.get(mRipples.size() - 1).width > DensityUtil.dip2px(mContext, mDensity)) {
mRipples.add(new Circle(0, 255));
}
}
- 刪除List中多余的圓
List中的圓存儲(chǔ)的數(shù)量不宜過(guò)多,多了內(nèi)存消耗大,需要在當(dāng)圓的半徑超過(guò)View的寬度時(shí)就刪掉這個(gè)圓
// 當(dāng)圓超出View的寬度后刪除
if (c.width > mWidth / 2) {
mRipples.remove(i);
}
我們也可以在外切正方形的頂點(diǎn)處刪除這個(gè)圓,需要用到勾股定律來(lái)計(jì)算擴(kuò)散圓到外切正方形頂點(diǎn)的位置

如上圖所示,得出計(jì)算公式為
// 使用勾股定律求得一個(gè)外切正方形中心點(diǎn)離頂點(diǎn)的距離 sqrtNumber = (int) (Math.sqrt(mWidth * mWidth + mHeight * mHeight) / 2);
這樣就需要修改刪除圓的位置了
if (c.width > sprtNumber) {
mRipples.remove(i);
}
- 控制擴(kuò)散圓的漸變度
當(dāng)圓在向View的邊緣擴(kuò)散時(shí),漸變度數(shù)的改變需要?jiǎng)討B(tài)來(lái)計(jì)算,漸變的計(jì)算算法要適配不同的圓寬度大小,我們知道透明度是0~255之間的,0表示完全透明,255表示百分百不透明,計(jì)算的時(shí)候就是需要將這個(gè)數(shù)值等份分配到圓的寬度里
這里要區(qū)分一點(diǎn),對(duì)于圓來(lái)說(shuō),寬度是由圓心從0開(kāi)始向外遞增,而漸變度數(shù)則是由圓心從255開(kāi)始向外遞減,當(dāng)圓與最外圍的正方形內(nèi)切時(shí)漸變度必須變?yōu)?,由此分析得知,公式如下
透明度 = 255 - 圓的寬度 * (255 / View寬度)
double alpha = 255 - c.width * (255 / ((double) mWidth / 2)); c.alpha = (int) alpha;
GitHub地址
https://github.com/zhuwentao2150/RippleView
總結(jié)
關(guān)于自定義View的總結(jié)部分在我的其它博客中已經(jīng)寫(xiě)過(guò)蠻多了,有興趣的可以去看看
好了,以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
- Android水波紋載入控件CircleWaterWaveView使用詳解
- android自定義WaveView水波紋控件
- Android自定義WaveProgressView實(shí)現(xiàn)水波紋加載需求
- Android自定義View實(shí)現(xiàn)水波紋效果
- Android自定義View實(shí)現(xiàn)水波紋引導(dǎo)動(dòng)畫(huà)
- Android 自定義view實(shí)現(xiàn)水波紋動(dòng)畫(huà)效果
- Android自定義View 實(shí)現(xiàn)水波紋動(dòng)畫(huà)引導(dǎo)效果
- Android自定義view實(shí)現(xiàn)水波紋進(jìn)度球效果
- Android項(xiàng)目實(shí)戰(zhàn)手把手教你畫(huà)圓形水波紋loadingview
- Android自定義View實(shí)現(xiàn)簡(jiǎn)單水波紋效果
相關(guān)文章
Android使用lottie加載json動(dòng)畫(huà)的示例代碼
本篇文章主要介紹了Android使用lottie加載json動(dòng)畫(huà)的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-01-01
Kotlin Flow常用封裝類(lèi)StateFlow使用詳解
這篇文章主要為大家介紹了Kotlin Flow常用封裝類(lèi)StateFlow使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08
Android activity和view判斷滑動(dòng)
這篇文章主要介紹了Android activity和view判斷滑動(dòng)的相關(guān)資料,需要的朋友可以參考下2017-06-06
淺談Android AsyncTask內(nèi)存安全的一種使用方式
這篇文章主要介紹了淺談Android AsyncTask內(nèi)存安全的一種使用方式,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-08-08
Android自定義View實(shí)現(xiàn)顏色選取器
這篇文章主要為大家詳細(xì)介紹了Android自定義View實(shí)現(xiàn)顏色選取器 ,類(lèi)似SeekBar的方式通過(guò)滑動(dòng)選擇顏色,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06
Android 通過(guò)自定義view實(shí)現(xiàn)水波紋效果案例詳解
這篇文章主要介紹了Android 通過(guò)自定義view實(shí)現(xiàn)水波紋效果案例詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08
詳解Android中的MVP架構(gòu)分解和實(shí)現(xiàn)
本篇文章主要介紹了詳解Android中的MVP架構(gòu)分解和實(shí)現(xiàn),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-02-02

