仿Timely时间选择控件

Timely简介

焕彩闹钟是一款专为Android设计的华丽时钟,这款软件拥有惊艳的用户体验和革命性的云服务支持,允许对闹钟数据的备份从而在不同设备之间同步。
设置闹钟从未如此简单:只需从屏幕的边缘滑过,再通过拖动便可设置所需的闹钟时间。单次点击可进行以5分钟为单位的微调。
舒心的叫醒体验:精心打造的高品质闹铃和创新的智能叫醒功能,让您在早上醒来时神清气爽。
您的品味,您的风格:从我们绚丽的色彩主题中选择符合您的品味和风格, 或使用我们的设计工具来创造新的主题。您同样也可选择多个由设计师精心制作的时钟样式。
从此再也不迟到:新颖的解除闹钟方式确保您在解除闹钟时确实清醒。按下按钮不够炫酷?翻转手机来推迟闹钟。此外,当您拿起您的手机时,焕彩闹钟将自动减小闹钟音量来减轻闹钟音量。

说了这么多,其实也就是一个好看的那种而已,下面来展示一个她美丽的时间设置界面

时间设置界面
设置好之后的界面

效果展示

自定义控件的效果如下,虽然没有完全实现,但是大体的效果也是差不多的,这里最小的单位我设置的10分,没有具体到每分钟
这里写图片描述

理清思路

刚开始也是走了一些弯路的,想通过一个类似于水果手机中闹钟时间选择的效果来编写,但是搞了一天发现实现的效果不是很好,在滑动的时候,整个界面都会跟着动,最后只好放弃,换一个思路,通过类似于ProcessBar的方式来编写,那么什么是ProcessBar,就是下面的效果。
这里写图片描述

就是这样一个调节音量的效果了,很easy,我们得到滑块的位置,然后根据其所占的比例进行时间的换算,最后进行重新的绘制就可以了。

代码分析

这里最关键的就是两个方法了,一个是触摸事件相应的方法,一个是界面绘制的方法。

触摸时间相应

首先进行边界的判断,然后根据触摸的位置,做出相应并调用setProgress方法,我们在这个方法中调用invalidate()进行UI界面的重新绘制。

    public boolean onTouchEvent(MotionEvent event) {
        if (event.getY() < pointRadius) {
            setProgress(PROGRESS_MIN);
            return true;
        } else if (event.getY() > getHeight() - pointRadius) {
            setProgress(PROGRESS_MAX);
            return true;
        } else {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    setProgress(calculProgress(event.getY()));
                    return true;
                case MotionEvent.ACTION_MOVE:
                    setProgress(calculProgress(event.getY()));
                    return true;
            }
        }
        return super.onTouchEvent(event);
    }
UI界面的绘制

这里我把数据写死了,其实完全可以将数据进行一下封装,使用接口的方式从外面传进来,UI界面的绘制还要注意屏幕适配的问题,代码的上面是时间轴效果的绘制,中间是一个简单动画效果的实现,下面是时间效果的绘制效果。

    public void draw(Canvas canvas) {
        Log.d(TAG, "[draw] .. in .. ");
        super.draw(canvas);

        linePaint = new Paint();
        linePaint.setAntiAlias(true);
        linePaint.setStyle(Paint.Style.FILL);
        linePaint.setStrokeWidth(lineHeight);
        linePaint.setColor(getResources().getColor(lineClor));

        String[] seq = new String[]{"-00", "-01", "-02", "-03", "-04", "-05", "-06", "-07", "-08",
                "-09", "-10", "-11", "-12", "-13", "-14", "-15", "-16", "-17",
                "-18", "-19", "-20", "-21", "-22", "-23", "-24"};


        //因为是以画布Canvas 为draw对象,所以RectF构造函数内的参数是以canvas为边界,而不是屏幕
        //画线以及动画效果的实现
        for(int i = 0; i < seq.length; i++){
            if(i < mThisHour-3 || i > mThisHour+3) {
                linePaint.setTextSize(30);
                canvas.drawText(seq[i], getWidth() / 2, getHeight() / 24 * (24 - i), linePaint);
            }else if(i == mThisHour+3 || i == mThisHour-3){
                linePaint.setTextSize(35);
                canvas.drawText(seq[i], getWidth() / 2 + 10, getHeight() / 24 * (24 - i), linePaint);
            }else if(i == mThisHour+2 || i == mThisHour-2){
                linePaint.setTextSize(40);
                canvas.drawText(seq[i], getWidth() / 2 + 20, getHeight() / 24 * (24 - i), linePaint);
            }else if(i == mThisHour+1 || i == mThisHour-1){
                linePaint.setTextSize(45);
                canvas.drawText(seq[i], getWidth() / 2 + 30, getHeight() / 24 * (24 - i), linePaint);
            }else{
                linePaint.setTextSize(50);
                canvas.drawText(seq[i], getWidth() / 2 + 40, getHeight() / 24 * (24 - i), linePaint);
            }
        }


        //时间效果的实现
        pointPaint = new Paint();
        pointPaint.setAntiAlias(true);
        pointPaint.setStyle(Paint.Style.FILL);
        pointPaint.setColor(getResources().getColor(pointColor));
        pointPaint.setTextSize(100);
        canvas.drawText(progressToTime(100 - progress),getWidth()/2+200, getCx()+10, pointPaint);

    }

可能通过上面的两个方法还不能完全理解,下面是自定义控件的完整代码,也可以从最下面的链接下载,直接运行查看效果即可

package com.iigt.myscoleftview.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import com.iigt.myscoleftview.R;

