汽水小公舉控件


github: https://github.com/ChanJLee/SodaLady

轉載請註明出處:http://blog.csdn.net/u013022222/article/details/50066095


package com.os.magic.progressbar.soda.view;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;

import com.o.democanvas.R;
import com.os.magic.progressbar.soda.util.Util;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * Created by chan on 15-11-26.
 * 橘子汽水進度條
 * 汽水小公舉
 */
public class OrangeSodaProgressBar extends View {
    ////////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * 
     */
    private static final int COLOR_WHITE = 0xfffde399;
    private static final int COLOR_ORANGE = 0xffffa800;
    /**
     * 振幅
     */
    private static final int AMPLITUDE_MIDDLE = 13;

    /**
     * 振幅差
     */
    private static final int AMPLITUDE_DISPARITY = 5;
    /**
     *  果粒飄動一個週期所花的時間
     */
    private static final long TIME_FLOAT = 3000;
    /**
     * 果粒旋轉一週需要的時間
     */
    private static final long TIME_ROTATE = 2000;
    /**
     *  用於控制繪製的進度條距離左/上/下的距離
     */
    private static final int LEFT_MARGIN = 9;
    /**
     * 用於控制繪製的進度條距離右的距離
     */
    private static final int RIGHT_MARGIN = 25;
    ////////////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 由顏色的畫筆
     */
    private Paint m_whitePaint, m_orangePaint;
    /**
     * 畫出圖片的畫筆
     */
    private Paint m_bitmapPaint;
    /**
     * 當前的進度值
     */
    private int m_currentProgressPosition = 0;
    /**
     * 最大的長度
     */
    private int m_maxLength = 100;
    /**
     * 當前處理到的部分
     */
    private int m_progress = 0;
    /**
     * 左右邊距
     */
    private int m_leftMargin, m_rightMargin;
    /**
     * 白色矩形 橙色矩形 扇形區域
     */
    private RectF m_whiteRectF, m_orangeRectF, m_arcRectF;
    /**
     * 所繪製的進度條部分的寬度
     */
    private int m_progressWidth;
    /**
     * 弧形的半徑
     */
    private int m_arcRadius;

    private int m_totalWidth;
    private int m_totalHeight;

    // arc的右上角的x座標,也是矩形x座標的起始點
    private int m_arcRightLocation;
    /**
     * 果粒集羣
     */
    private List<Pulp> m_pulps;

    private float m_floatDuration = TIME_FLOAT;
    private float m_rotateDuration = TIME_ROTATE;

