自定义View做的一个Clock案例

这是整个项目的结构。下来看看My_Clock.java文件内容

My_Clock.java Code:
  1. package com.android.yhb; 
  2.  
  3. import android.app.Activity; 
  4. import android.os.Bundle; 
  5.  
  6. public class My_Clock extends Activity { 
  7.     /** Called when the activity is first created. */ 
  8.     @Override 
  9.     public void onCreate(Bundle savedInstanceState) { 
  10.         super.onCreate(savedInstanceState); 
  11.         setContentView(R.layout.main); 
  12.     } 

My_ClockView.java的代码:

My_ClockView.Java  Code:
  1. package com.android.yhb; 
  2.  
  3. import java.util.TimeZone; 
  4.  
  5. import android.content.BroadcastReceiver; 
  6. import android.content.Context; 
  7. import android.content.Intent; 
  8. import android.content.IntentFilter; 
  9. import android.content.res.Resources; 
  10. import android.graphics.Canvas; 
  11. import android.graphics.drawable.Drawable; 
  12. import android.os.Handler; 
  13. import android.text.format.Time; 
  14. import android.util.AttributeSet; 
  15. import android.view.View; 
  16.  
  17. public class My_ClockView extends View { 
  18.  
  19.     private Time mCalendar; 
  20.      
  21.     //表盘上的给指针图片 
  22.     private Drawable mHourHand; 
  23.     private Drawable mMinuteHand; 
  24.     private Drawable mDial; 
  25.      
  26.     private boolean mAttached; //附着状态 
  27.      
  28.     private final Handler mHandler = new Handler(); //定一个Handler类实现更新时间 
  29.  
  30.     //定义表盘的宽和高 
  31.     private int mDialWidth; 
  32.     private int mDialHeight; 
  33.      
  34.     private float mMinute; 
  35.     private float mHour; 
  36.     private boolean mChanged; 
  37.      
  38.     public My_ClockView(Context context) { 
  39.         this(context, null); 
  40.         // TODO Auto-generated constructor stub 
  41.     } 
  42.      
  43.     public My_ClockView(Context context, AttributeSet attrs) { 
  44.         this(context, attrs, 0); 
  45.         // TODO Auto-generated constructor stub 
  46.     } 
  47.  
  48.     public My_ClockView(Context context, AttributeSet attrs, int defStyle) { 
  49.         super(context, attrs, defStyle); 
  50.         // TODO Auto-generated constructor stub 
  51.         Resources r = context.getResources(); 
  52.          
  53.         //通过资源加载各图片,用于绘制时钟 
  54.         mDial = r.getDrawable(R.drawable.clock); 
  55.         mHourHand = r.getDrawable(R.drawable.hour_hander); 
  56.         mMinuteHand = r.getDrawable(R.drawable.minute_hander); 
  57.          
  58.         mCalendar = new Time(); 
  59.         mDialWidth = mDial.getIntrinsicWidth(); 
  60.         mDialHeight = mDial.getIntrinsicHeight(); 
  61.     } 
  62.      
  63.      @Override 
  64.         protected void onAttachedToWindow() { 
  65.             super.onAttachedToWindow(); 
  66.             if (!mAttached) { 
  67.                 mAttached = true
  68.                 IntentFilter filter = new IntentFilter(); //注册一个消息过滤器,获取时间改变、时区改变的action 
  69.                 filter.addAction(Intent.ACTION_TIME_TICK);  
  70.                 filter.addAction(Intent.ACTION_TIME_CHANGED); 
  71.                 filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); 
  72.                 getContext().registerReceiver(mIntentReceiver, filter, null, mHandler); 
  73.             } 
  74.               mCalendar = new Time(); 
  75.             onTimeChanged(); 
  76.         } 
  77.         @Override 
  78.         protected void onDetachedFromWindow() { 
  79.             super.onDetachedFromWindow(); 
  80.             if (mAttached) { 
  81.                 getContext().unregisterReceiver(mIntentReceiver); //反注册消息过滤器 
  82.                 mAttached = false
  83.             } 
  84.         } 
  85.  
  86.  
  87.     @Override 
  88.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
  89.         // TODO Auto-generated method stub 
  90.         //取得组件的宽和高,以及指定模式 
  91.         int widthMode = MeasureSpec.getMode(widthMeasureSpec); 
  92.         int widthSize = MeasureSpec.getSize(widthMeasureSpec); 
  93.         int heightMode = MeasureSpec.getMode(heightMeasureSpec); 
  94.         int heightSize = MeasureSpec.getSize(heightMeasureSpec); 
  95.          
  96.         float hScale = 1.0f; 
  97.         float vScale = 1.0f; 
  98.          
  99.         if(widthMode != MeasureSpec.UNSPECIFIED && widthSize < mDialWidth) { 
  100.             hScale = (float)widthSize/(float)mDialWidth; 
  101.         } 
  102.         if(heightMode != MeasureSpec.UNSPECIFIED && heightSize < mDialHeight) { 
  103.             vScale = (float)heightSize/(float)mDialHeight; 
  104.         } 
  105.          
  106.         //如果表盘图像的宽和高超出其组件的宽和高,即要进行相应的缩放 
  107.         float scale = Math.min(hScale, vScale); 
  108.         setMeasuredDimension(resolveSize((int)(mDialWidth*scale), widthMeasureSpec), resolveSize((int)(mDialHeight*scale), heightMeasureSpec)); 
  109.     } 
  110.      
  111.     //绘制组件的方法,使用Canvas对象绘制组件的具体表现 
  112.     @Override 
  113.     protected void onDraw(Canvas canvas) { 
  114.         // TODO Auto-generated method stub 
  115.         super.onDraw(canvas); 
  116.         //用changed标识来判断是否需要重新绘制 
  117.         boolean changed = mChanged; 
  118.         if(changed) { 
  119.             mChanged = false
  120.         } 
  121.          
  122.         final int mRight = getRight(); 
  123.         final int mLeft = getLeft(); 
  124.         final int mTop = getTop(); 
  125.         final int mBottom = getBottom(); 
  126.          
  127.         int availableWidth = mRight - mLeft; 
  128.         int availableHeight = mBottom - mTop; 
  129.          
  130.         int x = availableWidth/2
  131.         int y = availableHeight/2
  132.          
  133.         final Drawable dial = mDial; 
  134.         int w = dial.getIntrinsicWidth(); 
  135.         int h = dial.getIntrinsicHeight(); 
  136.         boolean scaled = false
  137.          
  138.         if(availableWidth < w || availableHeight < h) { 
  139.             scaled = true
  140.             float scale = Math.min((float)availableWidth/(float)w, (float)availableHeight/(float)h); 
  141.             canvas.save(); 
  142.              
  143.             canvas.scale(scale, scale, x, y); 
  144.         } 
  145.          
  146.         if(changed) { 
  147.             dial.setBounds(x-(w/2), y-(h/2), x+(w/2), y+(h/2));//设置界线 
  148.         } 
  149.          
  150.         dial.draw(canvas); 
  151.          
  152.         canvas.save(); 
  153.         canvas.rotate(mHour/12.0f*360.0f, x, y);//根据系统时间绘制时针旋转的角度 
  154.         final Drawable hourHand = mHourHand; 
  155.         if(changed) { 
  156.             w = hourHand.getIntrinsicWidth(); 
  157.             h = hourHand.getIntrinsicHeight(); 
  158.             hourHand.setBounds(x-(w/2), y-(h/2), x+(w/2), y+(h/2)); 
  159.         } 
  160.         hourHand.draw(canvas); 
  161.         canvas.restore(); 
  162.          
  163.         canvas.save(); 
  164.         canvas.rotate(mMinute / 60.0f * 360.0f, x, y); //同理,分针旋转的角度 
  165.         final Drawable minuteHand = mMinuteHand; 
  166.         if (changed) { 
  167.             w = minuteHand.getIntrinsicWidth(); 
  168.             h = minuteHand.getIntrinsicHeight(); 
  169.             minuteHand.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2)); 
  170.         } 
  171.         minuteHand.draw(canvas); 
  172.         canvas.restore(); 
  173.  
  174.         if(scaled) { 
  175.             canvas.restore(); 
  176.         } 
  177.     } 
  178.      
  179.     private void onTimeChanged() {  //获取时间改变,计算当前的时分秒 
  180.         mCalendar.setToNow(); 
  181.         int hour = mCalendar.hour; 
  182.         int minute = mCalendar.minute; 
  183.         int second = mCalendar.second; 
  184.         mMinute = minute + second / 60.0f; 
  185.         mHour = hour + mMinute / 60.0f; 
  186.         mChanged = true
  187.     } 
  188.  
  189.      
  190.     private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { //监听获取时间改变action 
  191.         @Override 
  192.         public void onReceive(Context context, Intent intent) { 
  193.             if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) { 
  194.                 String tz = intent.getStringExtra("time-zone"); 
  195.                 mCalendar = new Time(TimeZone.getTimeZone(tz).getID()); 
  196.             } 
  197.             onTimeChanged(); //获取新的时间 
  198.              
  199.             invalidate(); //刷新屏幕,强制类调用onDraw方法实现分针时针的走动 
  200.         } 
  201.     };  
  202.  

