接上一篇博文:``继续分析AlarmClock类的各个方法:
还是先从简单的开始吧:
(1)updateAlarm(),代码如下:
1
2
3
4
5
6
7
8
|
private
void
updateAlarm(
boolean
enabled,
Alarm alarm) {
Alarms.enableAlarm(
this
, alarm.id, enabled);
if
(enabled) {
SetAlarm.popAlarmSetToast(
this
, alarm.hour, alarm.minutes,
alarm.daysOfWeek);
}
}
|
更新Alarm状态。
值得注意的上,在前一篇博文也出现了Alarms.enableAlarm()方法,
但是其实这个enableAlarm()方法实际上是根据enabled的状态来更新alarm的。
并不是单纯的启用alarm的。
如果enabled参数为false的话,实际是停用些alarm,
上面的方法,在设置的alarm状态之后。如果是启用的话,弹出一条Toast来提醒用户。
这个方法在用户点击闹钟列表项的选择框时调用。是一个点击事件的回调函数,在AlarmTimeAdapter中的bindView方法中,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
View indicator = view.findViewById(R.id.indicator);
// 为选择框设置初始的状态
// Set the initial state of the clock "checkbox"
final
CheckBox clockOnOff =
(CheckBox) indicator.findViewById(R.id.clock_onoff);
clockOnOff.setChecked(alarm.enabled);
// 在单选框的外部(指单选框周围的margin部分)单击也应该改变状态。
// Clicking outside the "checkbox" should also change the state.
indicator.setOnClickListener(
new
OnClickListener() {
public
void
onClick(View v) {
clockOnOff.toggle();
// 切换选择框状态
updateAlarm(clockOnOff.isChecked(), alarm);
//更新alarm状态。
}
});
|
我觉得很有必要将上面的R.id.indicator所代码布局代码贴上来,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<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布局的背景。
代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
/**
* 这是一个允许其父类本身被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
class
DontPressWithParentLayout
extends
LinearLayout {
public
DontPressWithParentLayout(Context context, AttributeSet attrs) {
super
(context, attrs);
}
@Override
public
void
setPressed(
boolean
pressed) {
// 如果父类类被pressed不要设置为pressed
// If the parent is pressed, do not set to pressed.
if
(pressed && ((View) getParent()).isPressed()) {
return
;
}
super
.setPressed(pressed);
}
}
|
(2)CursorAdapter子类AlarmTimeAdapter分析。
CursorAdapter类的简单介绍。
相信我们都对于BaseAdapter比较了解了吧,
那么先来看看CursorAdapter的签名吧,
1
2
3
|
public
abstract
class
CursorAdapter
extends
BaseAdapter
implements
Filterable,
CursorFilter.CursorFilterClient {
}
|
当我们扩展BaseAdapter的时候,我们主要是要重写BaseAdapter#getView()方法。
但是在CursorAdapter中这个方法它已经帮我们实现了,但是让我们去实现另外两个方法。
先看下CursorAdapter中的getView()方法吧,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
/**
* @see android.widget.ListAdapter#getView(int, View, ViewGroup)
*/
public
View getView(
int
position, View convertView, ViewGroup parent) {
if
(!mDataValid) {
throw
new
IllegalStateException(
"this should only be called when the cursor is valid"
);
}
if
(!mCursor.moveToPosition(position)) {
throw
new
IllegalStateException(
"couldn't move cursor to position "
+ position);
}
View v;
if
(convertView ==
null
) {
v = newView(mContext, mCursor, parent);
}
else
{
v = convertView;
}
bindView(v, mContext, mCursor);
return
v;
}
|
上面代码的逻辑也是我们通过实现getViw()的逻辑 ,先是对数据进行错误检查。
然后,如果view为空则调用newView()构造一个view,否则使用之前构造的。
然后调用bindView()将数据绑定上去。OK,如果使用过BaseAdapter的话是不是很熟悉啊!
那下面再来仔细看看,newView()和bindView()这两个方法吧。
(2.1)public View newView(Context context,Cursor cursor,ViewGroup parent)
代码如下:
1
2
3
4
5
6
7
8
9
|
@Override
public
View newView(Context context, Cursor cursor, ViewGroup parent) {
View ret = mFactory.inflate(R.layout.alarm_time, parent,
false
);
DigitalClock digitalClock =
(DigitalClock) ret.findViewById(R.id.digitalClock);
digitalClock.setLive(
false
);
return
ret;
}
|
首先进入我们眼中的是DigitalClock,看下DigitalClock的类型,如下:
public class DigitalClock extends RelativeLayout
哦,原来是一个RelativeLayout的子类。对于DigitalClock我们就暂时了解这么多,后面会单独分析。
(2.2) public void bindView(View view, Context context, Cursor cursor)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
@Override
public
void
bindView(View view, Context context, Cursor cursor) {
final
Alarm alarm =
new
Alarm(cursor);
View indicator = view.findViewById(R.id.indicator);
// Set the initial state of the clock "checkbox"
final
CheckBox clockOnOff =
(CheckBox) indicator.findViewById(R.id.clock_onoff);
clockOnOff.setChecked(alarm.enabled);
// Clicking outside the "checkbox" should also change the state.
indicator.setOnClickListener(
new
OnClickListener() {
public
void
onClick(View v) {
clockOnOff.toggle();
updateAlarm(clockOnOff.isChecked(), alarm);
}
});
// 上面部分的代码上updateAlarm()时已经有比较详细的分析了。
DigitalClock digitalClock =
(DigitalClock) view.findViewById(R.id.digitalClock);
// 设置闹钟显示的标签
final
Calendar c = Calendar.getInstance();
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
String daysOfWeekStr =
alarm.daysOfWeek.toString(AlarmClock.
this
,
false
);
if
(daysOfWeekStr !=
null
&& daysOfWeekStr.length() !=
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
(alarm.label !=
null
&& alarm.label.length() !=
0
) {
labelView.setText(alarm.label);
labelView.setVisibility(View.VISIBLE);
}
else
{
labelView.setVisibility(View.GONE);
}
}
};
|
上面的代码也比较清晰简单,无非就是,如果对应内容不为空的话,则设置并显示,否则隐藏对应控件。
下面的布局代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
<!-- A layout that displays the time. Shows time, am/pm (
if
12
-hour),
and an optional line below, used
for
day/days of week -->
<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>
|
上面布局代码中,有用到了com.android.deskclock.AndroidClockTextView 这个扩展了TextView的子类。
而这个使用自定义子类的目的是为了使用专门的字体。。
1
2
3
4
|
/**
* 使用特殊的AndroidClock字体来显示文本。
*/
public
class
AndroidClockTextView
extends
TextView
|
要了,到最这个类的最后时刻了,
(3)onCreate(Bundle icicle)
代码如下:
1
2
3
4
5
6
7
8
9
10
|
@Override
protected
void
onCreate(Bundle icicle) {
super
.onCreate(icicle);
mFactory = LayoutInflater.from(
this
);
mPrefs = getSharedPreferences(PREFERENCES,
0
);
mCursor = Alarms.getAlarmsCursor(getContentResolver());
updateLayout();
}
|
我上网查了下icicle的意思,如下:
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()
代码,比较长,但是还是清楚明白的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
private
void
updateLayout() {
setContentView(R.layout.alarm_clock);
mAlarmsList = (ListView) findViewById(R.id.alarms_list);
AlarmTimeAdapter adapter =
new
AlarmTimeAdapter(
this
, mCursor);
mAlarmsList.setAdapter(adapter);
mAlarmsList.setVerticalScrollBarEnabled(
true
);
mAlarmsList.setOnItemClickListener(
this
);
mAlarmsList.setOnCreateContextMenuListener(
this
);
View addAlarm = findViewById(R.id.add_alarm);
if
(addAlarm !=
null
) {
addAlarm.setOnClickListener(
new
View.OnClickListener() {
public
void
onClick(View v) {
addNewAlarm();
}
});
// Make the entire view selected when focused.
addAlarm.setOnFocusChangeListener(
new
View.OnFocusChangeListener() {
public
void
onFocusChange(View v,
boolean
hasFocus) {
v.setSelected(hasFocus);
}
});
}
View doneButton = findViewById(R.id.done);
if
(doneButton !=
null
) {
doneButton.setOnClickListener(
new
View.OnClickListener() {
public
void
onClick(View v) {
finish();
}
});
}
View settingsButton = findViewById(R.id.settings);
if
(settingsButton !=
null
) {
settingsButton.setOnClickListener(
new
View.OnClickListener() {
public
void
onClick(View v) {
startActivity(
new
Intent(AlarmClock.
this
, SettingsActivity.
class
));
finish();
}
});
}
ActionBar actionBar = getActionBar();
if
(actionBar !=
null
) {
actionBar.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP, ActionBar.DISPLAY_HOME_AS_UP);
}
}
|
方法第1行,设置布局资源。
第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()
方法如下:
1
2
3
4
5
6
7
8
|
@Override
protected
void
onDestroy() {
super
.onDestroy();
ToastMaster.cancelToast();
if
(mCursor !=
null
) {
mCursor.close();
}
}
|
上面方法,主要执行两个操作,将数据库的Cursor关闭(如果不为空的话)
取消Toast。ToastMaster是此应用自定义的一个封装类,代码比较简单。
一般在我们使用数据库的时候,都应该在最后正确的关闭数据库。
上面的方法就是这样做的。
到此这个类的分析,暂时到这里了。
其中还有一些地方肯定没有分析得很好,希望各位如果看到了这里,发现不足,欢迎给我些指点。谢谢