android 闹钟的实现

转载  http://blog.csdn.net/wdaming1986/article/details/7461043

 

Android闹钟最终版【android源码闹钟解析】

分类: android源码解析   43973人阅读  评论(484)  收藏  举报

       我以前写了个复杂闹钟的demo,参见Android闹钟【复杂版】【大明进化十五】 .但是里面的bug有一些,好多人留言,所以我就看看源码,找找原因?顺便把源码代码整理出来,弄成一个完整的可以运行的apk,今天上午就整理了一下,才发现,源码处理的逻辑多一些,考虑的情况多,比如开机后接收一个广播,然后从数据库中取时间和当前时间对比,设置闹钟,当时区改变了,也会发送广播,对闹钟进行设置的。

        转载请标明出处:http://blog.csdn.net/wdaming1986/article/details/7461043       

        有人问我:“当设置一个闹钟后,然后调系统时间超过设置的闹钟的时间,这时候闹钟响了??”

         通过源码,我发现这种情况是正常的。不是bug。可以理解。

         以下是我发现的几点闹钟中重要的点,分享一下:

         (1)在闹钟中有AudioManager管理机制,这个机制可以申请和释放OnAudioFocusChangeListener监听。

还有mTelephonyManager对象,处理在闹钟响的时候,来电铃声的切换。

         (2)广播接收闹钟,通过广播启动AlarmKlaxon这个Service,隐式启动service:

public static final String ALARM_INTENT_EXTRA = "intent.extra.alarm";

[java]  view plain copy print ?
  1. // Play the alarm alert and vibrate the device.  
  2.         Intent playAlarm = new Intent(Alarms.ALARM_ALERT_ACTION);  
  3.         playAlarm.putExtra(Alarms.ALARM_INTENT_EXTRA, alarm);  
  4.         context.startService(playAlarm);  

   在mainfest中,AlarmKlaxon这个服务的定义如下:

[java]  view plain copy print ?
  1. <service android:name="AlarmKlaxon"  
  2.                 android:description="@string/alarm_klaxon_service_desc"  
  3.                 >  
  4.             <intent-filter>  
  5.                 <action android:name="com.cn.daming.deskclock.ALARM_ALERT" />  
  6.             </intent-filter>  
  7.         </service>  

这个service做的是允许别的Activity打断正在响铃的铃声,播放其他的铃声,例如,闹钟响的时候来电话了。

        (3)在listview中包含checkbox,这时候闹钟的处理时,activity实现一个OnItemClickListener的监听,点击每一项的监听。然后在checkbox单独拿出去写一个类,继承LinearLayout,重写setPressed()这个方法,以实现“当点击checkbox的时候不触发parent的click事件”。关键代码如下:

[java]  view plain copy print ?
  1. <span style="font-size:18px;"@Override  
  2.     public void setPressed(boolean pressed) {  
  3.         // If the parent is pressed, do not set to pressed.  
  4.         if (pressed && ((View) getParent()).isPressed()) {  
  5.             return;  
  6.         }  
  7.         super.setPressed(pressed);  
  8.     }</span>  

 

下面看看我的程序截图:

                    红色圈的图标为我的闹钟。                                         点击“玲闹钟”后的界面

                                  

                      点击新建闹钟出现的界面                                           设置好时间弹出的toast。

                                  

下面我把我的主要入口类的代码贴出来

DeskClockMainActivity.java

