用小米的手机,发现其实还可以,无意间点开小米的计时器,发现界面非常好看和实用。于是自己仿照着写一个,由于技术不好,代码整体结构上可能有点乱,但主要的实现功能和掌握知识点。

Android中绘制采用canvase绘图类,加上timer计时器和handler来更新UI,核心就这点东西,非常简单。废话不多说,先附上效果图。

和小米自带的还是有差距的,主要是运行的时候指针的转动感觉没小米的那么流畅。这一点还在优化中。直接上代码,所有的注视都写上了,没什么好说的。

主页面xml文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#E0EEEE"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" > <LinearLayout
android:id="@+id/main_view"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_weight="5"
android:orientation="horizontal" >
</LinearLayout> <View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#FFFFFF" /> <ListView
android:id="@+id/main_listview"
android:layout_width="match_parent"
android:layout_height="30dp"
android:layout_weight="2" >
</ListView> <View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#FFFFFF" /> <Button
android:id="@+id/main_button_start"
android:layout_width="match_parent"
android:layout_height="10dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_weight="1"
android:text="开始" /> <LinearLayout
android:id="@+id/main_lin"
android:layout_width="match_parent"
android:layout_height="10dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_weight="1"
android:orientation="horizontal"
android:visibility="gone" > <Button
android:id="@+id/main_button_zanting"
android:layout_width="50dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="暂停" /> <Button
android:id="@+id/main_button_jici"
android:layout_width="50dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="记次" />
</LinearLayout> </LinearLayout>

XMl

辅助类Times,也就是时间类,实例化即可用,然后再tostring上做了格式输出

package com.example.timer;

import java.text.Format;

public class Times {

    public int getHour() {
return Hour;
} @Override
public String toString() {
return String.format("%02d",Hour)+":"+String.format("%02d",minute)+":"+String.format("%.1f",second);
} public Times(int hour, int minute, float second) {
super();
Hour = hour;
this.minute = minute;
this.second = second;
} public void setHour(int hour) {
Hour = hour;
} public int getMinute() {
return minute;
} public void setMinute(int minute) {
this.minute = minute;
} public float getSecond() {
return second;
} public void setSecond(float second) {
this.second = second;
} private int Hour;
private int minute;
private float second; public void secondAdd() {
if (second > 59) {
second = 0;
if (minute > 59) {
minute = 0;
Hour++;
} else {
minute++;
}
} else {
second += 0.1;
}
}
}

Times

接下来是最主要的绘图代码,MyCanvases继承View,绘制圆,指针刻度等等。其中绘制指针比较复杂,但是目前没有想到什么好的方法,大家如果有,请给我分享下。指针主要是画出一个封闭的三角形,为了保持转动,使用自己写的arithmetic1一个小的算法,因为要用到圆和三角函数来计算坐标(忘了差不多了,还把数学几何图形复习了一遍......累死了。细心的朋友可能发现,Math.sin方法参数是弧度,而我显示把角度计算出来,再换算成弧度,绕了一大圈。不要喷我,当时为了这些几何图形,手头又没笔,都把我写晕了,这部分代码真心不想动了。)附上代码

package com.example.timer;

