Android自定义View之数字时钟

前言:网上有许多关于自定义view的讲解,加上自己对自定义view挺感兴趣的以及公司的需求正好用到,所以写个简单的自定义view---数字插件。

基础:(1)自绘控件在构造函数中获取控件的属性,包括自定义属性;

            (2)onMeasure测量控件本身以及自控件的大小。

            (3)onLayout测量控件布局。

            (4)ondraw对控件本身进行绘制。

            (5)dispatchDraw通知子view绘制视图。

            (6)onAttachedToWindow在第一次ondraw之前调用,通过可以作一些初始化的工作,例如:注册广播。

            (7)onDetachedFromWindow销毁view的时候调用,可以作一些收尾的工作,例如:取消注册广播。

分析:数字时钟前两位显示小时的高位和地位,中间一个冒号,后面是分钟的高位和地位,如:12:35;所以本次以两种方式来实现:

(1)自绘控件:关键就是重写ondraw函数,在ondraw函数中完全绘制视图显示的内容。

           1)在构造函数中,通过如下代码获取控件的属性值,并对变量进行赋值:

        Resources r = context.getResources();
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.BirdDigitalClockView, defStyle, 0);

        int res_id = a.getResourceId(R.styleable.BirdDigitalClockView_time_res, 0);
        mTimeDrawableArray = r.obtainTypedArray(res_id);
        int size = mTimeDrawableArray.length();
        mHourHigh = mTimeDrawableArray.getDrawable(0);
        mHourLow = mTimeDrawableArray.getDrawable(1);
        mMinuteHigh = mTimeDrawableArray.getDrawable(2);
        mMinuteLow = mTimeDrawableArray.getDrawable(3);
        mDivider = mTimeDrawableArray.getDrawable(size - 1);
        a.recycle();
          2)在onAttachedToWindow方法中注册广播
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_TIME_TICK);
        filter.addAction(Intent.ACTION_TIME_CHANGED);
        filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
        getContext().registerReceiver(mIntentReceiver, filter, null, mHandler);
          3)相应的在onDetachedFromWindow取消广播

        if (mAttached) {
            getContext().unregisterReceiver(mIntentReceiver);
            mAttached = false;
        }
          4)在广播中监听,收到相应的广播就更新界面

        if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) {
            String tz = intent.getStringExtra("time-zone");
            mCalendar = new Time(TimeZone.getTimeZone(tz).getID());
        }
        onTimeChanged();
        invalidate();
          5)最后最终代码如下:

package com.example.myclockviewtest;

import java.util.Calendar;
import java.util.TimeZone;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.text.format.DateFormat;
import android.text.format.Time;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

/**
 * 自绘控件
 */
public class BirdDigitalClockView extends View {
    private Time mCalendar;

    private Drawable mHourHigh;
    private Drawable mHourLow;
    private Drawable mMinuteHigh;
    private Drawable mMinuteLow;
    private Drawable mDivider;

    private TypedArray mTimeDrawableArray;

    private boolean mAttached;

    private final Handler mHandler = new Handler();
    private boolean mChanged;

    public BirdDigitalClockView(Context context) {
        this(context, null);
    }

    public BirdDigitalClockView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public BirdDigitalClockView(Context context, AttributeSet attrs,
            int defStyle) {
        super(context, attrs, defStyle);
        Resources r = context.getResources();
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.BirdDigitalClockView, defStyle, 0);

        int res_id = a.getResourceId(R.styleable.BirdDigitalClockView_time_res, 0);
        mTimeDrawableArray = r.obtainTypedArray(res_id);
        int size = mTimeDrawableArray.length();
        mHourHigh = mTimeDrawableArray.getDrawable(0);
        mHourLow = mTimeDrawableArray.getDrawable(1);
        mMinuteHigh = mTimeDrawableArray.getDrawable(2);
        mMinuteLow = mTimeDrawableArray.getDrawable(3);
        mDivider = mTimeDrawableArray.getDrawable(size - 1);
        a.recycle();
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();

        if (!mAttached) {
            mAttached = true;
            IntentFilter filter = new IntentFilter();

            filter.addAction(Intent.ACTION_TIME_TICK);
            filter.addAction(Intent.ACTION_TIME_CHANGED);
            filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);