/**
 * Created by zhouheng on 2017/3/28.
 */

public class MyScrollView extends View {
    private static final String TAG = "TouchProgressView";

    private Paint linePaint;
    private Paint pointPaint;

    private int pointRadius = 10;//圆点默认半径,单位px
    private int pointColor = R.color.gray_dft;//圆点默认颜色

    private int lineHeight = 2;//线默认高度,单位px
    private int lineClor = R.color.gray_dft;//线默认颜色

    private int progress = 0;
    private final int PROGRESS_MIN = 0;
    private final int PROGRESS_MAX = 100;

    private OnProgressChangedListener progressChangedListener;
    private int mThisHour;
    private int mThisMin;

    public interface OnProgressChangedListener {
        void onProgressChanged(View view, int progress);
    }

    public MyScrollView(Context context) {
        super(context, null);
    }

    public MyScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

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

    /**
     * 设置百分比
     *
     * @param progress
     */
    public void setProgress(int progress) {
        if (progress < 0 || progress > 100) {
            throw new IllegalArgumentException("progress 不可以小于0 或大于100");
        }
        this.progress = progress;
        invalidate();

        if (progressChangedListener != null) {
            progressChangedListener.onProgressChanged(this, 100-progress);
        }
    }

    /**
     * 设置进度变化监听器
     *
     * @param onProgressChangedListener
     */
    public void setOnProgressChangedListener(OnProgressChangedListener onProgressChangedListener) {
        this.progressChangedListener = onProgressChangedListener;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getY() < pointRadius) {
            setProgress(PROGRESS_MIN);
            return true;
        } else if (event.getY() > getHeight() - pointRadius) {
            setProgress(PROGRESS_MAX);
            return true;
        } else {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    setProgress(calculProgress(event.getY()));
                    return true;
                case MotionEvent.ACTION_MOVE:
                    setProgress(calculProgress(event.getY()));
                    return true;
            }
        }
        return super.onTouchEvent(event);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    public void draw(Canvas canvas) {
        Log.d(TAG, "[draw] .. in .. ");
        super.draw(canvas);

        linePaint = new Paint();
        linePaint.setAntiAlias(true);
        linePaint.setStyle(Paint.Style.FILL);
        linePaint.setStrokeWidth(lineHeight);
        linePaint.setColor(getResources().getColor(lineClor));

        String[] seq = new String[]{"-00", "-01", "-02", "-03", "-04", "-05", "-06", "-07", "-08",
                "-09", "-10", "-11", "-12", "-13", "-14", "-15", "-16", "-17",
                "-18", "-19", "-20", "-21", "-22", "-23", "-24"};


        //因为是以画布Canvas 为draw对象,所以RectF构造函数内的参数是以canvas为边界,而不是屏幕
        //画线以及动画效果的实现
        for(int i = 0; i < seq.length; i++){
            if(i < mThisHour-3 || i > mThisHour+3) {
                linePaint.setTextSize(30);
                canvas.drawText(seq[i], getWidth() / 2, getHeight() / 24 * (24 - i), linePaint);
            }else if(i == mThisHour+3 || i == mThisHour-3){
                linePaint.setTextSize(35);
                canvas.drawText(seq[i], getWidth() / 2 + 10, getHeight() / 24 * (24 - i), linePaint);
            }else if(i == mThisHour+2 || i == mThisHour-2){
                linePaint.setTextSize(40);
                canvas.drawText(seq[i], getWidth() / 2 + 20, getHeight() / 24 * (24 - i), linePaint);
            }else if(i == mThisHour+1 || i == mThisHour-1){
                linePaint.setTextSize(45);
                canvas.drawText(seq[i], getWidth() / 2 + 30, getHeight() / 24 * (24 - i), linePaint);
            }else{
                linePaint.setTextSize(50);
                canvas.drawText(seq[i], getWidth() / 2 + 40, getHeight() / 24 * (24 - i), linePaint);
            }
        }


        //时间效果的实现
        pointPaint = new Paint();
        pointPaint.setAntiAlias(true);
        pointPaint.setStyle(Paint.Style.FILL);
        pointPaint.setColor(getResources().getColor(pointColor));
        pointPaint.setTextSize(100);
        canvas.drawText(progressToTime(100 - progress),getWidth()/2+200, getCx()+10, pointPaint);

    }


    /**
     * 将百分比转换成时间
     * @param progress 输入的百分比
     * @return 返回对应的时间
     */
    private String progressToTime(float progress){
        float myMin = (6.0f * 24.0f) / 100;
        float thisTime = progress * myMin;

        mThisHour = ((int)thisTime) / 6;
        mThisMin = ((int)thisTime) % 6;


        return Integer.toString(mThisHour) + ": "+ Integer.toString(mThisMin) + "0";
    }

    /**
     * 获取圆点的x轴坐标
     *
     * @return
     */
    private float getCx() {
        float cx = 0.0f;
        cx = (getHeight() - pointRadius * 2);
        if (cx < 0) {
            throw new IllegalArgumentException("TouchProgressView 宽度不可以小于 2 倍 pointRadius");
        }
        return cx / 100 * (progress) + pointRadius;
    }

    /**
     * 计算触摸点的百分比
     *
     * @param eventX
     * @return
     */
    private int calculProgress(float eventX) {
        float proResult = (eventX - pointRadius) / (getHeight() - pointRadius * 2);
        return (int) (proResult * 100);
    }

}

Demo下载地址:

https://github.com/jinhuoxingkong/MyScrollView
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值