[java]  view plain copy print ?
  1. <span style="font-size:18px;">package com.cn.daming.deskclock;  
  2.   
  3. import java.util.Calendar;  
  4.   
  5. import android.app.Activity;  
  6. import android.app.AlertDialog;  
  7. import android.content.Context;  
  8. import android.content.DialogInterface;  
  9. import android.content.Intent;  
  10. import android.content.SharedPreferences;  
  11. import android.database.Cursor;  
  12. import android.graphics.Typeface;  
  13. import android.os.Bundle;  
  14. import android.view.LayoutInflater;  
  15. import android.view.Menu;  
  16. import android.view.MenuItem;  
  17. import android.view.View;  
  18. import android.view.ContextMenu;  
  19. import android.view.ContextMenu.ContextMenuInfo;  
  20. import android.view.View.OnClickListener;  
  21. import android.view.ViewGroup;  
  22. import android.widget.AdapterView;  
  23. import android.widget.AdapterView.AdapterContextMenuInfo;  
  24. import android.widget.AdapterView.OnItemClickListener;  
  25. import android.widget.CheckBox;  
  26. import android.widget.CursorAdapter;  
  27. import android.widget.ImageButton;  
  28. import android.widget.ImageView;  
  29. import android.widget.ListView;  
  30. import android.widget.TextView;  
  31.   
  32. public class DeskClockMainActivity extends Activity implements OnItemClickListener{  
  33.       
  34.     static final String PREFERENCES = "AlarmClock";  
  35.   
  36.     /** This must be false for production.  If true, turns on logging, 
  37.         test code, etc. */  
  38.     static final boolean DEBUG = false;  
  39.   
  40.     private SharedPreferences mPrefs;  
  41.     private LayoutInflater mFactory;  
  42.     private ListView mAlarmsList;  
  43.     private Cursor mCursor;  
  44.       
  45.     @Override  
  46.     public void onCreate(Bundle savedInstanceState) {  
  47.         super.onCreate(savedInstanceState);  
  48.           
  49.         //取自定义布局的LayoutInflater  
  50.         mFactory = LayoutInflater.from(this);  
  51.         //取getSharedPreferences中key==“AlarmClock”的值  
  52.         mPrefs = getSharedPreferences(PREFERENCES, 0);  
  53.         //获取闹钟的cursor  
  54.         mCursor = Alarms.getAlarmsCursor(getContentResolver());  
  55.           
  56.         //更新布局界面  
  57.         updateLayout();  
  58.   
  59.     }  
  60.       
  61.     //加载更新界面布局  
  62.     private void updateLayout() {  
  63.         setContentView(R.layout.alarm_clock);  
  64.         mAlarmsList = (ListView) findViewById(R.id.alarms_list);  
  65.         AlarmTimeAdapter adapter = new AlarmTimeAdapter(this, mCursor);  
  66.         mAlarmsList.setAdapter(adapter);  
  67.         mAlarmsList.setVerticalScrollBarEnabled(true);  
  68.         mAlarmsList.setOnItemClickListener(this);  
  69.         mAlarmsList.setOnCreateContextMenuListener(this);  
  70.   
  71.         View addAlarm = findViewById(R.id.add_alarm);  
  72.         addAlarm.setOnClickListener(new View.OnClickListener() {  
  73.                 public void onClick(View v) {  
  74.                     addNewAlarm();  
  75.                 }  
  76.             });  
  77.         // Make the entire view selected when focused.  
  78.         addAlarm.setOnFocusChangeListener(new View.OnFocusChangeListener() {  
  79.                 public void onFocusChange(View v, boolean hasFocus) {  
  80.                     v.setSelected(hasFocus);  
  81.                 }  
  82.         });  
  83.   
  84.         ImageButton deskClock =  
  85.                 (ImageButton) findViewById(R.id.desk_clock_button);  
  86.         deskClock.setOnClickListener(new View.OnClickListener() {  
  87.                 public void onClick(View v) {  
  88.                       
  89.                 }  
  90.         });  
  91.     }  
  92.       
  93.     private void addNewAlarm() {  
  94.         startActivity(new Intent(this, SetAlarm.class));  
  95.     }  
  96.       
  97.     /** 
  98.      * listview的适配器继承CursorAdapter 
  99.      * @author wangxianming 
  100.      * 也可以使用BaseAdapter 
  101.      */  
  102.     private class AlarmTimeAdapter extends CursorAdapter {  
  103.         public AlarmTimeAdapter(Context context, Cursor cursor) {  
  104.             super(context, cursor);  
  105.         }  
  106.   
  107.         public View newView(Context context, Cursor cursor, ViewGroup parent) {  
  108.             View ret = mFactory.inflate(R.layout.alarm_time, parent, false);  
  109.   
  110.             DigitalClock digitalClock =  
  111.                     (DigitalClock) ret.findViewById(R.id.digitalClock);  
  112.             digitalClock.setLive(false);  
  113.             return ret;  
  114.         }  
  115.   
  116.         //把view绑定cursor的每一项  
  117.         public void bindView(View view, Context context, Cursor cursor) {  
  118.             final Alarm alarm = new Alarm(cursor);  
  119.   
  120.             View indicator = view.findViewById(R.id.indicator);  
  121.   
  122.             // Set the initial resource for the bar image.  
  123.             final ImageView barOnOff =  
  124.                     (ImageView) indicator.findViewById(R.id.bar_onoff);  
  125.             barOnOff.setImageResource(alarm.enabled ?  
  126.                     R.drawable.ic_indicator_on : R.drawable.ic_indicator_off);  
  127.   
  128.             // Set the initial state of the clock "checkbox"  
  129.             final CheckBox clockOnOff =  
  130.                     (CheckBox) indicator.findViewById(R.id.clock_onoff);  
  131.             clockOnOff.setChecked(alarm.enabled);  
  132.   
  133.             // Clicking outside the "checkbox" should also change the state.  
  134.             //对checkbox设置监听,使里外一致  
  135.             indicator.setOnClickListener(new OnClickListener() {  
  136.                     public void onClick(View v) {  
  137.                         clockOnOff.toggle();  
  138.                         updateIndicatorAndAlarm(clockOnOff.isChecked(),  
  139.                                 barOnOff, alarm);  
  140.                     }  
  141.             });  
  142.   
  143.             DigitalClock digitalClock =  
  144.                     (DigitalClock) view.findViewById(R.id.digitalClock);  
  145.   
  146.             // set the alarm text  
  147.             final Calendar c = Calendar.getInstance();  
  148.             c.set(Calendar.HOUR_OF_DAY, alarm.hour);  
  149.             c.set(Calendar.MINUTE, alarm.minutes);  
  150.             digitalClock.updateTime(c);  
  151.             digitalClock.setTypeface(Typeface.DEFAULT);  
  152.   
  153.             // Set the repeat text or leave it blank if it does not repeat.  
  154.             TextView daysOfWeekView =  
  155.                     (TextView) digitalClock.findViewById(R.id.daysOfWeek);  
  156.             final String daysOfWeekStr =  
  157.                     alarm.daysOfWeek.toString(DeskClockMainActivity.thisfalse);  
  158.             if (daysOfWeekStr != null && daysOfWeekStr.length() != 0) {  
  159.                 daysOfWeekView.setText(daysOfWeekStr);  
  160.                 daysOfWeekView.setVisibility(View.VISIBLE);  
  161.             } else {  
  162.                 daysOfWeekView.setVisibility(View.GONE);  
  163.             }  
  164.   
  165.             // Display the label  
  166.             TextView labelView =  
  167.                     (TextView) view.findViewById(R.id.label);  
  168.             if (alarm.label != null && alarm.label.length() != 0) {  
  169.                 labelView.setText(alarm.label);  
  170.                 labelView.setVisibility(View.VISIBLE);  
  171.             } else {  
  172.                 labelView.setVisibility(View.GONE);  
  173.             }  
  174.         }  
  175.     };  
  176.       
  177.     //更新checkbox  
  178.     private void updateIndicatorAndAlarm(boolean enabled, ImageView bar,  
  179.             Alarm alarm) {  
  180.         bar.setImageResource(enabled ? R.drawable.ic_indicator_on  
  181.                 : R.drawable.ic_indicator_off);  
  182.         Alarms.enableAlarm(this, alarm.id, enabled);  
  183.         if (enabled) {  
  184.             SetAlarm.popAlarmSetToast(this, alarm.hour, alarm.minutes,  
  185.                     alarm.daysOfWeek);  
  186.         }  
  187.     }  
  188.       
  189.     /* 
  190.      * (non-Javadoc) 
  191.      * @see android.app.Activity#onContextItemSelected(android.view.MenuItem) 
  192.      * 创建上下文菜单 
  193.      */  
  194.     @Override  
  195.     public boolean onContextItemSelected(final MenuItem item) {  
  196.         final AdapterContextMenuInfo info =  
  197.                 (AdapterContextMenuInfo) item.getMenuInfo();  
  198.         final int id = (int) info.id;  
  199.         // Error check just in case.  
  200.         if (id == -1) {  
  201.             return super.onContextItemSelected(item);  
  202.         }  
  203.         switch (item.getItemId()) {  
  204.             case R.id.delete_alarm:  
  205.                 // Confirm that the alarm will be deleted.  
  206.                 new AlertDialog.Builder(this)  
  207.                         .setTitle(getString(R.string.delete_alarm))  
  208.                         .setMessage(getString(R.string.delete_alarm_confirm))  
  209.                         .setPositiveButton(android.R.string.ok,  
  210.                                 new DialogInterface.OnClickListener() {  
  211.                                     public void onClick(DialogInterface d,  
  212.                                             int w) {  
  213.                                         Alarms.deleteAlarm(DeskClockMainActivity.this, id);  
  214.                                     }  
  215.                                 })  
  216.                         .setNegativeButton(android.R.string.cancel, null)  
  217.                         .show();  
  218.                 return true;  
  219.   
  220.             case R.id.enable_alarm:  
  221.                 final Cursor c = (Cursor) mAlarmsList.getAdapter()  
  222.                         .getItem(info.position);  
  223.                 final Alarm alarm = new Alarm(c);  
  224.                 Alarms.enableAlarm(this, alarm.id, !alarm.enabled);  
  225.                 if (!alarm.enabled) {  
  226.                     SetAlarm.popAlarmSetToast(this, alarm.hour, alarm.minutes,  
  227.                             alarm.daysOfWeek);  
  228.                 }  
  229.                 return true;  
  230.   
  231.             case R.id.edit_alarm:  
  232.                 Intent intent = new Intent(this, SetAlarm.class);  
  233.                 intent.putExtra(Alarms.ALARM_ID, id);  
  234.                 startActivity(intent);  
  235.                 return true;  
  236.   
  237.             default:  
  238.                 break;  
  239.         }  
  240.         return super.onContextItemSelected(item);  
  241.     }  
  242.       
  243.     /* 
  244.      * (non-Javadoc) 
  245.      * @see android.app.Activity#onCreateContextMenu(android.view.ContextMenu, android.view.View, android.view.ContextMenu.ContextMenuInfo) 
  246.      * 创建菜单 
  247.      */  
  248.     @Override  
  249.     public void onCreateContextMenu(ContextMenu menu, View view,  
  250.             ContextMenuInfo menuInfo) {  
  251.         // Inflate the menu from xml.  
  252.         getMenuInflater().inflate(R.menu.context_menu, menu);  
  253.   
  254.         // Use the current item to create a custom view for the header.  
  255.         final AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;  
  256.         final Cursor c =  
  257.                 (Cursor) mAlarmsList.getAdapter().getItem((int) info.position);  
  258.         final Alarm alarm = new Alarm(c);  
  259.   
  260.         // Construct the Calendar to compute the time.  
  261.         final Calendar cal = Calendar.getInstance();  
  262.         cal.set(Calendar.HOUR_OF_DAY, alarm.hour);  
  263.         cal.set(Calendar.MINUTE, alarm.minutes);  
  264.         final String time = Alarms.formatTime(this, cal);  
  265.   
  266.         // Inflate the custom view and set each TextView's text.  
  267.         final View v = mFactory.inflate(R.layout.context_menu_header, null);  
  268.         TextView textView = (TextView) v.findViewById(R.id.header_time);  
  269.         textView.setText(time);  
  270.         textView = (TextView) v.findViewById(R.id.header_label);  
  271.         textView.setText(alarm.label);  
  272.   
  273.         // Set the custom view on the menu.  
  274.         menu.setHeaderView(v);  
  275.         // Change the text based on the state of the alarm.  
  276.         if (alarm.enabled) {  
  277.             menu.findItem(R.id.enable_alarm).setTitle(R.string.disable_alarm);  
  278.         }  
  279.     }  
  280.       
  281.     /* 
  282.      * (non-Javadoc) 
  283.      * @see android.app.Activity#onOptionsItemSelected(android.view.MenuItem) 
  284.      * 设置菜单的点击事件的处理 
  285.      */  
  286.     @Override  
  287.     public boolean onOptionsItemSelected(MenuItem item) {  
  288.         switch (item.getItemId()) {  
  289.             case R.id.menu_item_settings:  
  290.                 startActivity(new Intent(this, SettingsActivity.class));  
  291.                 return true;  
  292.             case R.id.menu_item_desk_clock:  
  293.                 //modify by wangxianming in 2012-4-14  
  294. //                startActivity(new Intent(this, DeskClock.class));  
  295.                 return true;  
  296.             case R.id.menu_item_add_alarm:  
  297.                 addNewAlarm();  
  298.                 return true;  
  299.             default:  
  300.                 break;  
  301.         }  
  302.         return super.onOptionsItemSelected(item);  
  303.     }  
  304.      
  305.     /* 
  306.      * (non-Javadoc) 
  307.      * @see android.app.Activity#onCreateOptionsMenu(android.view.Menu) 
  308.      * 创建菜单 
  309.      */  
  310.     @Override  
  311.     public boolean onCreateOptionsMenu(Menu menu) {  
  312.         getMenuInflater().inflate(R.menu.alarm_list_menu, menu);  
  313.         return super.onCreateOptionsMenu(menu);  
  314.     }  
  315.       
  316.     /* 
  317.      * (non-Javadoc) 
  318.      * @see android.widget.AdapterView.OnItemClickListener#onItemClick(android.widget.AdapterView, android.view.View, int, long) 
  319.      * 创建菜单的点击事件响应 
  320.      */  
  321.     public void onItemClick(AdapterView<?> adapterView, View v, int pos, long id) {  
  322.         Intent intent = new Intent(this, SetAlarm.class);  
  323.         intent.putExtra(Alarms.ALARM_ID, (int) id);  
  324.         startActivity(intent);  
  325.           
  326.     }  
  327.       
  328.    @Override  
  329.     protected void onDestroy() {  
  330.         super.onDestroy();  
  331.         ToastMaster.cancelToast();  
  332.         mCursor.close();  
  333.     }  
  334. }</span>  

 

【说明】想要源码的可以留下邮箱,我看到后就给你发过去!

也可以到我的csdn资源中下载:http://download.csdn.net/detail/wdaming1986/4226174


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值