            getContext().registerReceiver(mIntentReceiver, filter, null, mHandler);
        }

        mCalendar = new Time();

        onTimeChanged();
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (mAttached) {
            getContext().unregisterReceiver(mIntentReceiver);
            mAttached = false;
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        boolean changed = mChanged;
        if (changed) {
            mChanged = false;
        }

        int availableWidth = getMeasuredWidth();
        

        int w = 0;
        // HourHigh
        final Drawable hourHigh = mHourHigh;
        int mHourHighWidth = hourHigh.getIntrinsicWidth();
        int mHourHighHeight = hourHigh.getIntrinsicHeight();

        // HourLow
        final Drawable hourLow = mHourLow;
        int mHourLowWidth = hourLow.getIntrinsicWidth();
        int mHourLowHeight = hourLow.getIntrinsicHeight();

        // dot
        final Drawable divider = mDivider;
        int mDividerWidth = divider.getIntrinsicWidth();

        // MinuteHigh
        final Drawable minuteHigh = mMinuteHigh;
        int mMinuteHighWidth = minuteHigh.getIntrinsicWidth();
        int mMinuteHighHeight = minuteHigh.getIntrinsicHeight();

        // MinuteLow
        final Drawable minuteLow = mMinuteLow;
        int mMinuteLowWidth = minuteLow.getIntrinsicWidth();
        int mMinuteLowHeight = minuteLow.getIntrinsicHeight();

        // because all drawables's height is same
        int height = mMinuteLowHeight;

        int temp = availableWidth / 2 - divider.getIntrinsicWidth() / 2;

        int temp_ = temp;

        canvas.save();

        // dot
        if (changed) {
            w = divider.getIntrinsicWidth();

            divider.setBounds(temp, 0, temp + w, height);

            temp = temp + w;
        }
        divider.draw(canvas);
        canvas.restore();

        canvas.save();

        // MinuteHigh
        if (changed) {
            w = minuteHigh.getIntrinsicWidth();

            minuteHigh.setBounds(temp, 0, temp + w, height);

            temp = temp + w;
        }
        minuteHigh.draw(canvas);
        canvas.restore();

        canvas.save();
        // MinuteLow
        if (changed) {
            w = minuteLow.getIntrinsicWidth();

            minuteLow.setBounds(temp, 0, temp + w, height);

            temp = temp_;
        }
        minuteLow.draw(canvas);
        canvas.restore();

        canvas.save();

        // hourLow
        if (changed) {
            w = hourLow.getIntrinsicWidth();

            hourLow.setBounds(temp - w, 0, temp, height);

            temp = temp - w;
        }
        hourLow.draw(canvas);
        canvas.restore();

        canvas.save();

        // HourHigh
        if (changed) {
            w = hourHigh.getIntrinsicWidth();
            hourHigh.setBounds(temp - w, 0, temp, height);
        }
        hourHigh.draw(canvas);
        canvas.restore();

        canvas.save();

    }

    private void onTimeChanged() {
        mCalendar.setToNow();
        mChanged = true;
        updateTime();
    }

    private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) {
                String tz = intent.getStringExtra("time-zone");
                mCalendar = new Time(TimeZone.getTimeZone(tz).getID());
            }
            onTimeChanged();
            invalidate();
        }
    };

    private void updateTime() {

        final Calendar myCalendar = Calendar.getInstance(TimeZone.getDefault());
        myCalendar.setTimeInMillis(System.currentTimeMillis());

        int my_hour_high = 0;
        int my_hour_low = 0;
        int my_min_high = 0;
        int my_min_low = 0;

        int my_Hour = myCalendar.get(Calendar.HOUR_OF_DAY);
        int my_Minute = myCalendar.get(Calendar.MINUTE);

        if (DateFormat.is24HourFormat(getContext())) {
            if (my_Hour == 24) {
                my_Hour = 0;
            }
            my_hour_high = my_Hour / 10;
            my_hour_low = my_Hour % 10;
        } else {
            if (my_Hour <= 12) {
                if (my_Hour == 0) {
                    my_Hour = 12;
                    my_hour_high = my_Hour / 10;
                    my_hour_low = my_Hour % 10;
                } else {
                    my_hour_high = my_Hour / 10;
                    my_hour_low = my_Hour % 10;
                }
            } else {
                my_Hour = my_Hour - 12;
                my_hour_high = my_Hour / 10;
                my_hour_low = my_Hour % 10;
            }
        }
        my_min_high = my_Minute / 10;
        my_min_low = my_Minute % 10;

        mHourHigh = mTimeDrawableArray.getDrawable(my_hour_high);
        mHourLow = mTimeDrawableArray.getDrawable(my_hour_low);
        mMinuteHigh = mTimeDrawableArray.getDrawable(my_min_high);
        mMinuteLow = mTimeDrawableArray.getDrawable(my_min_low);

    }

}
(2)组合控件:本例中组合控件和自绘控件最大的不同点就是:视图的绘制不需要自己去实现,包括大小、布局;但是关键点还是一样的,直接上代码:

package com.example.myclockviewtest;

import java.util.Calendar;
import java.util.TimeZone;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.text.format.DateFormat;
import android.text.format.Time;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.ImageView;
import android.widget.LinearLayout;
/*
 * 组合控件
 */
public class BirdDigitalClockView2 extends LinearLayout {

    private ImageView hour;
    private ImageView hour_2;
    private ImageView dot;
    private ImageView minute;
    private ImageView minute_2;

    private TypedArray mTimeDrawableArray;

    private Drawable mHourHigh;
    private Drawable mHourLow;
    private Drawable mMinuteHigh;
    private Drawable mMinuteLow;
    private Drawable mDivider;

    private Time mCalendar;
    private boolean mAttached = false;

