1.效果
換了大王之后审胸,順帶就下拉聯(lián)通手機(jī)營(yíng)業(yè)廳App,剛開(kāi)始用卸勺,出于好奇每天都會(huì)看看今天自己通過(guò)免流用了多少流量砂沛。
查看流量的入口長(zhǎng)這樣如下圖
由于流量為0,不顯示水波紋效果曙求,下面是我實(shí)現(xiàn)的水波紋效果碍庵,這樣的效果在很多App中都用到。
預(yù)告:
實(shí)現(xiàn)上面的效果將使用到以下知識(shí)點(diǎn)
- 1 xfermode(用的比較少)
- 2 貝塞爾曲線(挺常用的悟狱,Path中提供了相關(guān)Api)
- 3 屬性動(dòng)畫(huà)(太常用了静浴,這里不多說(shuō)了)
2.分步實(shí)現(xiàn)
2.1分析
2.1.1 定義屬性
如果從自定義的view的角度來(lái)時(shí)實(shí)現(xiàn),那么首先考慮的是這個(gè)view可改變的屬性是什么挤渐,比如邊框的顏色苹享、水波峰高度值、移動(dòng)的快慢等浴麻。在布局文件中通過(guò)配置這些屬性得问,讓view呈現(xiàn)不同的效果,如下圖白胀。
<declare-styleable name="XFPView">
<!--水波紋顏色-->
<attr name="backgroundColorWave" format="color"/>
<!--圓心填充顏色-->
<attr name="backgroundColorRound" format="color"/>
<!--圓的邊框顏色-->
<attr name="backgroundColorRoundBoder" format="color"/>
<!--波峰突出值-->
<attr name="waveDy" format="integer"/>
<!--最大完成值-->
<attr name="percentMax" format="float"/>
<!--動(dòng)畫(huà)移動(dòng)時(shí)長(zhǎng)(毫秒椭赋,建議大于1000,小于5000)-->
<attr name="durationAnim" format="integer"/>
</declare-styleable>
定義好了屬性或杠,這樣用起來(lái)方便哪怔,不用修改代碼,只通過(guò)改變xml中的屬性值來(lái)就可以改變view的相關(guān)狀態(tài)(讓你同事去修改你的代碼他會(huì)生氣的)向抢,接下按自定義view的步驟應(yīng)該要繼承View认境,重寫(xiě)onMeasure方法、最后是onDraw方法(套路)挟鸠。這個(gè)過(guò)程看似簡(jiǎn)單叉信,其實(shí)考慮的細(xì)節(jié)很多(紙上得來(lái)終覺(jué)淺)。
那么就來(lái)吧K蚁!E鹕怼硅急!
2.1.2 自定義XfPathView
先寫(xiě)一個(gè)類(lèi)繼承view,直接上代碼佳遂。
/**
* Created by Zhoudesen
* Created time 2018/1/29 16:20
* Description: Xfermode + Path之貝塞爾曲線 應(yīng)用
* Version: V 1.0
*/
public class XfPathView extends View {
/**
* 圓形畫(huà)筆
*/
private Paint mPaintRound;
/**
* 水波紋畫(huà)筆
*/
private Paint mPaintWave;
/**
* 最小寬度
*/
private final static int MIN_WIDTH = 300;
/**
* 最小高度
*/
private final static int MIN_HEIGHT = 300;
/**
* 水波紋 背景顏色
*/
private int mColorBgWave;
/**
* 水波紋 默認(rèn)顏色
*/
private final static int COLOR_BG_WAVE_DEFUALT = 0xaa00ff00;
/**
* 圓形boder顏色
*/
private int mColorBgRoundBoder;
/**
* 圓形背景顏色
*/
private int mColorBgRound;
/**
* 圓形 默認(rèn)顏色 完全透明色
*/
private final static int COLOR_BG_ROUND_DEFUALT = 0x00000000;
/**
* y 軸百分比
*/
private float mPercentY = 0f;
/**
* 最大完成值
*/
private float mPercentMax;
/**
* 1/2 波峰x軸長(zhǎng)度(每個(gè)波峰/波谷均為貝塞爾曲線的控制點(diǎn))
*/
private float mRadius;
/**
* x 軸偏移量(讓水波紋動(dòng)起來(lái))
*/
private float mDx = 0;
/**
* y 軸波峰突出值(波谷凹陷值)
*/
private int mDy = 0;
/**
* view高寬值(高=寬)
*/
private int mViewWidthHeight;
private Path mPath;
private Bitmap mBitmap;
private ValueAnimator mAnimator;
private PorterDuffXfermode mPorterDuffXfermode;
private RectF mRectf;
public XfPathView(Context context) {
this(context, null);
}
public XfPathView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public XfPathView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
/**
* 初始化參數(shù)
*
* @param context
* @param attrs
*/
private void init(Context context, AttributeSet attrs) {
if (attrs != null) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.XFPView);
mColorBgRound = typedArray.getColor(R.styleable.XFPView_backgroundColorRound, COLOR_BG_ROUND_DEFUALT);
mColorBgRoundBoder = typedArray.getColor(R.styleable.XFPView_backgroundColorRoundBoder, COLOR_BG_WAVE_DEFUALT);
mColorBgWave = typedArray.getColor(R.styleable.XFPView_backgroundColorWave, COLOR_BG_WAVE_DEFUALT);
mPercentMax = typedArray.getFloat(R.styleable.XFPView_percentMax, 100);
mDy = typedArray.getInteger(R.styleable.XFPView_waveDy, 20);
typedArray.recycle();
}
mPath = new Path();
//畫(huà)筆初始化
mPaintRound = new Paint();
mPaintRound.setAntiAlias(true);
mPaintRound.setStyle(Paint.Style.STROKE);
mPaintRound.setStrokeWidth(3);
mPaintRound.setColor(mColorBgRoundBoder);
mPaintWave = new Paint();
mPaintWave.setAntiAlias(true);
mPaintWave.setColor(mColorBgWave);
mPaintWave.setStyle(Paint.Style.FILL);
mPorterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
mRectf = new RectF();
}
以上代碼初始化了畫(huà)筆营袜,顏色等相關(guān)基礎(chǔ)工作,其中倒數(shù)第二行PorterDuffXfermode丑罪,才是主角荚板,把主角放到最后登場(chǎng)(這里有坑)。
接下來(lái)開(kāi)始對(duì)view進(jìn)行測(cè)量,這個(gè)view高度和寬度是相等的吩屹,如果高度和寬度不相等跪另,那么取小的值設(shè)置為它的高寬。視乎也很簡(jiǎn)單煤搜,就直接看下面代碼吧免绿。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int height = 0;
int width = 0;
if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY) {
width = MeasureSpec.getSize(widthMeasureSpec);
}
if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY) {
height = MeasureSpec.getSize(heightMeasureSpec);
}
//取最小值為view的高寬,讓“高”=“寬”
width = Math.min(width, height);
if ( width > MIN_WIDTH) {
setMeasuredDimension(width, width);
} else {
setMeasuredDimension(MIN_WIDTH, MIN_HEIGHT);
}
}
到這里宅楞,已經(jīng)完成了自定義相關(guān)屬性针姿,獲取了屬性值袱吆,并且完成初始化準(zhǔn)備厌衙,還重寫(xiě)了onMeasure測(cè)量了高寬。接下來(lái)就是onDraw绞绒,在onDraw之前要分析先畫(huà)什么婶希,后畫(huà)什么,還要計(jì)算相關(guān)的坐標(biāo)蓬衡,所以先分析吧喻杈。
先從水波紋的實(shí)現(xiàn)說(shuō)起(盡管大家都知道,但還是要啰嗦一下 >溫故而知新嘛)
2.1.3水波紋的實(shí)現(xiàn)
畫(huà)一條線或多邊形(或閉合曲線)總得有一個(gè)起點(diǎn)吧狰晚。
【起點(diǎn)】
Path.moveTo(x,y)
有了點(diǎn)就可以畫(huà)線了筒饰,就先看下面點(diǎn)曲線是怎么實(shí)現(xiàn)的【畫(huà)曲線】(就是貝塞爾曲線)
Path.quadTo(x1,y1,x2,y2);
quadTo(x1,y1,x2,y2)這個(gè)函數(shù)傳入兩個(gè)坐標(biāo),第一個(gè)是控制點(diǎn)壁晒,第二個(gè)是這段曲線的終點(diǎn)瓷们。
這個(gè)實(shí)際上就是二階貝塞爾曲線(還有三階、四階秒咐、用的不多) 谬晕。
quadTo是核心方法,各種曲線效果都是由第一個(gè)控制點(diǎn)的變化而變化的携取。當(dāng)然它是有一個(gè)公式的攒钳,但是Path Api中封裝好了,只管用就好(你所關(guān)注的就是調(diào)方法雷滋,傳坐標(biāo)參數(shù))不撑。
【鏈接到某一個(gè)點(diǎn)】(直線)
Path.lineTo(x,y) ;【首尾相連】構(gòu)成一個(gè)閉合的路徑(直線)
Path.close();
下圖是一個(gè)帶有水波紋的閉合圖形文兢,看看它是怎么實(shí)現(xiàn)的
解釋一下上圖吧
moveTo(點(diǎn)0) 起始點(diǎn)
quadTo(點(diǎn)1,點(diǎn)2)焕檬;
quadTo(點(diǎn)3禽作,點(diǎn)4);
quadTo(點(diǎn)5揩页,點(diǎn)6)旷偿;
quadTo(點(diǎn)7,點(diǎn)8)爆侣;
沒(méi)錯(cuò)萍程,水波紋就是有一段段貝塞爾曲線拼接而來(lái)的。
lineTo(點(diǎn)9)
lineTo(點(diǎn)10)
close()將首尾相連(圖中紅色線部分)
通過(guò)Path的四個(gè)方法:moveTo兔仰、quadTo茫负、lineTo和close。已經(jīng)能滿足畫(huà)任意閉合的圖形了乎赴,當(dāng)然Path遠(yuǎn)不止這些忍法,下回分解。
下圖:實(shí)現(xiàn)了略帶水波紋的閉合圖形
2.1.4 onDraw
我們最終要實(shí)現(xiàn)的效果是這樣的
可以拆分成兩部分來(lái)畫(huà)
1、在onDraw中可以先畫(huà)一個(gè)外邊圓的白色邊框boder羹蚣。
2原探、在畫(huà)里面的動(dòng)態(tài)水波紋。
水波紋的實(shí)現(xiàn)上述已經(jīng)介紹過(guò)了顽素,畫(huà)一個(gè)圓邊框視乎也毫不費(fèi)勁咽弦,上代碼。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//--畫(huà)圓boder
drawBoder(canvas);
//--畫(huà)水波紋
drawWave(canvas);
}
private void drawBoder(Canvas canvas){
canvas.drawCircle(getWidth() / 2, getHeight() / 2, mViewWidthHeight / 2 - 1, mPaintRound);
}
drawWave(canvas)的代碼也很簡(jiǎn)單胁出,
private void drawWave(Canvas canvas){
mPath.reset();
//起點(diǎn)
mPath.moveTo(0 - mDx, mPercentY * mViewWidthHeight);
//四段曲線
for (int i = 0; i < 4; i++) {
mDy = -mDy;
mPath.quadTo((1 + 2 * i) * mRadius - mDx, mPercentY * mViewWidthHeight + mDy,
(1 + i) * 2 * mRadius - mDx, mPercentY * mViewWidthHeight);
}
//連接到view的右邊底部
mPath.lineTo(2 * mViewWidthHeight - mDx, mViewWidthHeight);
//連接到view的左邊底部
mPath.lineTo(0 - mDx, mViewWidthHeight);
//閉合
mPath.close();
canvas.drawPath(mPath, mPaintWave);
}
mDx是X軸上的偏移量型型,通過(guò)改變mDx,就可以讓水波紋動(dòng)起來(lái)全蝶。
mDy是施加給控制點(diǎn)的Y軸坐標(biāo)的闹蒜,其中mDy = - mDy (取反)是為了實(shí)現(xiàn)每段曲線上下波動(dòng)效果的。
為了突出效果裸诽,畫(huà)的是一個(gè)填充的圓
實(shí)際效果如下圖
你發(fā)現(xiàn)了什么嫂用?
箭頭所指的兩塊白色區(qū)域并不是我們想要的,隨著水波紋的上升丈冬,圓的上部分也將會(huì)突出,這么解決這個(gè)問(wèn)題嘱函,話不多說(shuō),讓主角xfermode登場(chǎng)埂蕊。
2.1.5 Xfermode
它是干嘛用的往弓?
答:通過(guò)使用Xfermode將繪制的圖形的像素和Canvas上對(duì)應(yīng)位置的像素按照一定的規(guī)則進(jìn)行混合疏唾,形成新的像素,再更新到Canvas中形成最終的圖形它怎么用函似?
答:通過(guò)Paint.setXfermode代碼片段
關(guān)于這段代碼片段有幾點(diǎn)記錄一下:
- saveLayer 會(huì)產(chǎn)生一個(gè)新的圖層槐脏,之后的畫(huà)操作都是在這個(gè)圖層上進(jìn)行
- restoreToCount(int layer),將指定的圖層繪制到cavas.
更進(jìn)一步
實(shí)際上Xfermode有三個(gè)子類(lèi):AvoidXfermode, PixelXorXfermode和PorterDuffXfermode,前兩個(gè)類(lèi)在API 16被遺棄了撇寞,PorterDuffXfermode才是我們需要關(guān)注的顿天。在初始化的時(shí)候用的就是這個(gè)子類(lèi)。
看看它是真么實(shí)例化的:
mPorterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
PorterDuff.Mode蔑担,這個(gè)是什么牌废?SRC_IN代表什么?
看源碼:
// these value must match their native equivalents. See SkXfermode.h
public enum Mode {
/** [0, 0] */
CLEAR (0),
/** [Sa, Sc] */
SRC (1),
/** [Da, Dc] */
DST (2),
/** [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] */
SRC_OVER (3),
/** [Sa + (1 - Sa)*Da, Rc = Dc + (1 - Da)*Sc] */
DST_OVER (4),
/** [Sa * Da, Sc * Da] */
SRC_IN (5),
/** [Sa * Da, Sa * Dc] */
DST_IN (6),
/** [Sa * (1 - Da), Sc * (1 - Da)] */
SRC_OUT (7),
/** [Da * (1 - Sa), Dc * (1 - Sa)] */
DST_OUT (8),
/** [Da, Sc * Da + (1 - Sa) * Dc] */
SRC_ATOP (9),
/** [Sa, Sa * Dc + Sc * (1 - Da)] */
DST_ATOP (10),
/** [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc] */
XOR (11),
/** [Sa + Da - Sa*Da,
Sc*(1 - Da) + Dc*(1 - Sa) + min(Sc, Dc)] */
DARKEN (16),
/** [Sa + Da - Sa*Da,
Sc*(1 - Da) + Dc*(1 - Sa) + max(Sc, Dc)] */
LIGHTEN (17),
/** [Sa * Da, Sc * Dc] */
MULTIPLY (13),
/** [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] */
SCREEN (14),
/** Saturate(S + D) */
ADD (12),
OVERLAY (15);
Mode(int nativeInt) {
this.nativeInt = nativeInt;
}
/**
* @hide
*/
public final int nativeInt;
}
PorterDuff.Mode它是一個(gè)枚舉類(lèi)型啤握,而SRC_IN只是其中一個(gè)模式鸟缕。
看一張圖官方給的參考圖
圖中已經(jīng)列出了兩張圖(Src和Dst)對(duì)應(yīng)不同Mode所呈現(xiàn)的效果,參考這些模式對(duì)應(yīng)的混合效果排抬,實(shí)現(xiàn)的效果也太豐富了懂从。是時(shí)候開(kāi)始改寫(xiě)onDraw方法了。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//--畫(huà)圓boder
drawBoder(canvas);
//--saveLayer
int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
//--畫(huà)水波紋
drawWave(canvas);
canvas.restoreToCount(layerId);
}
根據(jù)之前的分析混合繪制要在一張新的圖層進(jìn)行所以執(zhí)行saveLayer方法蹲蒲,繪制完成后restoreToCount方法將混合后的圖層繪制到canvas上碉钠。
看看drawWave(canvas)中是如何繪制的
private void drawWave(Canvas canvas){
//--畫(huà)圓心
mPaintWave.setColor(mColorBgRound);
canvas.drawCircle(getWidth() / 2, getHeight() / 2, (mViewWidthHeight / 2) - 3, mPaintWave);
//--畫(huà)水波紋
mPath.reset();
mPath.moveTo(0 - mDx, mPercentY * mViewWidthHeight);
//四個(gè)點(diǎn)
for (int i = 0; i < 4; i++) {
mDy = -mDy;
mPath.quadTo((1 + 2 * i) * mRadius - mDx, mPercentY * mViewWidthHeight + mDy,
(1 + i) * 2 * mRadius - mDx, mPercentY * mViewWidthHeight);
}
//連接到右邊view底部
mPath.lineTo(2 * mViewWidthHeight - mDx, mViewWidthHeight);
//連接到左邊底部
mPath.lineTo(0 - mDx, mViewWidthHeight);
//閉合
mPath.close();
//--圓與水波紋fix
mPaintWave.setColor(mColorBgWave);
mPaintWave.setXfermode(mPorterDuffXfermode);
canvas.drawPath(mPath, mPaintWave);
mPaintWave.setXfermode(null);
}
畫(huà)一個(gè)圓與水波紋進(jìn)行混合忿檩,去除超出圓的部分沮焕。
混合前
Srcin這個(gè)模式符合我們的需求
看效果圖
哇参滴,水波紋底部已經(jīng)實(shí)現(xiàn)了我們的效果,but,上部分是什么鬼(這是混合圓的顏色)咖祭。需求可是要透明的啊,好那么就把這個(gè)圓改成透明色把蔫骂,我們已經(jīng)在xml中定義了顏色屬性么翰,很容易改,然后運(yùn)行得到如下結(jié)果辽旋。
圓是透明了浩嫌,水波紋也不見(jiàn)了〔古撸看來(lái)是想的太簡(jiǎn)單了
因?yàn)橥该鞫葧?huì)影響混合效果码耐,完全透明那么混合夠也透明了
那么圓不能完全透明,如果這個(gè)需求不要背景完全透明溶其,那么也就完事了骚腥,下圖Srctop模式的效果
好吧,接著又找了很幾種相像的模式試了一遍瓶逃,就是不行束铭。
機(jī)智時(shí)刻:既然圓不能完全透明廓块,和水波紋混合后上部的底色又得透明,那么可以通過(guò)一個(gè)完全透明矩形和上部分底色混合契沫,就可以消除了带猴。如下圖
看最終效果
//--用完全透明的矩形消除圓的上部分
mPaintWave.setColor(COLOR_BG_ROUND_DEFUALT);
mRectf.bottom = mViewWidthHeight * mPercentY - mDy;
完整代碼:
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.LinearInterpolator;
import itsen.com.bduidemo.R;
import itsen.com.bduidemo.lib.tool.LogTool;
/**
* Created by Zhoudesen
* Created time 2018/1/29 16:20
* Description: Xfermode Path之貝塞爾曲線 應(yīng)用
* Version: V 1.0
*/
public class XfPathView extends View {
/**
* 圓形畫(huà)筆
*/
private Paint mPaintRound;
/**
* 水波紋畫(huà)筆
*/
private Paint mPaintWave;
/**
* 最小寬度
*/
private final static int MIN_WIDTH = 300;
/**
* 最小高度
*/
private final static int MIN_HEIGHT = 300;
/**
* 水波紋 背景顏色
*/
private int mColorBgWave;
/**
* 水波紋 默認(rèn)顏色
*/
private final static int COLOR_BG_WAVE_DEFUALT = 0xaa00ff00;
/**
* 圓形boder顏色
*/
private int mColorBgRoundBoder;
/**
* 圓形背景顏色
*/
private int mColorBgRound;
/**
* 圓形 默認(rèn)顏色 完全透明色
*/
private final static int COLOR_BG_ROUND_DEFUALT = 0x00000000;
/**
* y 軸百分比
*/
private float mPercentY = 0f;
/**
* 最大完成值
*/
private float mPercentMax;
/**
* 1/2 波峰x軸長(zhǎng)度(每個(gè)波峰/波谷均為貝塞爾曲線的控制點(diǎn))
*/
private float mRadius;
/**
* x 軸偏移量(讓水波紋動(dòng)起來(lái))
*/
private float mDx = 0;
/**
* y 軸波峰突出值(波谷凹陷值)
*/
private int mDy = 0;
/**
* view高寬值(高=寬)
*/
private int mViewWidthHeight;
private Path mPath;
private Bitmap mBitmap;
private ValueAnimator mAnimator;
private PorterDuffXfermode mPorterDuffXfermode;
private RectF mRectf;
public XfPathView(Context context) {
this(context, null);
}
public XfPathView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public XfPathView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
/**
* 初始化參數(shù)
*
* @param context
* @param attrs
*/
private void init(Context context, AttributeSet attrs) {
if (attrs != null) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.XFPView);
mColorBgRound = typedArray.getColor(R.styleable.XFPView_backgroundColorRound, COLOR_BG_ROUND_DEFUALT);
mColorBgRoundBoder = typedArray.getColor(R.styleable.XFPView_backgroundColorRoundBoder, COLOR_BG_WAVE_DEFUALT);
mColorBgWave = typedArray.getColor(R.styleable.XFPView_backgroundColorWave, COLOR_BG_WAVE_DEFUALT);
mPercentMax = typedArray.getFloat(R.styleable.XFPView_percentMax, 100);
mDy = typedArray.getInteger(R.styleable.XFPView_waveDy, 20);
typedArray.recycle();
}
mPath = new Path();
//畫(huà)筆初始化
mPaintRound = new Paint();
mPaintRound.setAntiAlias(true);
mPaintRound.setStyle(Paint.Style.STROKE);
mPaintRound.setStrokeWidth(3);
mPaintRound.setColor(mColorBgRoundBoder);
mPaintWave = new Paint();
mPaintWave.setAntiAlias(true);
mPaintWave.setColor(mColorBgWave);
mPaintWave.setStyle(Paint.Style.FILL);
mPorterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
mRectf = new RectF(0,0,mViewWidthHeight,0);
setmPercentY(10);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int height = 0;
int width = 0;
if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY) {
width = MeasureSpec.getSize(widthMeasureSpec);
}
if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY) {
height = MeasureSpec.getSize(heightMeasureSpec);
}
//取最小值為view的高寬,讓“高”=“寬”
width = Math.min(width, height);
if (width > 0 && width > MIN_WIDTH) {
setMeasuredDimension(width, width);
} else {
setMeasuredDimension(MIN_WIDTH, MIN_HEIGHT);
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mViewWidthHeight = w;
//二分之一的波峰波谷
mRadius = mViewWidthHeight / 4;
if (mBitmap == null) {
mBitmap = Bitmap.createBitmap(mViewWidthHeight, mViewWidthHeight, Bitmap.Config.ARGB_8888);
}
startAinm();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//--畫(huà)圓boder
drawBoder(canvas);
//--saveLayer
int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
//--畫(huà)水波紋
drawWave(canvas);
canvas.restoreToCount(layerId);
}
private void drawBoder(Canvas canvas){
canvas.drawCircle(getWidth() / 2, getHeight() / 2, mViewWidthHeight / 2 - 1, mPaintRound);
}
private void drawWave(Canvas canvas){
//--畫(huà)圓心
mPaintWave.setColor(mColorBgRound);
canvas.drawCircle(getWidth() / 2, getHeight() / 2, (mViewWidthHeight / 2) - 3, mPaintWave);
//--畫(huà)水波紋
mPath.reset();
mPath.moveTo(0 - mDx, mPercentY * mViewWidthHeight);
//四個(gè)點(diǎn)
for (int i = 0; i < 4; i++) {
mDy = -mDy;
mPath.quadTo((1 + 2 * i) * mRadius - mDx, mPercentY * mViewWidthHeight + mDy,
(1 + i) * 2 * mRadius - mDx, mPercentY * mViewWidthHeight);
}
//連接到右邊view底部
mPath.lineTo(2 * mViewWidthHeight - mDx, mViewWidthHeight);
//連接到左邊底部
mPath.lineTo(0 - mDx, mViewWidthHeight);
//閉合
mPath.close();
//--圓與水波紋fix
mPaintWave.setColor(mColorBgWave);
mPaintWave.setXfermode(mPorterDuffXfermode);
canvas.drawPath(mPath, mPaintWave);
//--用完全透明的矩形消除圓的上部分
mPaintWave.setColor(COLOR_BG_ROUND_DEFUALT);
mRectf.right = mViewWidthHeight;
mRectf.bottom = mViewWidthHeight * mPercentY - mDy;
canvas.drawRect(mRectf, mPaintWave);
mPaintWave.setXfermode(null);
}
/**
* 初始化并開(kāi)始動(dòng)畫(huà)
*/
public void startAinm() {
mAnimator = ValueAnimator.ofFloat(0, mViewWidthHeight);
mAnimator.setDuration(2000);
mAnimator.setInterpolator(new LinearInterpolator());
mAnimator.setRepeatCount(ValueAnimator.INFINITE);
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mDx = (float) animation.getAnimatedValue();
postInvalidate();
}
});
mAnimator.start();
}
/**
* 暫停
*/
public void stopAnim() {
if (mAnimator.isStarted()) {
mAnimator.pause();
}
}
/**
* 重啟
*/
public void reStartAinm() {
if (mAnimator != null && mAnimator.isPaused()) {
mAnimator.start();
}
}
/**
* 設(shè)置完成度
*
* @param value
*/
public void setmPercentY(float value) {
this.mPercentY = 1 - (value / mPercentMax < 1 ? value / mPercentMax : 1);
}
}
水平有限懈万,疏漏之處請(qǐng)批評(píng)指正
寫(xiě)作不易拴清、喜歡的給個(gè)星,謝謝会通!