main.xml的代码:

Code:
  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  3.     android:orientation="vertical" 
  4.     android:layout_width="fill_parent" 
  5.     android:layout_height="fill_parent" 
  6.     android:background="@drawable/theme" 
  7.     android:gravity="center" 
  8.     > 
  9. <com.android.yhb.My_ClockView 
  10.     android:layout_width="200px" 
  11.     android:layout_height="200px" 
  12.     /> 
  13. </LinearLayout> 

上图是最终的运行结果!这个其实是OPhoneSDN里面的一个实例,做的时候遇到了一点小麻烦,不是技术上的问题,而是在找Clock素材的时候,我找的是一个完整的Clock,然后通过PS把它的时针和分针抠出来,抠出来保存的时候我并没有根据表盘的尺寸进行保存,而是直接把图片大小wrap时针/分针进行保存,出现了错误,导致分针/时针在表盘中散乱分部,还应注意一点,保存的图片需要旋转,使得时针/分针指向正上方,也就是在没有旋转之前让时针/分针指向12点这个位置,这样旋转出来的角度才会准确,进而做出来的Clock才能和真实的时间一致,大家是否注意到我的Clock的显示和真实的数字时间有微小的出入,正是因为我在旋转图片的时候没有准确旋转导致的……

对上面的代码进行一下简单的分析总结:1.关于onMeasure()方法,该方法里面传入的参数是widthMeasureSpec和heightMeasureSpec,可以通过这两个参数获取规定的宽和高以及模式。关于模式有三种:A、UNSPECIFIED说明容器对组件本身的尺寸没有任何限制,组件可以根据自己的需要随意规划自己的尺寸。这种情况下,容器提供的尺寸没有任何意义了;

B、EXACTLY说明容器严格要求其组件的尺寸必须为给定尺寸,不能自己决定尺寸大小;

C、AT_MOST说明容器提供的尺寸是一个最小值,也就是说,组件可以随意决定自己的尺寸,只要不大于容器指定的尺寸即可。可以通过方法public static int makeMeasureSpec(int size, int mode)进行定义模式。

关于onDraw()方法的主要步骤分析:大致是这样的,先比较dial/minuteHand/hourHand与View的大小,如果大的话对canvas进行缩放,然后接着就是设置setBounds();这个方法只要就是为了把图片画在制定的矩形区域里面,其实你可以任意设置这个区域的大小,当然图片也会跟着矩形区域的大小进行缩放,这里并没有进行缩放,即按照原图片的大小进行矩形区域的绘画,只是把这个矩形区域花在了view的中央,这样把限制在这个矩形区域里面的图片绘画在已经缩放过的cavans中的,达到了缩放效果……由于已经在画dial的之前对canvas进行了缩放,所以之后的hourHand/minuteHand直接画在canvas上,而无需再次惊醒缩放……这是我对这个过程的理解,有不对之处敬请大家的指教……

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值