    private Bitmap m_pulpBitmap;
    private Bitmap m_outRoundedBitmap;
    private int m_pulpWidth;
    private int m_pulpHeight;
    private int m_outerWidth;
    private int m_outerHeight;
    private Rect m_outerSrcRect;
    private Rect m_outerDestRect;
    private Bitmap m_orangeBitmap;
    private int m_orangeWidth, m_orangeHeight;
    private int m_orangeAngle = 0;
    ////////////////////////////////////////////////////////////////////////////////////////////////
    public OrangeSodaProgressBar(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public OrangeSodaProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public OrangeSodaProgressBar(Context context, AttributeSet attrs,
                                 int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context, attrs, defStyleAttr);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * 做一些初始化工作
     *
     * @param context
     * @param attributeSet
     * @param defStyleAttr
     */
    private void init(Context context, AttributeSet attributeSet, int defStyleAttr) {

        initAttribute(context, attributeSet, defStyleAttr);
        initBase();
        initBitmap();
        initPaint();
    }

    /**
     * 初始化屬性
     * @param context
     * @param attributeSet
     * @param defStyleAttr
     */
    private void initAttribute(Context context,AttributeSet attributeSet,int defStyleAttr){

        TypedArray typedArray  = context.obtainStyledAttributes(attributeSet,
                R.styleable.OrangeSodaProgressBar, defStyleAttr, 0);
        try {
            final int length = typedArray.getIndexCount();
            for(int i = 0; i < length; ++i) {
                switch (typedArray.getIndex(i)) {
                    case R.styleable.OrangeSodaProgressBar_maxLength:
                        m_maxLength = typedArray.getInt(i, 100);
                        break;
                }
            }
        } finally {
            if (typedArray != null)
                typedArray.recycle();
        }
    }

    /**
     * 初始基礎部分
     */
    private void initBase() {

        Context context = getContext();

        m_leftMargin = Util.dp2Px(context, LEFT_MARGIN);
        m_rightMargin = Util.dp2Px(context, RIGHT_MARGIN);

        m_pulps = PulpFactory.newInstances(m_floatDuration);
    }

    /**
     * 初始化要顯示的圖片
     */
    @SuppressWarnings("deprecated")
    private void initBitmap(){

        Resources resources = getResources();

        m_pulpBitmap = ((BitmapDrawable)
                resources.getDrawable(R.drawable.pupl)).getBitmap();
        m_pulpWidth = m_pulpBitmap.getWidth();
        m_pulpHeight = m_pulpBitmap.getHeight();

        m_outRoundedBitmap = ((BitmapDrawable)
                resources.getDrawable(R.drawable.bar)).getBitmap();
        m_outerWidth = m_outRoundedBitmap.getWidth();
        m_outerHeight = m_outRoundedBitmap.getHeight();

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = 2;
        options.inJustDecodeBounds = false;
        m_orangeBitmap = BitmapFactory.decodeResource(resources, R.drawable.orange, options);
        m_orangeWidth = m_orangeBitmap.getWidth();
        m_orangeHeight = m_orangeBitmap.getHeight();
    }

    /**
     * 初始化畫刷
     */
    private void initPaint(){

        m_bitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG
                | Paint.DITHER_FLAG | Paint.FILTER_BITMAP_FLAG);

        m_orangePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        m_orangePaint.setColor(COLOR_ORANGE);

        m_whitePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        m_whitePaint.setColor(COLOR_WHITE);
    }

    @Override
    protected void onSizeChanged(int w, int h, int ow, int oh) {
        super.onSizeChanged(w, h, ow, oh);
        initSizeParameter(w, h, ow, oh);
    }

    /**
     * 當大小發生改變時 變更新一下繪製的參數
     * @param newWidth
     * @param newHeight
     * @param prevWidth
     * @param prevHeight
     */
    private void initSizeParameter(int newWidth, int newHeight, int prevWidth, int prevHeight) {

        //算出總的長度
        m_totalWidth = newWidth;
        m_totalHeight = newHeight;

        //計算出進度條實際的長度
        m_progressWidth = m_totalWidth - m_leftMargin - m_rightMargin;
        //本來是扇形的半徑應該是高度的一半減去上下兩個邊界
        m_arcRadius = (m_totalHeight - 2 * m_leftMargin) / 2;

        //繪製圖片
        m_outerSrcRect = new Rect(
                0,
                0,
                m_outerWidth,
                m_outerHeight
        );
        m_outerDestRect = new Rect(
                0,
                0,
                m_totalWidth,
                m_totalHeight
        );

        //繪製顏色
        m_whiteRectF = new RectF(
                m_leftMargin + m_currentProgressPosition,
                m_leftMargin,
                m_totalWidth - m_rightMargin,
                m_totalHeight - m_leftMargin
        );
        m_orangeRectF = new RectF(
                m_leftMargin + m_arcRadius,
                m_leftMargin,
                m_currentProgressPosition
                , m_totalHeight - m_leftMargin
        );
        m_arcRectF = new RectF(
                m_leftMargin,
                m_leftMargin,
                m_leftMargin + 2 * m_arcRadius,
                m_totalHeight - m_leftMargin
        );

        //扇形和舉行的分界點
        m_arcRightLocation = m_leftMargin + m_arcRadius;

        //橘子的高度
        m_orangeBitmapSize = Math.min(Math.min(newHeight,
                m_orangeHeight), m_orangeWidth) - 20;
    }

