Android 绘制计时器
用小米的手机,发现其实还可以,无意间点开小米的计时器,发现界面非常好看和实用。于是自己仿照着写一个,由于技术不好,代码整体结构上可能有点乱,但主要的实现功能和掌握知识点。
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 绘制计时器的更多相关文章
- android绘制view的过程
1 android绘制view的过程简单描述 简单描述可以解释为:计算大小(measure),布局坐标计算(layout),绘制到屏幕(draw): 下面看看每一步的动作到底是 ...
- 【转】Android绘制View的过程研究——计算View的大小
Android绘制View的过程研究——计算View的大小 转自:http://liujianqiao398.blog.163.com/blog/static/18182725720121023218 ...
- Android绘制流程
一.前言 1.1.C++界面库 MFC.WTL.DuiLib.QT.Skia.OpenGL.Android里面的画图分为2D和3D两种: 2D是由Skia 来实现的,3D部分是由OpenGL实现的. ...
- Android Wear计时器开发
记得在2013年12月的时候,有系列文章是介绍怎么开发一个智能手表的App,让用户可以在足球比赛中记录停表时间.随着Android Wear的问世,在可穿戴设备中开发一款这样的App确实是个很不错的想 ...
- Android倒计时器——CountDownTimer
Android倒计时器--CountDownTimer 说明 第一个参数是倒计时的时间 第二个参数是多长时间执行一次回调 /** * @param millisInFuture The number ...
- Android绘制优化(二)布局优化
前言 我们知道一个界面的测量和绘制是通过递归来完成的,减少布局的层数就会减少测量和绘制的时间,从而性能就会得到提升.当然这只是布局优化的一方面,那么如何来进行布局的分析和优化呢?本篇文章会给你一个满意 ...
- Android绘制优化(一)绘制性能分析
前言 一个优秀的应用不仅仅是要有吸引人的功能和交互,同时在性能上也有很高的要求.运行Android系统的手机,虽然配置在不断的提升,但仍旧无法和PC相比,无法做到PC那样拥有超大的内存以及高性能的CP ...
- 理解Android绘制视图的方式
在创建自定义ViewGroup前,读者首先需要理解Android绘制视图的方式.我不会涉及过多细节,但是需要读者理解Android开发文档(见3.5节)中的一段话,这段话解释如何绘制一个布局.内容如下 ...
- Android 绘制中国地图
最近的版本有这样一个需求: 有 3 个要素: 中国地图 高亮省区 中心显示数字 面对这样一个需求,该如何实现呢? 高德地图 因为项目是基于高德地图来做的,所以很自然而然的想到了高德.但是当查阅高德地图 ...
随机推荐
- nmblookup
域网内可以通过下述命令来根据ip地址查询其他主机名(Linux) 使用nmblookup -A ip命令查询 [admin@v015213 ~/lpmall]$ nmblookup -A 10.19. ...
- 用CentOS 7打造合适的科研环境
http://seisman.info/linux-environment-for-seismology-research.html 这篇博文记录了我用CentOS 7搭建地震学科研环境的过程,供我个 ...
- MIME 部分扩展名与类型对应
svg image/svg+xml woff application/x-font-woff woff2 application/x-font-woff eot applicati ...
- 电脑小白学习软件开发-C#语言基础之循环重点讲解,习题
写代码也要读书,爱全栈,更爱生活.每日更新原创IT编程技术及日常实用视频. 我们的目标是:玩得转服务器Web开发,搞得懂移动端,电脑客户端更是不在话下. 本教程是基础教程,适合任何有志于学习软件开发的 ...
- Escape Sequences in String
Code Output \' single quote \" double quote \\ backslash \n newline \r carriage return \t tab ...
- linux查看文件个数命令
linux下查看当前目录下文件个数命令: 使用背景:有时想了解一个目录下具体有多少文件或者有多少文件夹. 1. 查看当前目录下文件个数 ls -l |grep "^-"|wc -l ...
- 利用Ajax改变发送请求方式
由于测试的时候需要模拟Head请求,解决办法:先访问要请求的站点,然后在浏览器的控制台下执行如下代码,请求方式就为参数给的值: var xmlHttp; if (window.ActiveXObjec ...
- 第二十六篇、因为自定item(nav)而使系统右滑返回手势失效的解决方法
@interface ViewController () <uigesturerecognizerdelegate> @end@implementation ViewController ...
- javascript异步执行函数导致的变量变化问题解决思路
for(var i=0;i<3;i++) { setTimeout(function(){ console.log(i) },0); }控制台输出:333 这是因为执行方法的时候for循环已经执 ...
- The method getContextPath() is undefined for the type ServletContext
我出错时,到网上说得是版本问题,我找到对应的包javax-servlet5.1.12.jar,把它删了,居然不报错了,原来是和包servlet-2_5-api.jar冲突了