版權聲明:本文爲博主原創文章,未經博主容許不得轉載php
系列教程:Android開發之從零開始系列html
源碼:AnliaLee/Progressbar,歡迎starjava
你們要是看到有錯誤的地方或者有啥好的建議,歡迎留言評論android
前言:相信同行們都知道,咱們程序員有一種痛,叫作別人的代碼。讀懂別人的代碼很重要的一點就是要抓住做者的思路,有了思路才能將過程推導出來,不然腦闊會疼。爲己爲人,本系列教程博客,我都會將本身實現的思路寫下來,帶你們一步步從零開始實現咱們想要的效果。由於最近在網上看了不少前輩們實現的 水波浪進度框,一時手癢,因此任性地決定這系列的第二篇博客的主角就是它了git
本篇只着重於思路和實現步驟,裏面用到的一些知識原理不會很是細地拿來說,若是有不清楚的api或方法能夠在網上搜下相應的資料,確定有大神講得很是清楚的,我這就不獻醜了。本着認真負責的精神我會把相關知識的博文連接也貼出來(其實就是懶不想寫那麼多哈哈),你們能夠自行傳送。爲了照顧第一次閱讀系列博客的小夥伴,本篇可能會出現一些在以前系列博客就講過的內容,看過的童鞋自行跳過該段便可程序員
國際慣例,先來效果展現github
相關博文連接
【Android - 自定義View】之自定義View淺析spring
Android 自定義View (一)canvas
既然咱們實現的是水波浪進度條,那咱們就先從波浪效果入手吧。波浪是上下起伏的,也就意味着咱們繪製的波浪應該是一條上下波動的曲線。查閱資料發現二階貝塞爾曲線足以知足咱們的需求,咱們能夠經過控制其控制點的座標系y值實現曲線的上下波動。Android中提供了繪製貝塞爾曲線的API及方法,下面咱們就試着繪製一條上下波動的二階貝塞爾曲線(有關貝塞爾曲線以及Path方面的知識已經有許多大大講得很是清楚了,這裏貼出他們的博客連接,就不詳細展開了)
public class WaveProgressView extends View {
private Paint wavePaint;//繪製波浪畫筆
private Path wavePath;//繪製波浪Path
private float waveWidth;//波浪寬度
private float waveHeight;//波浪高度
public WaveProgressView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context,attrs);
}
private void init(Context context,AttributeSet attrs){
waveWidth = DpOrPxUtils.dip2px(context,15);
waveHeight = DpOrPxUtils.dip2px(context,20);
wavePath = new Path();
wavePaint = new Paint();
wavePaint.setColor(Color.GREEN);
wavePaint.setAntiAlias(true);//設置抗鋸齒
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(getWavePath(),wavePaint);
}
private Path getWavePath(){
wavePath.reset();
wavePath.moveTo(0,waveHeight);//起始點移動至(0,waveHeight),注意座標系y軸是向下的
for (int i=0;i<5;i++){
wavePath.rQuadTo(waveWidth/2, waveHeight, waveWidth, 0);
wavePath.rQuadTo(waveWidth/2, -waveHeight, waveWidth, 0);
}
return wavePath;
}
}
複製代碼
其中用到了dp和px相互轉換的工具類(相關知識有興趣的能夠本身上網搜下),這裏也將相關代碼貼出來
public class DpOrPxUtils {
public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
public static int px2dip(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
}
複製代碼
界面佈局:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent">
<LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
<com.anlia.progressbar.WaveProgressView android:id="@+id/wave_progress" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:layout_marginLeft="20dp"/>
</LinearLayout>
</RelativeLayout>
複製代碼
在Activity中進行註冊
waveProgressView = (WaveProgressView) findViewById(R.id.wave_progress);
複製代碼
效果如圖
相關博文連接
根據咱們的需求,咱們要模擬出進度框中水位隨着進度的增長而不斷上升的效果。咱們將水看做是一種填充物,而後將填充物劃分紅最上層的波浪曲線區域以及下層的矩形區域。咱們能夠利用path.lineTo()和path.close()方法將波浪曲線和矩形組裝封閉起來,最終效果如圖
path繪製的順序以下圖所示(初始點爲p0,p3至p0段繪製波浪曲線)
實現代碼以下,修改咱們的WaveProgressView
public class WaveProgressView extends View {
//省略部分代碼...
private int waveNum;//波浪組的數量(一次起伏爲一組)
private int defaultSize;//自定義View默認的寬高
private int maxHeight;//爲了看到波浪效果,給定一個比填充物稍高的高度
private void init(Context context,AttributeSet attrs){
//省略部分代碼...
waveWidth = DpOrPxUtils.dip2px(context,20);
waveHeight = DpOrPxUtils.dip2px(context,10);
defaultSize = DpOrPxUtils.dip2px(context,200);
maxHeight = DpOrPxUtils.dip2px(context,250);
waveNum =(int) Math.ceil(Double.parseDouble(String.valueOf(defaultSize / waveWidth / 2)));//波浪的數量須要進一取整,因此使用Math.ceil函數
}
private Path getWavePath(){
wavePath.reset();
//移動到右上方,也就是p0點
wavePath.moveTo(defaultSize, maxHeight - defaultSize);
//移動到右下方,也就是p1點
wavePath.lineTo(defaultSize, defaultSize);
//移動到左下邊,也就是p2點
wavePath.lineTo(0, defaultSize);
//移動到左上方,也就是p3點
wavePath.lineTo(0, maxHeight - defaultSize);
//從p3開始向p0方向繪製波浪曲線
for (int i=0;i<waveNum;i++){
wavePath.rQuadTo(waveWidth/2, waveHeight, waveWidth, 0);
wavePath.rQuadTo(waveWidth/2, -waveHeight, waveWidth, 0);
}
//將path封閉起來
wavePath.close();
return wavePath;
}
}
複製代碼
相關博文連接
在上面的代碼中,View的寬高是由path區域的大小決定的,直接寫死在了init()方法中,而咱們的實際需求是View的寬高能夠由咱們在外部進行設置。根據需求,進度框是一個圓形,咱們須要將View的寬高強制相等,所以咱們重寫View的onMeasure()方法
public class WaveProgressView extends View {
//省略部分代碼...
private int viewSize;//從新測量後View實際的寬高
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int height = measureSize(defaultSize, heightMeasureSpec);
int width = measureSize(defaultSize, widthMeasureSpec);
int min = Math.min(width, height);// 獲取View最短邊的長度
setMeasuredDimension(min, min);// 強制改View爲以最短邊爲長度的正方形
viewSize = min;
waveNum =(int) Math.ceil(Double.parseDouble(String.valueOf(viewSize / waveWidth / 2)));
}
private int measureSize(int defaultSize,int measureSpec) {
int result = defaultSize;
int specMode = View.MeasureSpec.getMode(measureSpec);
int specSize = View.MeasureSpec.getSize(measureSpec);
if (specMode == View.MeasureSpec.EXACTLY) {
result = specSize;
} else if (specMode == View.MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
return result;
}
}
複製代碼
波浪隨進度上升,實際上就是填充物的高度(p0p1,p3p2的長度)隨進度值的增長而增長。修改咱們的WaveProgressView,並添加動畫效果
public class WaveProgressView extends View {
//省略部分代碼...
private WaveProgressAnim waveProgressAnim;
private float percent;//進度條佔比
private float progressNum;//能夠更新的進度條數值
private float maxNum;//進度條最大值
private void init(Context context,AttributeSet attrs){
//省略部分代碼...
percent = 0;
progressNum = 0;
maxNum = 100;
waveProgressAnim = new WaveProgressAnim();
}
private Path getWavePath(){
wavePath.reset();
//移動到右上方,也就是p0點
wavePath.moveTo(viewSize, (1-percent)*viewSize);//讓p0p1的長度隨percent的增長而增長(注意這裏y軸方向默認是向下的)
//移動到右下方,也就是p1點
wavePath.lineTo(viewSize, viewSize);
//移動到左下邊,也就是p2點
wavePath.lineTo(0, viewSize);
//移動到左上方,也就是p3點
wavePath.lineTo(0, (1-percent)*viewSize);//讓p3p2的長度隨percent的增長而增長(注意這裏y軸方向默認是向下的)
//從p3開始向p0方向繪製波浪曲線
for (int i=0;i<waveNum;i++){
wavePath.rQuadTo(waveWidth/2, waveHeight, waveWidth, 0);
wavePath.rQuadTo(waveWidth/2, -waveHeight, waveWidth, 0);
}
//將path封閉起來
wavePath.close();
return wavePath;
}
public class WaveProgressAnim extends Animation {
public WaveProgressAnim(){}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
percent = interpolatedTime * progressNum / maxNum;
postInvalidate();
}
}
/** * 設置進度條數值 * @param progressNum 進度條數值 * @param time 動畫持續時間 */
public void setProgressNum(float progressNum, int time) {
this.progressNum = progressNum;
percent = 0;
waveProgressAnim.setDuration(time);
this.startAnimation(waveProgressAnim);
}
}
複製代碼
在Activity中調用**setProgressNum()**方法
waveProgressView.setProgressNum(80,3000);
複製代碼
效果如圖
上一小節咱們實現的波浪上升的動畫,這一節中咱們要爲波浪添加一個循環向左平移的效果
讓波浪向左平移,咱們將其能夠理解爲繪製波浪曲線的起點不斷向左移動,而循環則是當起點移動一段距離後又回到原來的位置從新向左移動。經過以前的分析咱們知道波浪曲線的繪製起點是p3,所以整個波浪的平移效果咱們只須要經過修改p3的位置便可實現
但僅僅是這樣還不夠,咱們以前整段波浪曲線的寬度和View(正方形目標區域)的寬度是相等的,若是咱們僅僅只是讓p3向左平移,會出現曲線不能鋪滿目標區域的狀況,曲線與p0則會以默認的直線進行鏈接。有2D橫向遊戲開發經驗的小夥伴對於這種橫向背景循環的效果會很熟悉,通常的處理手段是將至少兩個相同的背景圖片拼接起來,當角色從第一個背景圖片最左端出發,向右移動了第一個背景圖片寬度的距離時,將角色從新放回到第一個背景圖片的最左端,這樣就能實現背景圖片循環的效果。參考這種手段,對於咱們波浪循環平移來講,p3就至關於角色,波浪曲線至關於背景圖片,p3點平移的最大距離爲原來一整段曲線的寬度(目標區域的寬度),整段曲線的寬度也變成原來的兩倍(至少兩倍)。爲了讓你們更清楚地瞭解整個過程,我修改了View寬度的測量邏輯給你們看下效果(波浪到達最大高度後高度再也不改變,僅進行平移循環)
而後下面是咱們實際要實現的效果
實現代碼以下,修改咱們的WaveProgressView
public class WaveProgressView extends View {
//省略部分代碼...
private float waveMovingDistance;//波浪平移的距離
private void init(Context context,AttributeSet attrs){
//省略部分代碼...
waveMovingDistance = 0;
}
private Path getWavePath(){
//省略部分代碼...
//移動到左上方,也就是p3點(x軸默認方向是向右的,咱們要向左平移,所以設爲負值)
//wavePath.lineTo(0, (1-percent)*viewSize);
wavePath.lineTo(-waveMovingDistance, (1-percent)*viewSize);
//從p3開始向p0方向繪製波浪曲線(曲線寬度爲原來的兩倍也就是波浪數量*2)
for (int i=0;i<waveNum*2;i++){
wavePath.rQuadTo(waveWidth/2, waveHeight, waveWidth, 0);
wavePath.rQuadTo(waveWidth/2, -waveHeight, waveWidth, 0);
}
}
public class WaveProgressAnim extends Animation {
//省略部分代碼...
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
//波浪高度到達最大值後就不須要循環了,只需讓波浪曲線平移循環便可
if(percent < progressNum / maxNum){
percent = interpolatedTime * progressNum / maxNum;
}
waveMovingDistance = interpolatedTime * waveNum * waveWidth * 2;
postInvalidate();
}
}
/** * 設置進度條數值 * @param progressNum 進度條數值 * @param time 動畫持續時間 */
public void setProgressNum(float progressNum, int time) {
//省略部分代碼...
waveAnim.setRepeatCount(Animation.INFINITE);//讓動畫無限循環
waveAnim.setInterpolator(new LinearInterpolator());//讓動畫勻速播放,否則會出現波浪平移停頓的現象
}
}
複製代碼
若是須要讓波浪到達最高處後平移的速度改變,給動畫設置監聽便可
waveProgressAnim.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {}
@Override
public void onAnimationEnd(Animation animation) {}
@Override
public void onAnimationRepeat(Animation animation) {
if(percent == progressNum / maxNum){
waveProgressAnim.setDuration(8000);
}
}
});
複製代碼
相關博文連接
終於要開始繪製進度框了,之因此要將進度框放到後面來說,不只是由於這部分比較簡單,並且按照這樣一個順序去思考設計對於初學者來講會更加友好,畢竟是從零開始的教程嘛(因此給個讚唄๑乛◡乛๑)。好了,一番自詡以後咱們進入正題,按照需求,咱們不只要繪製圓形進度框做爲背景,還須要取進度框和波浪填充物的交集部分繪製到進度框中,這裏用到了PorterDuffXfermode方面的知識(有不瞭解的童鞋能夠經過上面的博客連接傳送過去看看),咱們繼續修改WaveProgressView,只須要加多幾行代碼就能夠了
public class WaveProgressView extends View {
//省略部分代碼...
private Paint circlePaint;//圓形進度框畫筆
private Bitmap bitmap;//緩存bitmap
private Canvas bitmapCanvas;
private void init(Context context,AttributeSet attrs){
//省略部分代碼...
wavePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));//根據繪製順序的不一樣選擇相應的模式便可
circlePaint = new Paint();
circlePaint.setColor(Color.GRAY);
circlePaint.setAntiAlias(true);//設置抗鋸齒
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//這裏用到了緩存技術
bitmap = Bitmap.createBitmap(viewSize, viewSize, Bitmap.Config.ARGB_8888);
bitmapCanvas = new Canvas(bitmap);
bitmapCanvas.drawCircle(viewSize/2, viewSize/2, viewSize/2, circlePaint);
bitmapCanvas.drawPath(getWavePath(),wavePaint);
canvas.drawBitmap(bitmap, 0, 0, null);
}
}
複製代碼
效果如圖
一樣的,若是想要用其餘圖片做爲背景進度框,也能夠按照這樣的思路進行擴展,這留給小夥伴們本身去研究,就不展開說啦(若是用不規則圖片做爲背景時記得要從新測量View的大小)
相關博文連接
咱們的View中有許多屬性須要在佈局文件中進行設置,這須要咱們本身進行自定義,實現過程以下
首先在res\values文件夾中添加attr.xml,爲WaveProgressView自定義屬性
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--注意這裏的name要和自定義View的名稱一致,否則在xml佈局中沒法引用-->
<declare-styleable name="WaveProgressView">
<attr name="wave_color" format="color"></attr>
<attr name="bg_color" format="color"></attr>
<attr name="wave_width" format="dimension"></attr>
<attr name="wave_height" format="dimension"></attr>
</declare-styleable>
</resources>
複製代碼
修改WaveProgressView,爲自定義屬性賦值
public class WaveProgressView extends View {
//省略部分代碼...
private int waveColor;//波浪顏色
private int bgColor;//背景進度框顏色
private void init(Context context,AttributeSet attrs){
//省略部分代碼...
TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.WaveProgressView);
waveWidth = typedArray.getDimension(R.styleable.WaveProgressView_wave_width,DpOrPxUtils.dip2px(context,25));
waveHeight = typedArray.getDimension(R.styleable.WaveProgressView_wave_height,DpOrPxUtils.dip2px(context,5));
waveColor = typedArray.getColor(R.styleable.WaveProgressView_wave_color,Color.GREEN);
bgColor = typedArray.getColor(R.styleable.WaveProgressView_bg_color,Color.GRAY);
typedArray.recycle();
wavePaint.setColor(waveColor);
circlePaint.setColor(bgColor);
}
}
複製代碼
在佈局文件中設置自定義屬性試試效果
<!--省略部分代碼-->
<RelativeLayout xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
<com.anlia.progressbar.CircleBarView app:start_angle="135" app:sweep_angle="270" app:progress_color="@color/red" app:bg_color="@color/gray_light" app:bar_width="20dp"/>
</LinearLayout>
</RelativeLayout>
複製代碼
效果如圖
到這裏咱們的水波浪進度框的基礎框架已經搭建完畢,下面是在這基礎上進行擴展
根據需求,咱們須要顯示能夠隨進度變化的文字,網上許多實現的方法都是在自定義View中實現相應的文字處理邏輯,而後使用canvas.drawText去繪製文字。我我的以爲這樣寫比較麻煩且可擴展性不高,下面提供另一種思路供你們參考
個人作法是將條形進度條和文字顯示區分開來,文字顯示的組件直接在佈局文件用TextView就能夠了,將TextView傳入WaveProgressView,而後在WaveProgressView提供接口編寫文字處理的邏輯便可。這樣實現的好處在於後期咱們要是想改變文字的字體、樣式、位置等等都不須要再在WaveProgressView中傷筋動骨地去改,實現了文字與進度框控件解耦
具體實現以下,修改咱們的WaveProgressView
public class WaveProgressView extends View {
//省略部分代碼...
private TextView textView;
private OnAnimationListener onAnimationListener;
public class WaveProgressAnim extends Animation {
//省略部分代碼...
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
if(percent < progressNum / maxNum){
if(textView !=null && onAnimationListener!=null){
textView.setText(onAnimationListener.howToChangeText(interpolatedTime, progressNum,maxNum));
}
}
}
}
/** * 設置顯示文字的TextView * @param textView */
public void setTextView(TextView textView) {
this.textView = textView;
}
public interface OnAnimationListener {
/** * 如何處理要顯示的文字內容 * @param interpolatedTime 從0漸變成1,到1時結束動畫 * @param updateNum 進度條數值 * @param maxNum 進度條最大值 * @return */
String howToChangeText(float interpolatedTime, float updateNum, float maxNum);
}
public void setOnAnimationListener(OnAnimationListener onAnimationListener) {
this.onAnimationListener = onAnimationListener;
}
}
複製代碼
而後在Activity中調用接口
textProgress = (TextView) findViewById(R.id.text_progress);
waveProgressView.setTextView(textProgress);
waveProgressView.setOnAnimationListener(new WaveProgressView.OnAnimationListener() {
@Override
public String howToChangeText(float interpolatedTime, float updateNum, float maxNum) {
DecimalFormat decimalFormat=new DecimalFormat("0.00");
String s = decimalFormat.format(interpolatedTime * updateNum / maxNum * 100)+"%";
return s;
}
});
waveProgressView.setProgressNum(80,1500);
複製代碼
佈局文件也相應修改
<RelativeLayout android:layout_width="100dp" android:layout_height="100dp" android:layout_gravity="center_horizontal" android:layout_marginTop="10dp">
<com.anlia.progressbar.WaveProgressView android:id="@+id/wave_progress" android:layout_width="match_parent" android:layout_height="match_parent" app:wave_height="8dp" app:wave_width="40dp" app:wave_color="@color/blue_light" app:wave_bg_color="@color/gray_light"/>
<TextView android:id="@+id/text_progress" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_centerHorizontal="true" android:textColor="@color/textColorPrimary" android:textSize="13dp" android:textStyle="bold"/>
</RelativeLayout>
複製代碼
來看下效果
若是已經理解以前所講的波浪繪製以及接口擴展的原理,相信實現起來是很是簡單的,這裏就不詳細解釋了,你們看代碼便可
public class WaveProgressView extends View {
//省略部分代碼...
private Path getWavePath(){
//省略部分代碼...
float changeWaveHeight = waveHeight;
if(onAnimationListener!=null){
changeWaveHeight =
onAnimationListener.howToChangeWaveHeight(percent,waveHeight) == 0 && percent < 1
?waveHeight
:onAnimationListener.howToChangeWaveHeight(percent,waveHeight);
}
//從p3開始向p0方向繪製波浪曲線
for (int i=0;i<waveNum*2;i++){
wavePath.rQuadTo(waveWidth/2, changeWaveHeight, waveWidth, 0);
wavePath.rQuadTo(waveWidth/2, -changeWaveHeight, waveWidth, 0);
}
}
public interface OnAnimationListener {
//省略部分代碼...
/** * 如何處理波浪高度 * @param percent 進度佔比 * @param waveHeight 波浪高度 * @return */
float howToChangeWaveHeight(float percent, float waveHeight);
}
}
複製代碼
而後在Activity中調用接口
waveProgressView.setOnAnimationListener(new WaveProgressView.OnAnimationListener() {
//省略部分代碼...
@Override
public float howToChangeWaveHeight(float percent, float waveHeight) {
return (1-percent)*waveHeight;
}
});
複製代碼
效果如圖
咱們繪製第二層波浪要與第一層波浪平移的方向相反,只須要改一下path的繪製順序就能夠了。即初始點變爲p3,p0至p3段繪製波浪曲線,則繪製順序以下圖(哈哈又是這張圖,重複利用)所示
最後將相應的path繪製到咱們的緩存區便可(注意繪製的前後順序),實現代碼以下
public class WaveProgressView extends View {
//省略部分代碼...
private int secondWaveColor;//第二層波浪顏色
private boolean isDrawSecondWave;//是否繪製第二層波浪
private void init(Context context,AttributeSet attrs){
//省略部分代碼...
secondWaveColor = typedArray.getColor(R.styleable.WaveProgressView_second_wave_color,getResources().getColor(R.color.light));
secondWavePaint = new Paint();
secondWavePaint.setColor(secondWaveColor);
secondWavePaint.setAntiAlias(true);//設置抗鋸齒
//由於要覆蓋在第一層波浪上,且要讓半透明生效,因此選SRC_ATOP模式
secondWavePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
isDrawSecondWave = false;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
bitmap = Bitmap.createBitmap(viewSize, viewSize, Bitmap.Config.ARGB_8888);
bitmapCanvas = new Canvas(bitmap);
bitmapCanvas.drawCircle(viewSize/2, viewSize/2, viewSize/2, circlePaint);
bitmapCanvas.drawPath(getWavePath(),wavePaint);
if(isDrawSecondWave){
bitmapCanvas.drawPath(getSecondWavePath(),secondWavePaint);
}
canvas.drawBitmap(bitmap, 0, 0, null);
}
private Path getSecondWavePath(){
float changeWaveHeight = waveHeight;
if(onAnimationListener!=null){
changeWaveHeight =
onAnimationListener.howToChangeWaveHeight(percent,waveHeight) == 0 && percent < 1
?waveHeight
:onAnimationListener.howToChangeWaveHeight(percent,waveHeight);
}
wavePath.reset();
//移動到左上方,也就是p3點
wavePath.moveTo(0, (1-percent)*viewSize);
//移動到左下方,也就是p2點
wavePath.lineTo(0, viewSize);
//移動到右下方,也就是p1點
wavePath.lineTo(viewSize, viewSize);
//移動到右上方,也就是p0點
wavePath.lineTo(viewSize + waveMovingDistance, (1-percent)*viewSize);
//從p0開始向p3方向繪製波浪曲線(注意繪製二階貝塞爾曲線控制點和終點x座標的正負值)
for (int i=0;i<waveNum*2;i++){
wavePath.rQuadTo(-waveWidth/2, changeWaveHeight, -waveWidth, 0);
wavePath.rQuadTo(-waveWidth/2, -changeWaveHeight, -waveWidth, 0);
}
//將path封閉起來
wavePath.close();
return wavePath;
}
/** * 是否繪製第二層波浪 * @param isDrawSecondWave */
public void setDrawSecondWave(boolean isDrawSecondWave) {
this.isDrawSecondWave = isDrawSecondWave;
}
}
複製代碼
在Activity中設置isDrawSecondWave爲true
waveProgressView.setDrawSecondWave(true);
複製代碼
效果如圖
至此本篇從零開始實現的教程就告一段落了,若是你們看了感受還不錯麻煩點個贊,大家的支持是我最大的動力~要是小夥伴們想要擴展一些新的功能,也能夠在評論區給我留言,我有空會把新功能的實現教程更新上去