接上一篇博文:``继续分析AlarmClock类的各个方法:
还是先从简单的开始吧:
(1)updateAlarm(),代码如下:
12345678 | private updateAlarm( boolean
Alarm alarm) { Alarms.enableAlarm( this , alarm.id, enabled); if
SetAlarm.popAlarmSetToast( this , alarm.hour, alarm.minutes, alarm.daysOfWeek); } } |
更新Alarm状态。
值得注意的上,在前一篇博文也出现了Alarms.enableAlarm()方法,
但是其实这个enableAlarm()方法实际上是根据enabled的状态来更新alarm的。
并不是单纯的启用alarm的。
如果enabled参数为false的话,实际是停用些alarm,
上面的方法,在设置的alarm状态之后。如果是启用的话,弹出一条Toast来提醒用户。
这个方法在用户点击闹钟列表项的选择框时调用。是一个点击事件的回调函数,在AlarmTimeAdapter中的bindView方法中,如下:
12345678910111213141516 | View // // final
(CheckBox) clockOnOff.setChecked(alarm.enabled); // // indicator.setOnClickListener( new
public onClick(View v) { clockOnOff.toggle(); // 切换选择框状态 updateAlarm(clockOnOff.isChecked(), alarm); //更新alarm状态。 } }); |
1234567891011121314 | <com.android.deskclock.DontPressWithParentLayout android:id= "@+id/indicator" style= "@style/alarm_list_left_column" android:gravity= "center" android:orientation= "vertical" > <CheckBox android:id= "@+id/clock_onoff" android:focusable= "false" android:clickable= "false" android:duplicateParentState= "true" android:layout_height= "wrap_content" android:layout_width= "wrap_content" android:layout_gravity= "center"
</com.android.deskclock.DontPressWithParentLayout> |
这个自定义的布局要达到的效果是,当你点击CheckBox与DontPressWithParentLayout之间的区域时,不需要引发其它的视觉效果,即不会改变整个DontPressWithParentLayout布局的背景。
代码如下:
123456789101112131415161718192021222324 | /** * 这是一个允许其父类本身被pressed但是不需要正在被pressed的状态。 这样一来,在alarm列表的时间可以被pressed但不需要改变indicator的背景。 =========================== * Special class to to allow the parent to be pressed without being pressed * itself. This way the time in the alarm list can be pressed without changing * the background of the indicator. */ public DontPressWithParentLayout extends public
super (context, attrs); } @Override public setPressed( boolean
// 如果父类类被pressed不要设置为pressed // If the parent is pressed, do not set to pressed. if
return ; } super .setPressed(pressed); } } |
(2)CursorAdapter子类AlarmTimeAdapter分析。
CursorAdapter类的简单介绍。
相信我们都对于BaseAdapter比较了解了吧,
那么先来看看CursorAdapter的签名吧,
123 | public class CursorAdapter extends
implements
CursorFilter.CursorFilterClient { } |
当我们扩展BaseAdapter的时候,我们主要是要重写BaseAdapter#getView()方法。
但是在CursorAdapter中这个方法它已经帮我们实现了,但是让我们去实现另外两个方法。
先看下CursorAdapter中的getView()方法吧,如下:
12345678910111213141516171819 | /** * @see android.widget.ListAdapter#getView(int, View, ViewGroup) */ public
int
if
throw IllegalStateException( "this should only be called when the cursor is valid" ); } if
throw IllegalStateException( "couldn't move cursor to position " + position); } View v; if
null ) { v = newView(mContext, mCursor, parent); } else
v = convertView; } bindView(v, mContext, mCursor); return
} |
然后,如果view为空则调用newView()构造一个view,否则使用之前构造的。
然后调用bindView()将数据绑定上去。OK,如果使用过BaseAdapter的话是不是很熟悉啊!
那下面再来仔细看看,newView()和bindView()这两个方法吧。
(2.1)public View newView(Context context,Cursor cursor,ViewGroup parent)
代码如下:
123456789 | @Override public
View ret = mFactory.inflate(R.layout.alarm_time, parent, false ); DigitalClock digitalClock = (DigitalClock) ret.findViewById(R.id.digitalClock); digitalClock.setLive( false ); return
} |
首先进入我们眼中的是DigitalClock,看下DigitalClock的类型,如下:
public class DigitalClock extends RelativeLayout
哦,原来是一个RelativeLayout的子类。对于DigitalClock我们就暂时了解这么多,后面会单独分析。
(2.2) public void bindView(View view, Context context, Cursor cursor)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354 | @Override public bindView(View view, Context context, Cursor cursor) { final
new View indicator = view.findViewById(R.id.indicator); // Set the initial state of the clock "checkbox" final
(CheckBox) indicator.findViewById(R.id.clock_onoff); clockOnOff.setChecked(alarm.enabled); // Clicking outside the "checkbox" should also change the state. indicator.setOnClickListener( new
public onClick(View v) { clockOnOff.toggle(); updateAlarm(clockOnOff.isChecked(), alarm); } }); // 上面部分的代码上updateAlarm()时已经有比较详细的分析了。 DigitalClock digitalClock = (DigitalClock) view.findViewById(R.id.digitalClock); // 设置闹钟显示的标签 final
c.set(Calendar.HOUR_OF_DAY, alarm.hour); c.set(Calendar.MINUTE, alarm.minutes); digitalClock.updateTime(c); // 设置重要闹钟的时间,如果不重要则不显示 // Set the repeat text or leave it blank if it does not repeat. TextView daysOfWeekView = (TextView) digitalClock.findViewById(R.id.daysOfWeek); final
alarm.daysOfWeek.toString(AlarmClock. this , false ); if
null
0 ) { daysOfWeekView.setText(daysOfWeekStr); daysOfWeekView.setVisibility(View.VISIBLE); } else
daysOfWeekView.setVisibility(View.GONE); } // 设置此闹钟的备注标签 // Display the label TextView labelView = (TextView) view.findViewById(R.id.label); if
null
0 ) { labelView.setText(alarm.label); labelView.setVisibility(View.VISIBLE); } else
labelView.setVisibility(View.GONE); } } }; |
下面的布局代码:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758 | <!-- if
-hour), and an optional line below, used for
<com.android.deskclock.DigitalClock android:id= "@+id/digitalClock" android:layout_width= "wrap_content" android:layout_height= "match_parent" android:gravity= "center_vertical" android:layout_weight= "1" android:layout_gravity= "center_vertical" android:orientation= "vertical" android:paddingLeft= "16dip" android:paddingRight= "16dip" > <LinearLayout android:id= "@+id/time_wrapper" android:layout_width= "match_parent" android:layout_height= "wrap_content" android:baselineAligned= "true" > <com.android.deskclock.AndroidClockTextView android:id= "@+id/timeDisplay" android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:paddingRight= "6dip" android:textAppearance= "?android:attr/textAppearanceMedium" useClockTypeface= "false" /> <com.android.deskclock.AndroidClockTextView android:id= "@+id/am_pm" android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:textAppearance= "?android:attr/textAppearanceMedium" android:paddingRight= "10dip" android:textStyle= "bold" useClockTypeface= "false" /> <TextView android:id= "@+id/label" android:layout_width= "0dip" android:layout_height= "wrap_content" android:layout_weight= "1" android:paddingLeft= "8dip" android:textAppearance= "?android:attr/textAppearanceSmall" android:textColor= "?android:attr/textColorSecondary" android:textStyle= "bold" android:gravity= "right" android:singleLine= "true" /> </LinearLayout> <TextView android:id= "@+id/daysOfWeek" android:layout_width= "match_parent" android:layout_height= "wrap_content" android:layout_below= "@id/time_wrapper" android:paddingTop= "2dip" android:textAppearance= "?android:attr/textAppearanceSmall" android:textColor= "?android:attr/textColorTertiary" /> </com.android.deskclock.DigitalClock> |
而这个使用自定义子类的目的是为了使用专门的字体。。
1234 | /** * 使用特殊的AndroidClock字体来显示文本。 */ public AndroidClockTextView extends
|
要了,到最这个类的最后时刻了,
(3)onCreate(Bundle icicle)
代码如下:
12345678910 | @Override protected onCreate(Bundle icicle) { super .onCreate(icicle); mFactory = LayoutInflater.from( this ); mPrefs = getSharedPreferences(PREFERENCES, 0 ); mCursor = Alarms.getAlarmsCursor(getContentResolver()); updateLayout(); } |
icicle 冰柱,冰棍儿 | popsicle, ice-sucker, ice lolly, icicle |
呵呵,我想我们以后命名也可以多用吃的来命名,这样程序是不是会吸引人些呢?
上面方法的代码,比较清楚。
LayoutInflater.from(this)来获得LayoutInflater对象。
当然也可以通过getSystemService()来获得。
然后是获得当前偏好文件管理对象。getSharedPreferences(PREFERNCES,0);
0是默认的打开模式,即等于
MODE_PRIVATE
然后从Alarm的Dao类中,即从Alarms类中获得所有的Alarm的Cursor对象。
Alarms会在我后面的博文中详细分析。
然后就是,
(3)updateLayout()
代码,比较长,但是还是清楚明白的。
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748 | private updateLayout() { setContentView(R.layout.alarm_clock); mAlarmsList = (ListView) findViewById(R.id.alarms_list); AlarmTimeAdapter adapter = new
this , mCursor); mAlarmsList.setAdapter(adapter); mAlarmsList.setVerticalScrollBarEnabled( true ); mAlarmsList.setOnItemClickListener( this ); mAlarmsList.setOnCreateContextMenuListener( this ); View addAlarm = findViewById(R.id.add_alarm); if
null ) { addAlarm.setOnClickListener( new
public onClick(View v) { addNewAlarm(); } }); // Make the entire view selected when focused. addAlarm.setOnFocusChangeListener( new
public onFocusChange(View v, boolean
v.setSelected(hasFocus); } }); } View doneButton = findViewById(R.id.done); if
null ) { doneButton.setOnClickListener( new
public onClick(View v) { finish(); } }); } View settingsButton = findViewById(R.id.settings); if
null ) { settingsButton.setOnClickListener( new
public onClick(View v) { startActivity( new
this , SettingsActivity. class )); finish(); } }); } ActionBar actionBar = getActionBar(); if
null ) { actionBar.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP, ActionBar.DISPLAY_HOME_AS_UP); } } |
第2行获得闹钟ListView对象。
第3行,构造AlarmTimeAdapter,
第4行,为listView对象设置adapter
第5行,listView启用ListView的垂直滑动条。
第6行,设置listView中闹钟项的单击事件。
第7行,设置listView中闹钟的长按监听。
下面的代码,基本是同样的模式,
查找某一个View,如果不为空则设置点击事件。
然后有一个获得ActionBar,如果actionBar不为空的话,
actionBar.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP, ActionBar.DISPLAY_HOME_AS_UP);
这个方法的特点是,前面所指定的选项呢,是将要设置的,但是如果前面第一参数里出现了选项,没有出现在第二个参数中,那么此选项将被认为是disable的。
ActionBar是android3之后才新出的组件。值得我们去学习。使用。
(4)onDestroy()
方法如下:
12345678 | @Override protected onDestroy() { super .onDestroy(); ToastMaster.cancelToast(); if
null ) { mCursor.close(); } } |
上面方法,主要执行两个操作,将数据库的Cursor关闭(如果不为空的话)
取消Toast。ToastMaster是此应用自定义的一个封装类,代码比较简单。
一般在我们使用数据库的时候,都应该在最后正确的关闭数据库。
上面的方法就是这样做的。
到此这个类的分析,暂时到这里了。
其中还有一些地方肯定没有分析得很好,希望各位如果看到了这里,发现不足,欢迎给我些指点。谢谢