編輯:關於Android編程
先看下最終的效果
靜態:
動態:
一、開始實現
新建一個DoughnutProgress繼承View
public class DoughnutProgress extends View { }
先給出一些常量、變量以及公共方法的代碼,方便理解後面的代碼
private static final int DEFAULT_MIN_WIDTH = 400; //View默認最小寬度 private static final int RED = 230, GREEN = 85, BLUE = 35; //基礎顏色,這裡是橙紅色 private static final int MIN_ALPHA = 30; //最小不透明度 private static final int MAX_ALPHA = 255; //最大不透明度 private static final float doughnutRaduisPercent = 0.65f; //圓環外圓半徑占View最大半徑的百分比 private static final float doughnutWidthPercent = 0.12f; //圓環寬度占View最大半徑的百分比 private static final float MIDDLE_WAVE_RADUIS_PERCENT = 0.9f; //第二個圓出現時,第一個圓的半徑百分比 private static final float WAVE_WIDTH = 5f; //波紋圓環寬度 //圓環顏色 private static int[] doughnutColors = new int[]{ Color.argb(MAX_ALPHA, RED, GREEN, BLUE), Color.argb(MIN_ALPHA, RED, GREEN, BLUE), Color.argb(MIN_ALPHA, RED, GREEN, BLUE)}; private Paint paint = new Paint(); //畫筆 private float width; //自定義view的寬度 private float height; //自定義view的高度 private float currentAngle = 0f; //當前旋轉角度 private float raduis; //自定義view的最大半徑 private float firstWaveRaduis; private float secondWaveRaduis; // private void resetParams() { width = getWidth(); height = getHeight(); raduis = Math.min(width, height)/2; } private void initPaint() { paint.reset(); paint.setAntiAlias(true); }
重寫onMeasure方法,為什麼要重寫onMeasure方法可以看我的上一篇文章,點這裡
/** * 當布局為wrap_content時設置默認長寬 * * @param widthMeasureSpec * @param heightMeasureSpec */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(measure(widthMeasureSpec), measure(heightMeasureSpec)); } private int measure(int origin) { int result = DEFAULT_MIN_WIDTH; int specMode = MeasureSpec.getMode(origin); int specSize = MeasureSpec.getSize(origin); if (specMode == MeasureSpec.EXACTLY) { result = specSize; } else { if (specMode == MeasureSpec.AT_MOST) { result = Math.min(result, specSize); } } return result; }
下面就是最重要的重寫onDraw方法,大致流程如下
在開始繪制之前,先初始化width、height、raduis, 以及將View的中心作為原點
resetParams(); //將畫布中心設為原點(0,0), 方便後面計算坐標 canvas.translate(width / 2, height / 2);
實現靜態的漸變圓環
1、畫漸變圓環
float doughnutWidth = raduis * doughnutWidthPercent;//圓環寬度 //圓環外接矩形 RectF rectF = new RectF( -raduis * doughnutRaduisPercent, -raduis * doughnutRaduisPercent, raduis * doughnutRaduisPercent, raduis * doughnutRaduisPercent); initPaint(); paint.setStrokeWidth(doughnutWidth); paint.setStyle(Paint.Style.STROKE); paint.setShader(new SweepGradient(0, 0, doughnutColors, null)); canvas.drawArc(rectF, 0, 360, false, paint);
通過修改doughnutColors可以實現不同的漸變效果
2、畫圓環旋轉頭部的圓
//畫旋轉頭部圓 initPaint(); paint.setStyle(Paint.Style.FILL); paint.setColor(Color.argb(MAX_ALPHA, RED, GREEN, BLUE)); canvas.drawCircle(raduis * doughnutRaduisPercent, 0, doughnutWidth / 2, paint);
此時運行代碼得到效果如下圖:
我們還可以在繪制圓環之前通過旋轉畫布得到不同初始狀態
canvas.rotate(-45, 0, 0);
canvas.rotate(-180, 0, 0);
此時聰明的你應該已經想到怎麼讓這個圓環旋轉起來了吧^_^
對!正如你所想的,就是通過canvas.rotate方法不停地旋轉畫布(這個“地”是這麼用的吧o(╯□╰)o)
讓圓環旋轉起來
在繪制圓環之前加上下面的代碼:
//轉起來 canvas.rotate(-currentAngle, 0, 0); if (currentAngle >= 360f){ currentAngle = currentAngle - 360f; } else{ currentAngle = currentAngle + 2f; }
然後再讓一個線程循環刷新就好了
private Thread thread = new Thread(){ @Override public void run() { while(true){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } postInvalidate(); } } };
試試!轉起來了嗎O(∩_∩)O~
下面是比較有意思的部分,實現類似水波漣漪的效果
分析水波漣漪效果的實現原理(畫了張草圖方便理解):
假設淡黃色背景區域為整個View的大小
黑色圓圈為View內的最大圓(半徑為R3)
橙色圓環代表漸變圓環
紅色圓圈代表圓環的外圓(半徑為R1)
紫色圓圈是干啥子的,待會兒再介紹~(半徑為R2)
通過觀察實現的最終效果,可以發現有個圓的半徑從R1逐漸增大R3,不透明度逐漸減小到0。
那是不是這樣周而復始就可以實現最終的效果了呢?
沒那麼簡單。。。
仔細觀察發現,第二個圓不是等到第一個圓的半徑增大到R3才開始出現的,而是在將要消失的時候就出現了,有一段時間是兩個圓同時存在的。
那麼我們就假設當第一個圓的半徑增大到R2,第二個圓開始出現。
開始想象兩個圓的循環運行模型~~~
我的方案是:
繪制兩個圓,每個圓的半徑都從R1增大到R1+2x(R2-R1),不透明度還是從R1到R3的過程中逐漸變為0,也就是當圓的半徑大於R3時,不透明度就為0了(不可見了),將第一個圓半徑初始值設為R1,第二個圓半徑初始值設為R2。這樣兩個圓半徑同時逐漸增大,當半徑大於 R1+2x(R2-R1)時又重新回到R1大小繼續增大,就實現了類似水波漣漪的效果了。
//實現類似水波漣漪效果 initPaint(); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(5); secondWaveRaduis = calculateWaveRaduis(secondWaveRaduis); firstWaveRaduis = calculateWaveRaduis(secondWaveRaduis + raduis*(MIDDLE_WAVE_RADUIS_PERCENT - doughnutRaduisPercent) - raduis*doughnutWidthPercent/2); paint.setColor(Color.argb(calculateWaveAlpha(secondWaveRaduis), RED, GREEN, BLUE)); canvas.drawCircle(0, 0, secondWaveRaduis, paint); //畫第二個圓(初始半徑較小的) initPaint(); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(5); paint.setColor(Color.argb(calculateWaveAlpha(firstWaveRaduis), RED, GREEN, BLUE)); canvas.drawCircle(0, 0, firstWaveRaduis, paint); //畫第一個圓(初始半徑較大的) /** * 計算波紋圓的半徑 * @param waveRaduis * @return */ private float calculateWaveRaduis(float waveRaduis){ if(waveRaduis < raduis*doughnutRaduisPercent + raduis*doughnutWidthPercent/2){ waveRaduis = raduis*doughnutRaduisPercent + raduis*doughnutWidthPercent/2; } if(waveRaduis > raduis*MIDDLE_WAVE_RADUIS_PERCENT + raduis*(MIDDLE_WAVE_RADUIS_PERCENT - doughnutRaduisPercent) - raduis*doughnutWidthPercent/2){ waveRaduis = waveRaduis - (raduis*MIDDLE_WAVE_RADUIS_PERCENT + raduis*(MIDDLE_WAVE_RADUIS_PERCENT - doughnutRaduisPercent) - raduis*doughnutWidthPercent/2) + raduis*doughnutWidthPercent/2 + raduis*doughnutRaduisPercent; } waveRaduis += 0.6f; return waveRaduis; } /** * 根據波紋圓的半徑計算不透明度 * @param waveRaduis * @return */ private int calculateWaveAlpha(float waveRaduis){ float percent = (waveRaduis-raduis*doughnutRaduisPercent-raduis*doughnutWidthPercent/2)/(raduis-raduis*doughnutRaduisPercent-raduis*doughnutWidthPercent/2); if(percent >= 1f){ return 0; }else{ return (int) (MIN_ALPHA*(1f-percent)); } }
以上就是本文的全部內容,希望對大家的學習Android軟件編程有所幫助。
最近開發App,美工設計了一個有鋸齒邊沿效果的背景圖,只給了我一個鋸齒,然後需要平鋪展示鋸齒效果: android中實現平鋪圖片有兩種方式:(1)在drawable中的d
這篇我們來介紹一下享元模式(Flyweight Pattern),Flyweight 代表輕量級的意思,享元模式是對象池的一種實現。享元模式用來盡可能減少內存使用量,它適
演示效果主要代碼如下自定義的一個EditText,用於實現有文字的時候顯示可以清楚的按鈕:import android.content.Context;import an
Android的安全機制包括以下幾個方面: • 進程沙箱隔離機制。 • 應用程序簽名機制。 • 權限聲明機制。 • 訪問