Android计时器项目

来源:互联网 发布:文选 上海古籍 淘宝 编辑:程序博客网 时间:2024/05/19 03:21

本人是非计算机学院纯零基础,学了极客班的安卓微专业一个半月,准备将课程学习中做的作业进行一个整理,欢迎大家指正。
今天记录的项目是一个类似于安卓原生计时器使用handler来更新UI。当然这种方法肯定不是计时器的正确编写方法,在时间上会有很大误差,只是为了练习自定义控件和Handler的用法。
效果图
最后做出的结果如图所示,在自定义控件中绘制了圆环作为进度条,在进度条内绘制数字来显示时间。有开始、计次、复位三个按钮。当按计次按钮时在按钮上方的listview中会记录下当前记录条数、当前记录的总时间、当前记录和上次记录的间隔时间。
总体的思路就是绘制一个自定义控件,在activity中引用自定义控件,在activity中使用handler不断发送信息来更新自定义控件。
话不多说,直接上代码,由于是初学者,所以代码会注释得十分详细,也很适合初学者看。

public class ProgressBarByMyself extends View {    //当前进度条进度    private static float mProgress;    //总进度    private int mTotalProgress = 10000;    //画圆环背景的画笔    private Paint mCirclePaint;    //画圆环的画笔    private Paint mRingPaint;    //画字体的画笔    private Paint mTextPaint;    //文字长度    private float mTextWidth;    //文字高度    private float mTextHeight;    //设置毫秒字体的画笔、文字长度、文字高度    private Paint mTestTextPaint;    private float mTestTextWidth;    private float mTestTextHeight;    //圆环背景颜色    private int mCircleColor;    //圆环颜色    private int mRingColor;    //圆环半径    private float mRingRadius;    //圆环宽度    private float mStrokeWidth;    //内圆半径    private float mRadius;    //圆心X坐标    private int mXCenter;    //圆心Y坐标    private int mYCenter;    public ProgressBarByMyself(Context context) {        this(context, null);    }    public ProgressBarByMyself(Context context, AttributeSet attrs) {        this(context, attrs, 0);        //获取自定义属性    }    public ProgressBarByMyself(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        //初始化各种属性        initAttrs(context, attrs);        //获取画笔属性        initVariable();    }    private void initVariable() {        //设置背景圆环的画笔属性        //设置抗锯齿属性        mCirclePaint.setAntiAlias(true);        //设置颜色        mCirclePaint.setColor(mCircleColor);        //设置画图样式为圆环        mCirclePaint.setStyle(Paint.Style.STROKE);        mCirclePaint.setStrokeWidth(mStrokeWidth);        //设置红色活动进度条的画笔属性        mRingPaint.setAntiAlias(true);        mRingPaint.setColor(mRingColor);        mRingPaint.setStyle(Paint.Style.STROKE);        mRingPaint.setStrokeWidth(mStrokeWidth);        //设置字体的画笔属性        mTextPaint.setAntiAlias(true);        mTextPaint.setStyle(Paint.Style.FILL);        //设置透明度和色彩,第一个参数是透明度,后三个参数是色彩        mTextPaint.setARGB(255, 255, 255, 255);        //设置毫秒字体的画笔属性        mTestTextPaint.setAntiAlias(true);        mTestTextPaint.setStyle(Paint.Style.FILL);        mTestTextPaint.setARGB(255, 255, 255, 255);    }    private void initAttrs(Context context, AttributeSet attrs) {        //构建画笔实例        mCirclePaint = new Paint();        mRingPaint = new Paint();        mTextPaint = new Paint();        mTestTextPaint = new Paint();        TypedArray typeArray = context.obtainStyledAttributes(attrs,R.styleable.ProgressBarByMyself);        //设置圆环宽度        mStrokeWidth = typeArray.getDimension(R.styleable.ProgressBarByMyself_strokeWidth, 20);        //设置背景进度条的颜色        mCircleColor=typeArray.getColor(R.styleable.ProgressBarByMyself_circleColor, 0xFFFFFFFF);        //设置红色活动进度条的颜色        mRingColor =typeArray.getColor(R.styleable.ProgressBarByMyself_ringColor, 0xFFFF3426);    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        //获取各属性的具体数字,mXcenter和mYcenter是圆环的圆心坐标。        mXCenter = getWidth()/2;        mYCenter =getHeight()/3;        mRadius = getWidth()/3;        //设置字体的大小        mTextPaint.setTextSize(mRadius*2/3);        mTestTextPaint.setTextSize(mRadius/3);        mRingRadius = mRadius +mStrokeWidth/2;        //绘制出背景圆环        canvas.drawCircle(mXCenter, mYCenter, mRingRadius, mCirclePaint);        //获取字体属性        Paint.FontMetrics fm = mTextPaint.getFontMetrics();        mTextHeight = (int) Math.ceil(fm.descent - fm.ascent);        Paint.FontMetrics lf = mTestTextPaint.getFontMetrics();        mTestTextHeight = (int) Math.ceil(fm.descent - fm.ascent);        //设置格式化显示数字        String text = String.format("%1$03d",((int)mProgress/100));        String testtext = String.format("%1$02d",(int)mProgress%100);        //设置字体宽度是刚好将自己放满的宽度        mTextWidth = mTextPaint.measureText(text, 0, text.length());        mTestTextWidth = mTestTextPaint.measureText(testtext, 0, testtext.length());        //计算外切矩形的点        RectF Oval = new RectF();        Oval.left = (mXCenter - mRingRadius);        Oval.top=(mYCenter - mRingRadius);        Oval.right = mRingRadius+mXCenter;        Oval.bottom = mRingRadius+mYCenter;        //画出显示秒和毫秒的数字以及红色活动进度条        canvas.drawText(text,mXCenter-mTextWidth*2/3,mYCenter+mTextHeight/4,mTextPaint);        canvas.drawText(testtext,mXCenter+mTextWidth/2,mYCenter+mTestTextHeight/4,mTestTextPaint);        canvas.drawArc(Oval,-90,(mProgress/mTotalProgress*360),false,mRingPaint);    }    public static void setProgress(float progress) {        mProgress = progress;    }    public static float getProgress(){        float l=mProgress;        return l;    }}

在自定义控件中主要画出的就是计时器的圆形进度条和显示的数字,其中的mProgress设置的是当前进度条的进度,在Activity中就是通过这一参数才对进度条进行更新,同样的计次中listview中的数据也是通过获取这个mProgress来计时的。
接着就上MainActivity的代码。

public class HandlerActivity extends Activity implements View.OnClickListener {    private ArrayList<TimeCount> timeCounts = new ArrayList<TimeCount>();    //设置Message的辨别代号    public static final int MESSAGE_CODE = 888888;    //初始化Handler    private TestHandler mTestHandler;    //初始化按钮    private Button startButton;    private Button timeCountButton;    private Button resetButton;    //初始化自定义控件    public ProgressBarByMyself progressBarByMyself;    //设置进度条状态标识,flag的真假显示了当前是运行还是暂停    private Boolean flag = true;    //用于记录当前是第几次计次    private int count = 1;    //初始化listView    private ListView listView;    private TimeAdapter adapter;    private float countthistime=0;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        //通过id寻找按钮和自定义控件        progressBarByMyself = (ProgressBarByMyself) findViewById(R.id.my_progress_view);        startButton = (Button) findViewById(R.id.start_button);        timeCountButton = (Button) findViewById(R.id.time_count_button);        resetButton = (Button) findViewById(R.id.reset_button);        //为按钮设置点击事件        startButton.setOnClickListener(this);        timeCountButton.setOnClickListener(this);        resetButton.setOnClickListener(this);        //为listview设置适配器        adapter= new TimeAdapter(HandlerActivity.this,R.layout.time_countent,timeCounts);        listView = (ListView) findViewById(R.id.listView);        listView.setAdapter(adapter);    }    @Override    public void onClick(View v) {        switch (v.getId()){            case R.id.start_button:                start();                break;            case R.id.time_count_button:                count();                break;            case R.id.reset_button:                reset();                break;        }    }    public void start(){        if (flag) {            //如果之前是暂停状态,那么获取当前进度条的进度信息然后继续发送消息            mTestHandler = new TestHandler(this);            Message message = mTestHandler.obtainMessage();            message.arg1 = 0;            message.arg2 = 1;            message.what = MESSAGE_CODE;            message.obj = (int) progressBarByMyself.getProgress();            mTestHandler.sendMessage(message);            //当flag设置为false即开始状态            flag = false;            startButton.setText("停止");        } else {            //如果之前是开始状态,那么现在将loop里的消息全部清除,这样就暂停了            mTestHandler.removeMessages(MESSAGE_CODE);            flag = true;            startButton.setText("开始");            }    }    //计时方法    public void count() {        //向listView的适配器传入当前记录是第几条,当前经过总时间,和当前总之间与上次记录的时间差        TimeCount timeCount= new TimeCount(count,progressBarByMyself.getProgress()/100,(progressBarByMyself.getProgress()-countthistime)/100);        //将当前记录的总时间保存起来,用来下次减的时候用        countthistime=progressBarByMyself.getProgress();        timeCounts.add(timeCount);        //liseView里加上变化的数据        adapter.notifyDataSetChanged();        //让表示记录个数的变量自增        count += 1;    }    //重置方法    public void reset(){        //移除loop内的Message        mTestHandler.removeMessages(MESSAGE_CODE);        //将进度条进度设置为零并重画        progressBarByMyself.setProgress(0);        progressBarByMyself.invalidate();        //将liseView和适配器都清零        timeCounts.clear();        adapter.clear();        count=1;        countthistime = 0;        //将按钮和文本都清零        startButton.setText("开始");        flag=true;    }    //创建TestHandler类    public class TestHandler extends Handler {        public WeakReference<HandlerActivity> mHandlerActivityWeakReference;        public TestHandler(HandlerActivity activity) {            mHandlerActivityWeakReference = new WeakReference<>(activity);        }        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            HandlerActivity handlerActivity = mHandlerActivityWeakReference.get();            // 接收消息            switch (msg.what) {                case MESSAGE_CODE:                    int value = (int) msg.obj;                    //将接受到的时间值设置为进度条的进度值并让控件重画                    progressBarByMyself.setProgress(value);                    progressBarByMyself.invalidate();                    //更改Message信息                    msg = Message.obtain();                    msg.arg1 = 0;                    msg.arg2 = 1;                    msg.what = MESSAGE_CODE;                    msg.obj = value + 1;                    //每隔10ms发一次消息                    sendMessageDelayed(msg, 10);                    break;            }        }    }}

在主布局中,通过开始、计次和清零按钮来对UI进行控制。
当点击开始按钮时,Message消息开始每隔10ms发送一次,接受到消息之后就将消息里的value值给mProgress,然后进行UI更新。
当计时器开始计时后,开始按钮的文本就更换为暂停,点击暂停后,就会将MessageQueue里的Message全部清空,这样就不会继续接受和发送了,进度条也就不动了。
当点击计次按钮时,会将当前的次数count、进度条的进度mProgress、本次记录的进度与上次记录的进度相减这三个信息传入adapter,同时将当前进度记录下来用来下次计算。为了让最后的记录始终能最先看到,listview里要设置一个属性android:stackFromBottom=”true”这样每次listview更新后会自动滚动到最下方。
接下来贴上Adapter和计次类。

public class TimeCount {    //初始化当前记录条数、当前总时间、本次与上次记录的时间差    private int mNumber;    private float time;    private float alltime;    private static float Alltime;    //获取到当前记录条数、当前总时间、本次与上次记录的时间差    public TimeCount(int count, float progress, float v) {        this.mNumber = count;        this.alltime = progress;        this.time = v;    }    public int getmNumber() {        return mNumber;    }    public float getTime() {        return time;    }    public float getAlltime() {        return alltime;    }}
public class TimeAdapter extends ArrayAdapter<TimeCount> {    private int resourceId;    public TimeAdapter(Context context, int textViewResourceId, List<TimeCount> objects) {        super(context,textViewResourceId, objects);        resourceId = textViewResourceId;    }    @Override    public View getView( int position,View convertView, ViewGroup parent) {        //获取位置信息        TimeCount timeCount = getItem(position);        View view;        view =  LayoutInflater.from(getContext()).inflate(resourceId, null);        //计次参数        TextView number= (TextView) view.findViewById(R.id.number);        //计次时间间隔        TextView time= (TextView) view.findViewById(R.id.time);        //当前经过的总时间        TextView alltime= (TextView) view.findViewById(R.id.all_time);        //获取到HandlerActivity传入TimeCount里的记录条数、当前总时间、两次记录时间差得信息        number.setText(String.format("# %1$02d",timeCount.getmNumber()));        time.setText(timeCount.getTime()+"");        alltime.setText(timeCount.getAlltime()+"");        return view;    }}

由于本人是新手,所以一些命名和格式可能做得不规范,欢迎大家指正。

0 0