习惯了Android的默认倒计时字体,这里采用Canvas画图的方式实现倒计时时钟效果,首先先看一下静态效果
不难发现,每个数字都是一个小圆形,也可以改成一个小正方形等等,这些都是可以实现的。
这里使用圆形。
仔细观察数字发现其是由一个二维数组组成,类似如下所示:根据下面数组可以画出0的,效果
{0,0,1,1,1,0,0},
{0,1,1,0,1,1,0},
{1,1,0,0,0,1,1},
{1,1,0,0,0,1,1},
{1,1,0,0,0,1,1},
{1,1,0,0,0,1,1},
{1,1,0,0,0,1,1},
{1,1,0,0,0,1,1},
{0,1,1,0,1,1,0},
{0,0,1,1,1,0,0}
具体如何实现,可以先定义一个常量类,这里也可以使用json格式,然后进行读取。
/**
* Constants.java
* Copyright(C) 2014
* creator:cuiran 2014-12-19 下午2:28:34
*/
package com.cayden.countdown;
/**
* TODO
* @author cuiran
* @version 1.0.0
*/
public interface Constants {
int[][] data0={
{0,0,1,1,1,0,0},
{0,1,1,0,1,1,0},
{1,1,0,0,0,1,1},
{1,1,0,0,0,1,1},
{1,1,0,0,0,1,1},
{1,1,0,0,0,1,1},
{1,1,0,0,0,1,1},
{1,1,0,0,0,1,1},
{0,1,1,0,1,1,0},
{0,0,1,1,1,0,0}
};
int[][] data1={
{0,0,0,1,1,0,0},
{0,1,1,1,1,0,0},
{0,0,0,1,1,0,0},
{0,0,0,1,1,0,0},
{0,0,0,1,1,0,0},
{0,0,0,1,1,0,0},
{0,0,0,1,1,0,0},
{0,0,0,1,1,0,0},
{0,0,0,1,1,0,0},
{1,1,1,1,1,1,1}
};
int[][] data2={
{0,1,1,1,1,1,0},
{1,1,0,0,0,1,1},
{0,0,0,0,0,1,1},
{0,0,0,0,1,1,0},
{0,0,0,1,1,0,0},
{0,0,1,1,0,0,0},
{0,1,1,0,0,0,0},
{1,1,0,0,0,0,0},
{1,1,0,0,0,1,1},
{1,1,1,1,1,1,1}
};
int[][] data3={
{1,1,1,1,1,1,1},
{0,0,0,0,0,1,1},
{0,0,0,0,1,1,0},
{0,0,0,1,1,0,0},
{0,0,1,1,1,0,0},
{0,0,0,0,1,1,0},
{0,0,0,0,0,1,1},
{0,0,0,0,0,1,1},
{1,1,0,0,0,1,1},
{0,1,1,1,1,1,0}
};
int[][] data4={
{0,0,0,0,1,1,0},
{0,0,0,1,1,1,0},
{0,0,1,1,1,1,0},
{0,1,1,0,1,1,0},
{1,1,0,0,1,1,0},
{1,1,1,1,1,1,1},
{0,0,0,0,1,1,0},
{0,0,0,0,1,1,0},
{0,0,0,0,1,1,0},
{0,0,0,1,1,1,1}
};
int[][] data5={
{1,1,1,1,1,1,1},
{1,1,0,0,0,0,0},
{1,1,0,0,0,0,0},
{1,1,1,1,1,1,0},
{0,0,0,0,0,1,1},
{0,0,0,0,0,1,1},
{0,0,0,0,0,1,1},
{0,0,0,0,0,1,1},
{1,1,0,0,0,1,1},
{0,1,1,1,1,1,0}
};
int[][] data6={
{0,0,0,0,1,1,0},
{0,0,1,1,0,0,0},
{0,1,1,0,0,0,0},
{1,1,0,0,0,0,0},
{1,1,0,1,1,1,0},
{1,1,0,0,0,1,1},
{1,1,0,0,0,1,1},
{1,1,0,0,0,1,1},
{1,1,0,0,0,1,1},
{0,1,1,1,1,1,0}
};
int[][] data7={
{1,1,1,1,1,1,1},
{1,1,0,0,0,1,1},
{0,0,0,0,1,1,0},
{0,0,0,0,1,1,0},
{0,0,0,1,1,0,0},
{0,0,0,1,1,0,0},
{0,0,1,1,0,0,0},
{0,0,1,1,0,0,0},
{0,0,1,1,0,0,0},
{0,0,1,1,0,0,0}
};
int[][] data8={
{0,1,1,1,1,1,0},
{1,1,0,0,0,1,1},
{1,1,0,0,0,1,1},
{1,1,0,0,0,1,1},
{0,1,1,1,1,1,0},
{1,1,0,0,0,1,1},
{1,1,0,0,0,1,1},
{1,1,0,0,0,1,1},
{1,1,0,0,0,1,1},
{0,1,1,1,1,1,0}
};
int[][] data9={
{0,1,1,1,1,1,0},
{1,1,0,0,0,1,1},
{1,1,0,0,0,1,1},
{1,1,0,0,0,1,1},
{0,1,1,1,0,1,1},
{0,0,0,0,0,1,1},
{0,0,0,0,0,1,1},
{0,0,0,0,1,1,0},
{0,0,0,1,1,0,0},
{0,1,1,0,0,0,0}
};
int[][] data10={
{0,0,0,0},
{0,0,0,0},
{0,1,1,0},
{0,1,1,0},
{0,0,0,0},
{0,0,0,0},
{0,1,1,0},
{0,1,1,0},
{0,0,0,0},
{0,0,0,0}
};
}
然后自定义一个CountDownView继承SurfaceView,实现Runnable, Callback,Constants 这三个接口,之所以实现线程接口是为了后续动态改变时候使用。
具体代码如下:
/**
* CountDownView.java
* Copyright(C) 2014
* creator:cuiran 2014-12-19 下午2:18:59
*/
package com.cayden.countdown.view;
import java.util.ArrayList;
import com.cayden.countdown.Constants;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
/**
* 倒计时View
* @author cuiran
* @version 1.0.0
*/
public class CountDownView extends SurfaceView implements Runnable, Callback,Constants {
private static final String TAG="CountDownView";
private SurfaceHolder mHolder; //用于控制SurfaceView
private Canvas mCanvas; //声明画布
private Paint mPaint; //声明画笔
private Thread mThread; //声明一个线程
private static final int RADIUS=10; //声明小球半径
private static final int MARGIN_TOP = 60;
private static final int MARGIN_LEFT = 30;
private ArrayList<int[][]> list=new ArrayList<int[][]>();
public CountDownView(Context context) {
super(context);
mHolder = this.getHolder(); //获得SurfaceHolder对象
mHolder.addCallback(this); //添加状态监听
mPaint = new Paint(); //创建一个画笔对象
mPaint.setColor(Color.BLUE); //设置画笔的颜色
list.add(data0);
list.add(data1);
list.add(data2);
list.add(data3);
list.add(data4);
list.add(data5);
list.add(data6);
list.add(data7);
list.add(data8);
list.add(data9);
list.add(data10);
}
@Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
}
@Override
public void surfaceCreated(SurfaceHolder arg0) {
mThread = new Thread(this); //创建线程对象
mThread.start();
}
@Override
public void surfaceDestroyed(SurfaceHolder arg0) {
}
@Override
public void run() {
try{
mDraw();
}catch(Exception e){
Log.e(TAG,"run error",e);
}
}
/**
* 自定义绘图方法
* 2014-12-19 下午2:22:45
*
*/
public void mDraw() {
mCanvas = mHolder.lockCanvas(); //获得画布对象,开始对画布画画
mCanvas.drawColor(Color.BLACK); //设置画布颜色为黑色
canvas(mCanvas);
mHolder.unlockCanvasAndPost(mCanvas); //把画布显示在屏幕上
}
public void canvas(Canvas mCanvas) {
//画圆,(x轴,y轴,半径,画笔)
int hours=12;
int minutes=36;
int seconds=24;
canvasDigit( MARGIN_LEFT , MARGIN_TOP , hours/10 , mCanvas );
canvasDigit( MARGIN_LEFT + 15*(RADIUS+1) , MARGIN_TOP , hours%10 , mCanvas );
canvasDigit( MARGIN_LEFT + 30*(RADIUS + 1) , MARGIN_TOP , 10 , mCanvas );
canvasDigit( MARGIN_LEFT + 39*(RADIUS+1) , MARGIN_TOP , minutes/10 , mCanvas);
canvasDigit( MARGIN_LEFT + 54*(RADIUS+1) , MARGIN_TOP , minutes%10 , mCanvas);
canvasDigit( MARGIN_LEFT + 69*(RADIUS+1) , MARGIN_TOP , 10 , mCanvas);
canvasDigit( MARGIN_LEFT + 78*(RADIUS+1) , MARGIN_TOP , seconds/10 , mCanvas);
canvasDigit( MARGIN_LEFT + 93*(RADIUS+1) , MARGIN_TOP , seconds%10 , mCanvas);
}
public void canvasDigit(int x,int y,int num,Canvas mCanvas) {
int [][] data=list.get(num);
for(int i=0;i<data.length;i++){
for(int j=0;j<data[i].length;j++){
if(data[i][j]==1){
mCanvas.drawCircle(x + j*2*(RADIUS+1)+(RADIUS+1), y + i*2*(RADIUS+1)+(RADIUS+1), RADIUS, mPaint);
}
}
}
}
}
关键代码是在画圆时候中间间隔的一个计算公式
mCanvas.drawCircle(x + j*2*(RADIUS+1)+(RADIUS+1), y + i*2*(RADIUS+1)+(RADIUS+1), RADIUS, mPaint);
这里引用了一个图片:
图片就是表示计算下一个圆的位置
然后在写一个Activity 来显示我们刚写的View
package com.cayden.countdown;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.Window;
import android.view.WindowManager;
import com.cayden.countdown.view.CountDownView;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 隐藏状态栏
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
// 把Activity的标题去掉
requestWindowFeature(Window.FEATURE_NO_TITLE);
// 设置布局
setContentView(new CountDownView(this));
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
最后运行效果就如最开始的图片显示的。