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);
}
}