    private int m_orangeBitmapSize;

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        drawProgressBar(canvas);
        drawBackground(canvas);
        drawOrange(canvas);
        postInvalidate();
    }

    /**
     * 繪製橘子
     * @param canvas
     */
    private void drawOrange(Canvas canvas) {
        m_orangeAngle = (m_orangeAngle + 5) % 360;
        canvas.save();
        Matrix matrix = new Matrix();

        float dx = m_totalWidth - m_totalHeight / 2 - m_orangeWidth / 2;
        float dy = m_totalHeight / 2 - m_orangeHeight / 2;
        matrix.postTranslate(dx, dy);

        float px = dx + m_orangeWidth / 2;
        float py = dy + m_orangeHeight / 2;
        matrix.postRotate(m_orangeAngle, px, py);

        canvas.drawBitmap(m_orangeBitmap, matrix, m_bitmapPaint);
        canvas.restore();
    }
    /**
     * 繪製背景
     * @param canvas
     */
    private void drawBackground(Canvas canvas) {
        canvas.drawBitmap(m_outRoundedBitmap, m_outerSrcRect, m_outerDestRect, m_bitmapPaint);
    }

    //取一個不可能的值
    private float m_previousProgressPosition = -1;

    /**
     * 繪製出進度條的形狀
     * @param canvas
     */
    private void drawProgressBar(Canvas canvas){

        //計算出當前的位置
        m_currentProgressPosition = m_progressWidth * m_progress /
                (m_maxLength == 0 ? 1 : m_maxLength);

        //如果超出長度 那麼就是最長的長度 不能再增加
        if(m_currentProgressPosition > m_progressWidth)
            m_currentProgressPosition = m_progressWidth;

        //如果進度還是在扇形之內
        if(m_currentProgressPosition < m_arcRadius) {

            //首先要畫出所有白色的部分
            canvas.drawArc(m_arcRectF, 90, 180, false, m_whitePaint);
            m_whiteRectF.left = m_arcRightLocation;
            canvas.drawRect(m_whiteRectF, m_whitePaint);

            //之後是橘色的扇形部分
            //計算出sin值
            float sinXValue = (m_arcRadius - m_currentProgressPosition) / m_arcRadius;
            float angleX = (float) Math.asin(sinXValue);
            float sweep = 2 * (90 - angleX);
            float start = 90 + angleX;
            canvas.drawArc(m_arcRectF, start, sweep, false, m_whitePaint);
        }else if(m_currentProgressPosition >= m_arcRadius) {

            //橘色扇形 橘色矩形部分
            canvas.drawArc(m_arcRectF, 90, 180, false, m_orangePaint);
            m_orangeRectF.right = m_currentProgressPosition;
            m_orangeRectF.left = m_arcRightLocation;
            canvas.drawRect(m_orangeRectF,m_orangePaint);

            //畫出白色矩形部分
            m_whiteRectF.left = m_currentProgressPosition;
            canvas.drawRect(m_whiteRectF,m_whitePaint);
        }

//        //記錄一下現在的長度
//        m_previousProgressPosition = m_currentProgressPosition;

        //繪製果粒
        drawPulps(canvas);
    }

    private void drawPulps(Canvas canvas) {
        final int size = m_pulps.size();
        long current = System.currentTimeMillis();

        for (int i = 0; i < size; ++i) {

            Pulp pulp = m_pulps.get(i);
            if(current < pulp.m_startTime) continue;

            calPulpParameter(pulp);

            canvas.save();

            Matrix matrix = new Matrix();

            float dx = m_leftMargin + pulp.m_x;
            float dy = m_leftMargin + pulp.m_y;
            matrix.postTranslate(dx, dy);

            float rotate = pulp.m_rotateAngle;
            float px = dx + m_pulpWidth / 2;
            float py = dy + m_pulpHeight / 2;
            matrix.postRotate(rotate, px, py);

            canvas.drawBitmap(m_pulpBitmap, matrix, m_bitmapPaint);
            canvas.restore();
        }
    }

    /**
     *
     * @param pulp 計算果粒的參數
     */
    private void calPulpParameter(Pulp pulp) {
        long currentTime = System.currentTimeMillis();
        calPulpLocation(pulp, currentTime);
        calPulpRotateAngle(pulp, currentTime);
    }

    /**
     * 計算果粒的旋轉角度
     * @param pulp
     * @param currentTime
     */
    private void calPulpRotateAngle(Pulp pulp,long currentTime){

        // 通過時間關聯旋轉角度
        float rotateFraction = ((currentTime - pulp.m_startTime) % m_rotateDuration)
                / (float) m_rotateDuration;
        int angle = (int) (rotateFraction * 360);
        // 根據葉子旋轉方向確定葉子旋轉角度
        pulp.m_rotateAngle = (pulp.m_rotateOrientation == Pulp.ORIENTATION_LEFT
                ? angle : -angle);
    }

    /**
     * 計算果粒的當前位置
     * @param pulp 果粒
     * @param currentTime 當前的時間
     */
    private void calPulpLocation(Pulp pulp, long currentTime) {

        //獲得時間間隔 如果還沒有準備好要播放 那就不執行下面的代碼
        long intervalTime = currentTime - pulp.m_startTime;
        if (intervalTime < 0) {
            return;
        }

        //如果已經超過了漂浮的時間 就重新計算一次開始的時間 以循環使用果粒
        if (intervalTime > m_floatDuration) {
            pulp.m_startTime = System.currentTimeMillis()
                    + new Random().nextInt((int) m_floatDuration);
            return;
        }

        pulp.m_x = calPulpX(intervalTime);
        pulp.m_y = calPulpY(pulp);

        if(pulp.m_x < m_currentProgressPosition){
            pulp.m_x = m_progressWidth;
        }
    }

    /**
     * 計算果粒的x座標
     * @param intervalTime
     * @return
     */
    private float calPulpX(long intervalTime){
        //計算完成度
        float fraction = (float) intervalTime / m_floatDuration;
        return (m_progressWidth - m_progressWidth * fraction);
    }

    /**
     * 計算果粒的y
     * @param pulp
     * @return
     */
    private float calPulpY(Pulp pulp) {

        // y = A(wx+Q)+h

        float w = (float) ((float) 2 * Math.PI / m_progressWidth);
        float a = AMPLITUDE_MIDDLE;
        switch (pulp.m_type) {
            case Pulp.TYPE_LITTLE:
                // 小振幅 = 中等振幅 - 振幅差
                a = AMPLITUDE_MIDDLE - AMPLITUDE_DISPARITY;
                break;
            case Pulp.TYPE_MID:
                a = AMPLITUDE_MIDDLE;
                break;
            case Pulp.TYPE_HIGH:
                // 大振幅 = 中等振幅 + 振幅差
                a = AMPLITUDE_MIDDLE + AMPLITUDE_DISPARITY;
                break;
            default:
                break;
        }

        //三角函數
        return (int) (a * Math.sin(w * pulp.m_x)) + m_arcRadius * 2 / 3;
    }

    /**
     * 設置
     * @return
     */
    public int getProgress() {
        return m_progress;
    }

    public void setProgress(int progress) {
        m_progress = progress;
    }

    public int getMaxLength() {
        return m_maxLength;
    }

    public void setMaxLength(int maxLength) {
        m_maxLength = maxLength;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 飄動的橘子果粒
     */
    static final private class Pulp{
        ////////////////////////////////////////////////////////////////////////////////////////////
        /**
         * 果粒漂浮時候的幅度 分別爲小中大
         */
        private static final short TYPE_LITTLE = 0x01;
        private static final short TYPE_MID = 0x02;
        private static final short TYPE_HIGH = 0x03;
        /**
         * 左旋
         */
        private static final short ORIENTATION_LEFT = 0x01;
        /**
         * 右旋
         */
        private static final short ORIENTATION_RIGHT = 0x02;
        ////////////////////////////////////////////////////////////////////////////////////////////
        /**
         * 開始的x y 座標
         */
        private float m_x, m_y;
        /**
         * 震動的幅度
         */
        private short m_type;
        /**
         * 旋轉的方向
         */
        private short m_rotateOrientation;
        /**
         * 旋轉的角度
         */
        private int m_rotateAngle;
        /**
         * 開始的時間
         */
        private long m_startTime;
        ////////////////////////////////////////////////////////////////////////////////////////////

        /**
         * @param x 果粒的座標
         * @param y
         * @param type 飄動幅度
         *             {@link OrangeSodaProgressBar.Pulp#TYPE_HIGH}
         *             {@link OrangeSodaProgressBar.Pulp#TYPE_LITTLE}
         *             {@link OrangeSodaProgressBar.Pulp#TYPE_MID}
         * @param rotateOrientation 旋轉方向
         *                          {@link OrangeSodaProgressBar.Pulp#ORIENTATION_LEFT}
         *                          {@link OrangeSodaProgressBar.Pulp#ORIENTATION_RIGHT}
         * @param rotateAngle 旋轉的角度
         * @param startTime 開始的時間
         */
        public Pulp(float x, float y, short type, short rotateOrientation, int rotateAngle, long startTime) {
            m_x = x;
            m_y = y;
            m_type = type;
            m_rotateOrientation = rotateOrientation;
            m_startTime = startTime;
            m_rotateAngle = rotateAngle;
        }
    }

    /**
     * 果粒工廠
     */
    static final private class PulpFactory {
        ////////////////////////////////////////////////////////////////////////////////////////////
        /**
         * 隨機數產生器
         */
        private static final Random RANDOM = new Random();
        /**
         * 默認的果粒個數
         */
        private static final short DEFAULT_SIZE = 0x0006;
        /**
         * 延遲的時間
         */
        private static int s_addTime = 0;
        ////////////////////////////////////////////////////////////////////////////////////////////

        /**
         * 產生一個果粒
         * @param floatTime 果粒飄動的時間
         * @return
         */
        static public Pulp newInstance(float floatTime) {

            //隨機產生果粒飄動的幅度
            short type = Pulp.TYPE_LITTLE;
            switch (RANDOM.nextInt(3)) {
                case 0:
                    type = Pulp.TYPE_LITTLE;
                    break;
                case 1:
                    type = Pulp.TYPE_MID;
                    break;
                case 2:
                    type = Pulp.TYPE_HIGH;
                    break;
            }

            //隨機產生果粒旋轉的方向
            short rotateOrientation = Pulp.ORIENTATION_LEFT;
            switch (RANDOM.nextInt(2)) {
                case 0:
                    rotateOrientation = Pulp.ORIENTATION_LEFT;
                    break;
                case 1:
                    rotateOrientation = Pulp.ORIENTATION_RIGHT;
                    break;
            }

            //旋轉的角度
            int rotateAngle = RANDOM.nextInt(360);

            //播放時候延遲的時間
            s_addTime += RANDOM.nextInt((int) (Math.abs(floatTime) * 2));
            long startTime = s_addTime + System.currentTimeMillis();

            //果粒默認的起始座標爲0 0
            return new Pulp(0, 0, type, rotateOrientation, rotateAngle, startTime);
        }

        /**
         * 產生果粒集合
         * @param count 產生果粒的總個數
         * @param floatTime 果粒飄動的時間
         * @return
         */
        static public List<Pulp> newInstances(int count, float floatTime) {
            List<Pulp> pulpList = new ArrayList<>();
            for (int i = 0; i < count; ++i) {
                pulpList.add(newInstance(floatTime));
            }
            return pulpList;
        }

        /**
         * 產生默認大小的果粒集合
         * @param floatTime
         * @return
         */
        static public List<Pulp> newInstances(float floatTime) {
            List<Pulp> pulpList = new ArrayList<>();
            for (int i = 0; i < DEFAULT_SIZE; ++i) {
                pulpList.add(newInstance(floatTime));
            }
            return pulpList;
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
}


發佈了71 篇原創文章 · 獲贊 106 · 訪問量 22萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章