Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android自定義view-WaveCircle

Android自定義view-WaveCircle

編輯:關於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)動畫沒有時間的設定
以上三點會在後面的博客中一一解決。
最後附上一句”業精於勤而荒於嬉,行成於思而毀於隨“勉勵自己!

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved