自定義時間選擇器

這裏是源碼地址:http://download.csdn.net/detail/h55l55/9521851

首先簡單介紹一下這個選擇器,可以旋轉錶針的方式旋轉選擇時間,然後在下一個視圖中選擇分鐘數。通過兩個接口回調來返回選擇的結果。接下來說一下編程思路:

1、確定需要的顏色常量,以及可以改變值得常量

    /**
     * 默認外圓半徑
     */
    private final static int DEFAULT_OUT_RADIO = 100;
    /**
     * 默認選擇小圓的半徑
     */
    private final static int DEFAULT_CHECK_RAIDO_WIDTH = 20;
    /**
     * 外部文字未選中顏色
     */
    private final static int DEFAULT_OUT_TEXT_UNCHECKED_COLOR = 0xff1f1f1f;
    /**
     * 內部文字未選中顏色
     */
    private final static int DEFAULT_IN_TEXT_UNCHECKED_COLOR = 0xff6d6d6d;
    /**
     * 外部文字選中顏色
     */
    private final static int DEFAULT_OUT_TEXT_CHECKED_COLOR = 0xffffffff;
    /**
     * 內部文字選中顏色
     */
    private final static int DEFAULT_IN_TEXT_CHECKED_COLOR = 0xffffffff;
    /**
     * 連接線條顏色
     */
    private final static int DEFAULT_LINE_COLOR = 0xffff6633;
    /**
     * 外部文字大小
     */
    private final static int DEFAULT_OUT_TEXT_SIZE = 14;
    /**
     * 內部文字大小
     */
    private final static int DEFAULT_IN_TEXT_SIZE = 10;
    /**
     * 時間改變監聽器
     */
    private onHourChangedListener listener;
    /** 選中時候圓圈的半徑 */
    private int checkCircleRadio;
    /** 未選中外部文字顏色 */
    private int mOutUnCheckedColor;
    /** 未選中內部文字顏色 */
    private int mInUncheckedColor;
    /** 外部選中文字顏色 */
    private int mOutTextCheckedColor;
    /** 外部選中文字顏色 */
    private int mInTextCheckedColor;
    /** 線的顏色 */
    private int mLineColor;
    /** 外部文字大小 */
    private int mOutTextSize;
    /** 內部文字大小 */
    private int mInTextSize;
    /** 畫表盤數字的畫筆 */
    private Paint mTextPaint = new Paint();;
    /** 外面圓的半徑 */
    private int mOutRadius;
    /** 內部圓的半徑 */
    private int mInRadius;
    /** 單前選中的小時數 */
    private Integer hours;
    /** 當前視圖的寬度 */
    private int mViewWight;  

2、初始化畫筆的值

在此之前需要重寫構造方法,然後在構造方法中調動此方法。

    /** 給變量賦值,可以使用自定義屬性 */
    private void init() {
        mOutUnCheckedColor = DEFAULT_OUT_TEXT_UNCHECKED_COLOR;
        mInUncheckedColor = DEFAULT_IN_TEXT_UNCHECKED_COLOR;
        mOutTextCheckedColor = DEFAULT_OUT_TEXT_CHECKED_COLOR;
        mLineColor = DEFAULT_LINE_COLOR;
        mOutTextSize = sp2px(getContext(), DEFAULT_OUT_TEXT_SIZE);
        mInTextCheckedColor = DEFAULT_IN_TEXT_CHECKED_COLOR;
        mInTextSize = sp2px(getContext(), DEFAULT_IN_TEXT_SIZE);
        mOutRadius = dp2px(getContext(), DEFAULT_OUT_RADIO);
        checkCircleRadio = dp2px(getContext(), DEFAULT_CHECK_RAIDO_WIDTH);
        mInRadius = mOutRadius - 3 * mOutTextSize;
    }  

3、在Ondraw中開始繪製選擇圈

在onDraw中調用畫圈和畫數字的方法

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.translate(mViewWight / 2, mViewWight / 2);
        drawCircle(canvas);
        drawNumber(canvas);
    }  

畫圈的方法,用來繪製表的指針和選中後的圓圈

    /** 畫選數字的圓圈 */
    private void drawCircle(Canvas canvas) {
        mTextPaint.setTextSize(mOutTextSize);
        if (hours > 12 || hours == 0) {// 13點到00點
            mTextPaint.setColor(mLineColor);
            mTextPaint.setStrokeWidth(checkCircleRadio / 20);
            Point point = getPoint(hours * 30, mInRadius);
            int y = point.y;
            canvas.drawCircle(point.x, y, checkCircleRadio, mTextPaint);
            canvas.drawLine(0, 0, point.x, y, mTextPaint);
        } else {// 1到12點
            mTextPaint.setColor(mLineColor);
            Point point = getPoint(hours * 30, mOutRadius);
            int y = point.y;
            canvas.drawCircle(point.x, y, checkCircleRadio, mTextPaint);
            canvas.drawLine(0, 0, point.x, y, mTextPaint);
        }
        canvas.drawCircle(0, 0, checkCircleRadio / 10, mTextPaint);
    }  

4、在ondraw中開始繪製數字表盤