import java.util.Timer;
import java.util.TimerTask;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathEffect;
import android.os.Handler;
import android.os.Message;
import android.view.View; public class MyCanvases extends View {
private Paint paint, paint2, paint3, paint4, paint5, paint6, paint7;
private Timer timer;
private Handler handler;
private Canvas canvas;
private float X = 50, Y = 20, W = 0, H = 0;
private Times times = new Times(0, 0, 0);
private Path path;
Message message;
MyTimer myTimer; public MyCanvases(Context context, float x, float y) {
super(context);
message = Message.obtain();
handler = new MyHandler(); this.W = x;
this.H = y;
paint = new Paint();
// 去锯齿
paint.setAntiAlias(true);
paint.setColor(Color.BLACK);
// 画笔样式
paint.setStyle(Paint.Style.STROKE);
// 画笔粗细
paint.setStrokeWidth(1); paint3 = new Paint();
paint3.setAntiAlias(true);
paint3.setColor(Color.GRAY);
paint3.setStyle(Paint.Style.STROKE);
paint3.setStrokeWidth(35); paint2 = new Paint();
paint2.setAntiAlias(true);
paint2.setColor(Color.BLACK);
paint2.setStyle(Paint.Style.FILL);
paint2.setTextSize(53); paint4 = new Paint();
paint4.setAntiAlias(true);
paint4.setColor(Color.rgb(224, 238, 238));
paint4.setStyle(Paint.Style.FILL_AND_STROKE);
paint4.setStrokeWidth(10); paint5 = new Paint();
paint5.setAntiAlias(true);
paint5.setColor(Color.YELLOW);
paint5.setStyle(Paint.Style.STROKE);
paint5.setStrokeWidth(30); paint6 = new Paint();
paint6.setAntiAlias(true);
paint6.setColor(Color.GRAY);
PathEffect pathEffect = new DashPathEffect(new float[] { 5, 5 }, 1);
paint6.setPathEffect(pathEffect);
paint6.setStyle(Paint.Style.STROKE);
paint6.setStrokeWidth(12); paint7 = new Paint();
paint7.setAntiAlias(true);
paint7.setColor(Color.GRAY);
paint7.setStyle(Paint.Style.FILL);
paint7.setStrokeWidth(1);
} protected void onDraw(Canvas canvas) {
this.canvas = canvas;
// 内外圆
canvas.drawCircle(W / 2, H / 2 - 50, W / 2 - 100, paint);
canvas.drawCircle(W / 2, H / 2 - 50, W / 2 - 150, paint3);
// 时间
canvas.drawText(times.toString(), W / 2 - 120, H / 2 - 50, paint2);
// 指针
// 这个指针其实是一个三角形,外圆上一点加上内圆上两点,构成一个闭合三脚形。时间变化时3个点一起移动,看起来就是一个指针的效果了
float a[] = arithmetic1(W / 2, H / 2 - 50, W / 2 - 137,
times.getSecond());
float b[] = arithmetic1(W / 2, H / 2 - 50, W / 2 - 180,
times.getSecond() - 2);
float c[] = arithmetic1(W / 2, H / 2 - 50, W / 2 - 180,
times.getSecond() + 2);
path = new Path();
path.moveTo(a[0], a[1]);
path.lineTo(b[0], b[1]);
path.lineTo(c[0], c[1]);
path.close();
canvas.drawPath(path, paint4);
// 小圆
canvas.drawCircle(W - 150, H - 150, 80, paint);
// 刻度
canvas.drawCircle(W - 150, H - 150, 70, paint6);
// 指针 canvas.drawCircle(W - 150, H - 150, 10, paint7); a = arithmetic1(W - 150, H - 150, 60, times.getSecond() * 60);
b = arithmetic1(W - 150, H - 150, 10, times.getSecond() * 60 - 4);
c = arithmetic1(W - 150, H - 150, 10, times.getSecond() * 60 + 4);
path = new Path();
path.moveTo(a[0], a[1]);
path.lineTo(b[0], b[1]);
path.lineTo(c[0], c[1]);
path.close();
canvas.drawPath(path, paint7);
} // 计时器
public class MyTimer extends TimerTask {
public void run() {
handler.sendMessage(new Message().obtain());
}
} public class MyHandler extends Handler {
public void handleMessage(Message msg) {
times.secondAdd();
invalidate(); }
} /**
* 自定义方法,以60秒为圆的一圈,给出圆的参数,获得对应时间在圆周上点的坐标。所以不同时间可以通用,只需要根据秒对比相应的直即可。(
* 例如传入的time为分钟,将time/60就是分钟的坐标变化)
*
* @param x
* 圆心x坐标
* @param y
* 圆心y坐标
* @param r
* 圆的半径
* @param time
* 时间
* @return 返回一个float数组,大小为2。float0为x坐标,float1为y坐标;
*/
public float[] arithmetic1(float x, float y, float r, float time) {
float circle[] = new float[2];
float angle;
angle = 90 - time * 6;
circle[0] = (float) (Math.cos(angle * Math.PI / 180) * r + x);
circle[1] = (float) (y - Math.sin(angle * Math.PI / 180) * r);
return circle;
} /**
* 启动计时器
*/
public void Start() {
timer = new Timer();
myTimer = new MyTimer();
timer.schedule(myTimer, 0, 100); } /**
* 停止计时器
*/
public void Stop() {
timer.cancel();
myTimer.cancel();
} /**
* 获取这一刻的时间
*
* @return
*/
public String getTime() {
return times.toString();
} /**
* 这个方法其实就是重置时间
*
*/
public void setTime() {
times = new Times(0, 0, 0);
invalidate();
}
}

