編輯:關於Android編程
首先上圖:

如何達到該View的效果?波浪如何繪制?
(1)在實現該View之前,先要了解Xfermode
什麼是Xfermode:稱之為圖像混合模式,Android官方給了張圖來解釋

也就是圖像的集合操作
其中我們先繪制Dst,然後給Paint設置Xfermode模式,然後在繪制Src,呈現的效果就如上圖所示
幾個常用的
SrcIn:取兩層交集,顯示上層的,DstIn與之相反
SrcOut:取上層圖像的非交集部分,DstOut與之相反等等
那麼來看我們實現該View的思路:
這種效果可以使用PZ喎?/kf/ware/vc/" target="_blank" class="keylink">vcnRlckR1ZmYuTW9kZS5TUkNfSU7Eo8q9o6zSsr7NysfIocG9suO9u7yvo6zP1Mq+yc+y47XEo6y8tLvhz8i75tbG0ru49tSyo6wgyLu688no1sNYZmVybW9kZbXExKPKvc6qU3JjSW4syLu689Tau+bWxtK7uPayqMDLo6zKucbk0+vUss/gvbujrLK7ts+1xNLGtq+75tbGtcSyqMDLvs2/ydLUtO+1vbKotq+1xNCnufujrM2syrG9q7KowMvP8snP0sa2r6Os0ru0ztPQy67B99bwvaXJz8n9tcTQp7n7oaM8YnIgLz4NCsbk1tDKudPDtb21xNaqyrbT0DxiciAvPg0Ko6gxo6lYZmVybW9kZTxiciAvPg0Ko6gyo6nKudPDSGFuZGxlcr/Y1sa2r7utwt+8rTxiciAvPg0Ko6gzo6nKudPDsbTI+7b7x/rP38q1z9ayqMDL0Ke5+zxiciAvPg0KwLS/tNStwO3NvKO6PGJyIC8+DQo8aW1nIGFsdD0="這裡寫圖片描述" src="/uploadfile/Collfiles/20160727/20160727104914400.png" title="\" />
(1)使用Path的貝塞爾曲線方法rQuadTo構建波浪曲線,代碼如下:
mPath.reset();
mPath.moveTo(-2 * radius + count, 2 * radius - progress);
mPath.rQuadTo(radius / 2, A, radius, 0);
mPath.rQuadTo(radius / 2, -A, radius, 0);
mPath.rQuadTo(radius / 2, A, radius, 0);
mPath.rQuadTo(radius / 2, -A, radius, 0);
mPath.lineTo(2 * radius + count, 2 * radius);
mPath.lineTo(-2 * radius + count, 2 * radius);
mPath.close();
可以看到使用Path的rQuadTo方法來構建波浪,最後使其封閉,其中rQuadTo的方法相對於前一個點的步進,類似scrollby。在構建完波浪後,我們通過不停的mPath.moveTo,使波浪超前移動,在與圓相交後顯示出來,這裡注意,波浪與圓的位置關系,波浪在圓的正下方並且有兩個完整的波,這樣當我們moveTo到一個波浪時,我們在講其moveTo到原始的位置,波浪有回到了遠點,就源源不斷的流下來了。
代碼如下,當波浪的步進移動了一個完整的波時,就置為為0,讓其回到遠點
if (count >= 2 * radius) {
count = 0;
}
(2)Xfermode的使用:代碼如下:
xfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//繪制圓
mPaint.setColor(circleColor);
canvas.drawCircle(radius, radius, radius, mPaint);
//設置Xfermode
mPaint.setXfermode(xfermode);
resetPath2();
mPaint.setColor(textColor);
//畫波浪
canvas.drawPath(mPath, mPaint);
mPaint.setXfermode(null);
if (A < radius / 4) {
textPaint.setAlpha(getAlpha(A));
canvas.drawText(mTragetValue, radius - mTextWidth / 2, radius + mTextHeight / 2, textPaint);
}
}
(3)動畫邏輯的判斷:
使用Handler的sendEmptyMessage方法,通過在Handler的handleMessage方法中sendEmptyMessage(0);以此達到不停回調的效果,注意Handler的退出條件
完整的代碼,有詳細注釋,希望大家能學到知識,共同進步
public class CustomCircle extends View {
/**
* 退出Handler的MSG
*/
public static final int EXIT_FLAG = 1;
/**
* 進行動畫的的MSG
*/
public static final int GOON_FLAG = 0;
public static final int MAX_PROGRESS = 100;
/**
* 圓的半徑
*/
private int radius;
private Paint mPaint;
private Path mPath;
private Paint textPaint;
/**
* 圖像混合模式
*/
private Xfermode xfermode;
/**
* 顏色
*/
private int textColor;
private int circleColor;
private int progress = 0;
//y = Asin(bx+c);
public static final int CIRCLE_DEGREE = 180;
/**
* 振幅
*/
private int A = 50;
private double[] pis = new double[360];
/**
* 要達到的進度
*/
private String mTragetValue = "";
/**
* 波浪前進的step
*/
private int count = 0;
/**
* 真實的進度
*/
private int mActualHei;
private int mTextWidth;
private int mTextHeight;
private int alpha;
public LoadingView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.loadingCircle, 0, 0);
int count = typedArray.getIndexCount();
int attr = 0;
for (int i = 0; i < count; i++) {
attr = typedArray.getIndex(i);
switch (attr) {
case R.styleable.loadingCircle_radius:
radius = typedArray.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, getResources().getDisplayMetrics()));
break;
case R.styleable.loadingCircle_circleColor:
circleColor = typedArray.getColor(attr, Color.BLUE);
break;
case R.styleable.loadingCircle_textColor:
textColor = typedArray.getColor(attr, Color.BLACK);
break;
}
}
typedArray.recycle();
textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
textPaint.setTextSize(radius / 3);
textPaint.setStyle(Paint.Style.FILL);
textPaint.setColor(Color.WHITE);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Paint.Style.FILL);
//設置圖像的混合模式
xfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
mPath = new Path();
//關閉硬件加速,對Xfermode有影響
setLayerType(LAYER_TYPE_SOFTWARE, null);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//繪制圓
mPaint.setColor(circleColor);
canvas.drawCircle(radius, radius, radius, mPaint);
//設置Xfermode
mPaint.setXfermode(xfermode);
resetPath2();
mPaint.setColor(textColor);
//畫波浪
canvas.drawPath(mPath, mPaint);
mPaint.setXfermode(null);
if (A < radius / 4) {
textPaint.setAlpha(getAlpha(A));
canvas.drawText(mTragetValue, radius - mTextWidth / 2, radius + mTextHeight / 2, textPaint);
}
}
//字體淡出的透明度
private int getAlpha(int a) {
if (a<1){
return 255;
}
int b = radius / 4;
int temp = (b - a) * 255 / b;
return temp;
}
//測量字體的寬高
private void getTextWidthOrHei(String msg) {
Rect rect = new Rect();
textPaint.getTextBounds(msg, 0, msg.length(), rect);
mTextWidth = rect.width();
mTextHeight = rect.height();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
//測量
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
MeasureSpec.getMode(widthMeasureSpec);
MeasureSpec.getSize(widthMeasureSpec);
//測量,強制view的大小為園的大小
setMeasuredDimension(2 * radius, 2 * radius);
}
/**
* 構造我們的path,使用的是貝塞爾曲線
*/
private void resetPath2() {
if (mActualHei <= 0) {
return;
}
mPath.reset();
mPath.moveTo(-2 * radius + count, 2 * radius - progress);
mPath.rQuadTo(radius / 2, A, radius, 0);
mPath.rQuadTo(radius / 2, -A, radius, 0);
mPath.rQuadTo(radius / 2, A, radius, 0);
mPath.rQuadTo(radius / 2, -A, radius, 0);
mPath.lineTo(2 * radius + count, 2 * radius);
mPath.lineTo(-2 * radius + count, 2 * radius);
mPath.close();
}
/**
* 控制動畫的邏輯
*/
private Handler mPathHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
//退出信號
if (msg.what == EXIT_FLAG) {
return;
}
//如果振幅<0,代表動畫的結束
if (A < 0) {
removeMessages(GOON_FLAG);
return;
}
//在進度達到設定的進度時,振幅開始減小
if (progress >= mActualHei) {
A--;
} else {
//波浪的步進,通過moveTo達到移動效果
count += 5;
//波浪高度的步進
progress += 1;
//如果已近移動了一個完整的波浪,那麼時時候回到原點了
if (count >= 2 * radius) {
count = 0;
}
}
//更新
invalidate();
sendEmptyMessage(0);
}
};
//停止動畫
private void stopAnimator() {
Message msg = mPathHandler.obtainMessage(EXIT_FLAG);
mPathHandler.sendMessageAtFrontOfQueue(msg);
mPathHandler.removeMessages(GOON_FLAG);
progress = 0;
count = 0;
A = radius / 4;
}
//開始動畫
public void animatorStart() {
stopAnimator();
mPathHandler.sendEmptyMessage(0);
}
//設置比例
public void setTargetValue(int target) {
mTragetValue = String.valueOf(target);
mActualHei = (int) (2 * radius * (target * 1.f / MAX_PROGRESS));
getTextWidthOrHei(mTragetValue);
}
}
不足之處:
(1)View的測量太粗暴,沒能考慮充分考慮父view的意願。
(2)不支持padding
(3)動畫沒有時間的設定
以上三點會在後面的博客中一一解決。
最後附上一句”業精於勤而荒於嬉,行成於思而毀於隨“勉勵自己!
java/android 設計模式學習筆記(17)---策略模式
這篇博客我們來介紹一下策略模式(Strategy Pattern,或者叫 Policy Pattern),也是行為型模式之一。通常在軟件開發中,我們為了一個功能可能會設計
Android Studio簡單設置
1.界面設置默認的 Android Studio 為灰色界面,可以選擇使用炫酷的黑色界面。Settings --> Appearance --> Theme
Android錯誤信息的匯總
犯過的錯給自己提個醒 【錯誤信息】 [2011-01-19 16:39:10 - ApiDemos] WARNING: Application does not spec
Android學習筆記二十八之AlertDialog提示對話框和幾個常用的提示框
在我們實際開發中,常常需要有對話框彈出跟用戶交互。AndroidOS提供有多種對話框,這一節,我們介紹一下AlertDialog和幾個常用Dialog,AlertDial