自定义时间选择器

这里是源码地址: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);
    }  
<script type="text/javascript"> $(function () { $('pre.prettyprint code').each(function () { var lines = $(this).text().split('\n').length; var $numbering = $('<ul/>').addClass('pre-numbering').hide(); $(this).addClass('has-numbering').parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($('<li/>').text(i)); }; $numbering.fadeIn(1700); }); }); </script>
### 回答1: WPF是一种强大的UI框架,它提供了许多控件,其中包括时间选择器控件。但是,由于在特定的应用程序中,需要自定义时间选择器以满足一些个性化需求。在这种情况下,可以使用WPF自定义控件来创建自己的时间选择器控件。 首先,在WPF中创建时间选择器控件,需要使用Calendar控件和TimePicker控件。Calendar控件用于显示日期,而TimePicker控件用于选择时间时间选择器控件的主体是StackPanel控件。在StackPanel控件中添加了两个控件Calendar和TimePicker,以实现时间选择器的基本功能。 然后,需要在时间选择器控件中定义一些附加属性,例如:选定日期、选定时间等等,以实现一些高级功能。 最后,为时间选择器控件添加样式,并实现一些触发器和动画效果,以使其外观和功能与应用程序的主题相匹配。 实现WPF自定义时间选择器可能需要一些时间和经验,但对于需要一个不寻常的时间选择器的应用程序来说,是值得的。 这样的时间选择器是用户友好的,具有很好的设计和功能,并且以C#编写,可以很容易地与WPF应用程序集成。 ### 回答2: WPF自定义时间选择器是一种功能强大、灵活性高的工具,它可以根据需求自行设计不同的选择器,可以实现小时、分钟、秒数的选择等多种功能。 首先,我们需要使用WPF自带的DatePicker控件和TimePicker控件来实现时间选择器。接下来,我们可以自定义控件的样式和模板,使其更符合我们的设计需求。 在自定义控件的样式时,我们需要设置控件的各个属性,比如控件的边框、背景、字体等。同时,我们可以通过设置样式来调整控件的布局和显示效果。 在时间选择器的实现中,需要涉及到一些比较复杂的计算,比如计算时间的差值、时间的格式转换等。我们可以使用C#中的DateTime类和TimeSpan类来实现这些功能。 最后对于自定义时间选择器的控件事件,需要自定义一些控件事件,使其更加符合我们的设计需求。比如增加或减少系统时钟里的时间。 总而言之,实现WPF自定义时间选择器需要对WPF控件、样式、模板、计算和控件事件等各个方面有深入的了解。只有掌握了这些知识,才能够设计出优秀的时间选择器,满足用户的需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值