MyCanvases

最后是主页面activity代码,其中在listview上没有写布局,就用最简单的数组资源做适配器,因为这个listview只有一个显示的功能,所以就没用多纠结。大家可以自己改改喜欢的布局加上去。

package com.example.timer;

import java.util.ArrayList;
import java.util.List; import android.os.Bundle;
import android.app.Activity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ListView; public class MainActivity extends Activity {
private ListView listview;
private Button btnStart, btnJici, btnStop;
private LinearLayout lin, linView;
MyCanvases myCanvases;
List<String> list; protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
list = new ArrayList<String>();
intview();
} /**
* 获得控件关联,为按钮设置监听
*/
private void intview() {
linView = (LinearLayout) findViewById(R.id.main_view);
listview = (ListView) findViewById(R.id.main_listview);
btnStart = (Button) findViewById(R.id.main_button_start);
btnStart.setOnClickListener(new MyOnClick());
btnJici = (Button) findViewById(R.id.main_button_jici);
btnJici.setOnClickListener(new MyOnClick());
btnStop = (Button) findViewById(R.id.main_button_zanting);
btnStop.setOnClickListener(new MyOnClick());
lin = (LinearLayout) findViewById(R.id.main_lin);
} /**
* 实现点击事件接口,用控件id来区分不同事件,将所有事件集中到一起处理。(个人比较喜欢这样写)
*
* @author
*
*/
public class MyOnClick implements OnClickListener { public void onClick(View v) {
switch (v.getId()) {
case R.id.main_button_start:
myCanvases.Start();
btnStart.setVisibility(View.GONE);
lin.setVisibility(View.VISIBLE);
break;
case R.id.main_button_zanting:
if (btnStop.getText().toString().equals("暂停")) {
myCanvases.Stop();
btnStop.setText("继续");
btnJici.setText("重置");
} else {
myCanvases.Start();
btnStop.setText("暂停");
btnJici.setText("记次");
}
break;
case R.id.main_button_jici:
if (btnJici.getText().toString().equals("重置")) {
myCanvases.setTime();
btnStart.setVisibility(View.VISIBLE);
lin.setVisibility(View.GONE);
btnStop.setText("暂停");
btnJici.setText("记次");
// 清空数据
list.clear();
// 清空listview
listview.setAdapter(null);
} else {
// 添加数据
list.add(myCanvases.getTime());
String[] text = new String[list.size()];
for (int i = 0; i < list.size(); i++) {
text[i] = "第" + (i + 1) + "次: "
+ list.get(i);
}
// 建立适配器
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
MainActivity.this,
android.R.layout.simple_list_item_1, text);
// 添加适配器
listview.setAdapter(adapter);
// 将listview的焦点始终锁定在最后一行,很有用
listview.setSelection(adapter.getCount());
}
default:
break;
}
}
} // 重写activit的onWindowFocusChanged方法,是为了在布局加载完成之后,
// 才能获得到控件的大小和位置,将参数传递到绘图类
public void onWindowFocusChanged(boolean hasFocus) {
if (hasFocus) {
myCanvases = new MyCanvases(this, linView.getWidth(),
linView.getHeight());
//将绘制的图像加载到对应的布局上显示
linView.addView(myCanvases);
}
}
}

MainActivity

最后说一些细节。在绘制的坐标问题上,我先是在activity布局加载完成之后,将绘制区域的宽度和高度获取到,根据这个数据来绘制,本来是想实现在不同手机屏幕实现自适应效果,后来发现。手机分辨率不一样的时候,还是没能实现,所以大家根据自己的需要改改坐标。至于怎么实现自适应屏幕,由于没有系统的学习过,现在还是没有解决好。我会努力的。还有就是小米自带计时器上有渲染的效果,就是指针转动时颜色会发生变化,这个用android自带的绘图渲染来写很麻烦。所以可能是用第三方包,这里我就不写了(试着写过,但是渲染效果很差,就删除了)。

大家有什么意见,欢迎随时交流。大神勿喷....

