編輯:關於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)動畫沒有時間的設定
以上三點會在後面的博客中一一解決。
最後附上一句”業精於勤而荒於嬉,行成於思而毀於隨“勉勵自己!
這篇博客我們來介紹一下策略模式(Strategy Pattern,或者叫 Policy Pattern),也是行為型模式之一。通常在軟件開發中,我們為了一個功能可能會設計
1.界面設置默認的 Android Studio 為灰色界面,可以選擇使用炫酷的黑色界面。Settings --> Appearance --> Theme
犯過的錯給自己提個醒 【錯誤信息】 [2011-01-19 16:39:10 - ApiDemos] WARNING: Application does not spec
在我們實際開發中,常常需要有對話框彈出跟用戶交互。AndroidOS提供有多種對話框,這一節,我們介紹一下AlertDialog和幾個常用Dialog,AlertDial