    private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) {
                String tz = intent.getStringExtra("time-zone");
                mCalendar = new Time(TimeZone.getTimeZone(tz).getID());
            }

            onTimeChanged();

            invalidate();
        }
    };

    public BirdDigitalClockView2(Context context) {
        this(context, null);
    }

    public BirdDigitalClockView2(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public BirdDigitalClockView2(Context context, AttributeSet attrs,
            int defStyle) {
        super(context, attrs, defStyle);
        LayoutInflater.from(context).inflate(R.layout.clock, this);
        hour = (ImageView) findViewById(R.id.hour);
        hour_2 = (ImageView) findViewById(R.id.hour_2);
        dot = (ImageView) findViewById(R.id.dot);
        minute = (ImageView) findViewById(R.id.minute);
        minute_2 = (ImageView) findViewById(R.id.minute_2);

        Resources r = context.getResources();
        TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.BirdDigitalClockView2, defStyle, 0);
        int res_id = a.getResourceId(
                R.styleable.BirdDigitalClockView2_time_res2, 0);
        mTimeDrawableArray = r.obtainTypedArray(res_id);
        int size = mTimeDrawableArray.length();
        mHourHigh = mTimeDrawableArray.getDrawable(0);
        mHourLow = mTimeDrawableArray.getDrawable(1);
        mMinuteHigh = mTimeDrawableArray.getDrawable(2);
        mMinuteLow = mTimeDrawableArray.getDrawable(3);
        mDivider = mTimeDrawableArray.getDrawable(size - 1);
        a.recycle();
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        if (!mAttached) {
            mAttached = true;
            IntentFilter filter = new IntentFilter();

            filter.addAction(Intent.ACTION_TIME_TICK);
            filter.addAction(Intent.ACTION_TIME_CHANGED);
            filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);

            getContext().registerReceiver(mIntentReceiver, filter, null, null);
        }

        mCalendar = new Time();

        onTimeChanged();
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (mAttached) {
            getContext().unregisterReceiver(mIntentReceiver);
            mAttached = false;
        }
    }

    private void onTimeChanged() {
        mCalendar.setToNow();
        updateTime();
    }

    private void updateTime() {

        final Calendar myCalendar = Calendar.getInstance(TimeZone.getDefault());
        myCalendar.setTimeInMillis(System.currentTimeMillis());

        int my_hour_high = 0;
        int my_hour_low = 0;
        int my_min_high = 0;
        int my_min_low = 0;

        int my_Hour = myCalendar.get(Calendar.HOUR_OF_DAY);
        int my_Minute = myCalendar.get(Calendar.MINUTE);

        if (DateFormat.is24HourFormat(getContext())) {
            if (my_Hour == 24) {
                my_Hour = 0;
            }
            my_hour_high = my_Hour / 10;
            my_hour_low = my_Hour % 10;
        } else {
            if (my_Hour <= 12) {
                if (my_Hour == 0) {
                    my_Hour = 12;
                    my_hour_high = my_Hour / 10;
                    my_hour_low = my_Hour % 10;
                } else {
                    my_hour_high = my_Hour / 10;
                    my_hour_low = my_Hour % 10;
                }
            } else {
                my_Hour = my_Hour - 12;
                my_hour_high = my_Hour / 10;
                my_hour_low = my_Hour % 10;
            }
        }
        my_min_high = my_Minute / 10;
        my_min_low = my_Minute % 10;
        mHourHigh = mTimeDrawableArray.getDrawable(my_hour_high);
        mHourLow = mTimeDrawableArray.getDrawable(my_hour_low);
        mMinuteHigh = mTimeDrawableArray.getDrawable(my_min_high);
        mMinuteLow = mTimeDrawableArray.getDrawable(my_min_low);

        hour.setImageDrawable(mHourHigh);
        hour_2.setImageDrawable(mHourLow);
        minute.setImageDrawable(mMinuteHigh);
        minute_2.setImageDrawable(mMinuteLow);
    }

}
(3)最后写好了控件只需要在xml文件中直接引用就可以了,例如:

   <com.example.myclockviewtest.BirdDigitalClockView2
        android:id="@+id/digital_clock"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical|center_horizontal"
        myapp:time_res2="@array/clock_widget_drawables" >
   </com.example.myclockviewtest.BirdDigitalClockView2>
       说明:1)myapp是一个命名空间,在xml的上面有定义,例如:xmlns:myapp="http://schemas.android.com/apk/res/com.example.myclockviewtest",其中http://schemas.android.com/apk/res/是固定的,后面是自定义类的包名。
                   2)time_res2这是一个自定义的属性,首先需要在attrs.xml文件中进行属性的声明:
           <?xml version="1.0" encoding="utf-8"?>
           <resources>
               <declare-styleable name="BirdDigitalClockView">
                   <attr name="time_res" format="reference" />
               </declare-styleable>
               <declare-styleable name="BirdDigitalClockView2">
                   <attr name="time_res2" format="reference" />
               </declare-styleable>
           </resources>
.....................................................................................................易水寒.....................................................................................................

                



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值