Android 绘制计时器的更多相关文章

  1. android绘制view的过程

    1 android绘制view的过程简单描述  简单描述可以解释为:计算大小(measure),布局坐标计算(layout),绘制到屏幕(draw):            下面看看每一步的动作到底是 ...

  2. 【转】Android绘制View的过程研究——计算View的大小

    Android绘制View的过程研究——计算View的大小 转自:http://liujianqiao398.blog.163.com/blog/static/18182725720121023218 ...

  3. Android绘制流程

    一.前言 1.1.C++界面库 MFC.WTL.DuiLib.QT.Skia.OpenGL.Android里面的画图分为2D和3D两种: 2D是由Skia 来实现的,3D部分是由OpenGL实现的. ...

  4. Android Wear计时器开发

    记得在2013年12月的时候,有系列文章是介绍怎么开发一个智能手表的App,让用户可以在足球比赛中记录停表时间.随着Android Wear的问世,在可穿戴设备中开发一款这样的App确实是个很不错的想 ...

  5. Android倒计时器——CountDownTimer

    Android倒计时器--CountDownTimer 说明 第一个参数是倒计时的时间 第二个参数是多长时间执行一次回调 /** * @param millisInFuture The number ...

  6. Android绘制优化(二)布局优化

    前言 我们知道一个界面的测量和绘制是通过递归来完成的,减少布局的层数就会减少测量和绘制的时间,从而性能就会得到提升.当然这只是布局优化的一方面,那么如何来进行布局的分析和优化呢?本篇文章会给你一个满意 ...

  7. Android绘制优化(一)绘制性能分析

    前言 一个优秀的应用不仅仅是要有吸引人的功能和交互,同时在性能上也有很高的要求.运行Android系统的手机,虽然配置在不断的提升,但仍旧无法和PC相比,无法做到PC那样拥有超大的内存以及高性能的CP ...

  8. 理解Android绘制视图的方式

    在创建自定义ViewGroup前,读者首先需要理解Android绘制视图的方式.我不会涉及过多细节,但是需要读者理解Android开发文档(见3.5节)中的一段话,这段话解释如何绘制一个布局.内容如下 ...

  9. Android 绘制中国地图

    最近的版本有这样一个需求: 有 3 个要素: 中国地图 高亮省区 中心显示数字 面对这样一个需求,该如何实现呢? 高德地图 因为项目是基于高德地图来做的,所以很自然而然的想到了高德.但是当查阅高德地图 ...

随机推荐

  1. Apache配置虚拟主机后,不能访问localhost的问题

    今天想试用一下php7,但是发现php7只支持Apache2.4版本,而我电脑上的Apache是2.2版本,为了想尝鲜,就必须去下载新的Apache2.4 php7和apache2.4安装整合以后,l ...

  2. SQL 学习与工作日常:语句积累

    1.跨服务器连接数据表 --打开服务器配置'Ad Hoc Distributed Queries' --exec sp_configure 'show advanced options',1 --re ...

  3. APUE(3)——文件I/O

    大多数情况下,我们都会利用Standard I/O Library来进行I/O操作,而这一章所讲的I/O是UNIX系统直接提供的I/O操作,且大多是Unbuffered I/O,即每一次读或写都会出现 ...

  4. Servlet原理

    Servlet主要用来处理客户端请求并将其结果发送到客户端,下面我们来详细看一下Servlet. 一.Servlet的生命周期 Servlet的生命周期是由Servlet的容器来控制的(Tomcat ...

  5. 浅谈android应用性能之内存(转)

    如何测试一个APP的内存占用情况?一个APP占用的内存分哪些部分?如何检查一个APP是否存在内存泄漏? 一.Android内存介绍: 在java开发过程中,是通过new来为对象分配内存的,而内存的释放 ...

  6. .NET学习笔记(3) — VisualStudio使用总结

    目录 一:VS是什么? 二:VS可以创建什么类型的工程? 三:VS的常用功能? 四:VS都有哪些使用技巧? 五:注意事项 六:资源汇总   一:VS是什么? Microsoft Visual Stud ...

  7. PL/SQL在Oracle服务器上连接出错

    今天在Oracle服务器上使用PL/SQL连接Oracle软件的时候出现了错误,错误如下: 具体的解决办法如下: 需要下载32位的Oracle Client,具体的步骤如下:登录Oracle官方网站 ...

  8. php Composer中国全量镜像

    http://pkg.phpcomposer.com/ http://www.yiichina.com/doc/guide/2.0/intro-upgrade-from-v1 https://getc ...

  9. solr-tomcat 中文乱码

    <Connector URIEncoding="UTF-8" port="8080" protocol="HTTP/1.1" conn ...

  10. 06链队列_LinkQueue--(栈与队列)

    #include "stdio.h" #include "stdlib.h" #include "io.h" #include " ...