畫表盤的方法,用來繪製選擇時候的數字

    /** 畫數字表盤 */
    private void drawNumber(Canvas canvas) {
        mTextPaint.setAntiAlias(true);
        mTextPaint.setStyle(Paint.Style.FILL);
        mTextPaint.setTextSize(mOutTextSize);
        // 所繪製文字寬高
        int textWight;
        for (int i = 1; i < 13; i++) {
            if (hours == i) {
                mTextPaint.setColor(mOutTextCheckedColor);
            } else {
                mTextPaint.setColor(mOutUnCheckedColor);
            }
            textWight = (int) mTextPaint.measureText(i + "");
            Point point = getPoint(i * 30, mOutRadius);
            canvas.drawText(i + "", point.x - textWight / 2, 
                    point.y + mOutTextSize / 2, mTextPaint);
        }
        mTextPaint.setTextSize(mInTextSize);
        for (int i = 13; i < 25; i++) {
            if (hours == i) {
                mTextPaint.setColor(mInTextCheckedColor);
            } else {
                mTextPaint.setColor(mInUncheckedColor);
            }
            textWight = (int) mTextPaint.measureText("00");
            String text = i + "";
            if (i == 24) {
                text = "00";
                if (hours == 0) {
                    mTextPaint.setColor(mInTextCheckedColor);
                }
            }
            Point point = getPoint((i - 12) * 30, mInRadius);
            canvas.drawText(text, point.x - textWight / 2,
                     point.y + mInTextSize / 2, mTextPaint);
        }
    }  

getPoint方法,用來獲取當前旋轉角度下的座標

    /** 獲取在角度爲degree,半徑爲radio時候點的座標 */
    private Point getPoint(int degree, int radio) {
        int y = (int) (-Math.cos(2 * Math.PI / 360 * degree) * radio);
        int x = (int) (Math.sin(2 * Math.PI / 360 * degree) * radio);
        return new Point(x, y);
    }  

5、重寫onTouchEvent方法,獲取手指的座標,然後重新繪製,使圓圈移動到指定位置

重寫onTouchEvent方法

    /** 監聽觸摸事件 */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        switch (action) {
        case MotionEvent.ACTION_DOWN:
            hours = getTouchHours(event.getX(), event.getY());
            invalidate();
            break;
        case MotionEvent.ACTION_MOVE:
            // 獲取屬於哪個時間點
            // 更改時間屬性
            hours = getTouchHours(event.getX(), event.getY());
            // 重繪
            invalidate();
            break;
        case MotionEvent.ACTION_UP:
            if (hours < 13) {
                Toast.makeText(getContext(), hours + "", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(getContext(), hours + "", Toast.LENGTH_SHORT).show();
            }
            if (null != listener) {
                listener.onHourChanged(hours, true);
            }
            break;
        }
        return true;
    }  

獲取當前點擊位置對應的時間

    /** 獲取當前觸摸點對應的小時值 */
    private int getTouchHours(float x, float y) {
        x = x - mViewWight / 2;
        y = y - mViewWight / 2;
        for (int i = 0; i < 360; i += 30) {
            if (isInOutCircle(i, x, y)) {
                if (i == 0) {
                    return 12;
                }
                return i / 30;
            }
            if (isInInnerCircle(i, x, y)) {
                return 12 + i / 30 == 12 ? 0 : 12 + i / 30;
            }
        }
        return hours;
    }  

判斷當前點擊位置是否位於指定的時間處

    /** 座標是否落在外部圓上 */
    private boolean isInOutCircle(int angle, float x, float y) {
        // DateUtils.getDayOfWeekString(Calendar.MONDAY, abbrev)
        // 獲取x座標的範圍
        int xx = (int) ((Math.sin(2 * Math.PI / 360 * angle)) * mOutRadius);
        int yy = -(int) ((Math.cos(2 * Math.PI / 360 * angle)) * mOutRadius);
        // (0,-1440)
        // xx-textHeight<x<xx+textHeight
        if (x < xx - checkCircleRadio || x > xx + checkCircleRadio) {
            return false;
        }
        if (yy > 0) {
            if (y < yy - checkCircleRadio || y > yy) {
                return false;
            }
        } else {
            if (y < yy || y > yy + checkCircleRadio) {
                return false;
            }
        }
        return true;
    }
    /** 座標是否落在內部圓上 */
    private boolean isInInnerCircle(int angle, float x, float y) {
        // 獲取x座標的範圍
        int xx = (int) ((Math.sin(2 * Math.PI / 360 * angle)) * mInRadius);
        int yy = -(int) ((Math.cos(2 * Math.PI / 360 * angle)) * mInRadius);
        // xx-textHeight<x<xx+textHeight
        if (x < xx - checkCircleRadio || x > xx + checkCircleRadio) {
            return false;
        }
        if (yy > 0) {
            if (y < yy - checkCircleRadio || y > yy) {
                return false;
            }
        } else {
            if (y < yy || y > yy + checkCircleRadio) {
                return false;
            }
        }
        return true;
    }  

6、在手指擡起時候回調接口方法,把選擇的時間傳遞出去

    /** 設置時間回調監聽器 */
    public void setOnHourChangedListener(onHourChangedListener listener) {
        this.listener = listener;
    }
    /** 當時間改變時候回調 */
    interface onHourChangedListener {
        void onHourChanged(int hour, boolean isUp);
    }  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章