上周花了不到一个周研究了一下android中的Calendar这个东西,下面把我的收获与大家分享一下。
我的研究是从阅读官方文档开始的。官方文档上的开头部分涉及到了一个ContentProvider和ContentResolver的东西。这个概念在android开发中很重要。
为了在应用程序之间交换数据,android提供了ContentProvider,ContentProvider是不同应用程序之间进行数据交换的标准API,当一个应用程序需要把自己的数据暴露给其他程序使用时,该应用程序就可以通过提供ContentProvider实现。其他应用程序就可以通过ContentResolver来操作ContentProvider暴露的数据(一般是以数据库的一个表的形式暴露,因此用ContentResolver操作数据时,也很类似对数据库的表的操作)。
ContentProvider也是android应用的四大组件之一,与Activity、Service、BroadcastReceiver相似,它们都需要在AndroidManifest.xml中配置。
还有,一般来说,ContentProvider是单例模式的,当多个应用程序通过ContentResolver来操作ContentProvider提供的数据时,ContentResolver调用的数据将会委托给同一个ContentProvider处理。
一旦某个应用程序通过ContentProvider暴露了自己的数据操作接口,那么不管该应用程序是否启动,其他应用程序都可以通过该接口来操作该应用程序的内部数据,包括增删改查。
还有一个很重要的东西叫Uri(不是Url,其实和Url作用很像的)。通俗一点说就是当你用ContentResolver操作数据时,用Uri来指明数据的url。
使用ContentResolver操作数据的步骤其实很简单:首先调用Activity的getContentResolver( )获取ContentResolver对象,然后根据需要调用ContentResolver的insert( )、update( )、delete( ) 和query方法操作数据即可。为了操作数据,我们需要了解ContentProvider的Uri。这也是上述三个概念的简单联系。
扯了这么多,其实这个Demo中ContentProvider和ContentResolver的概念体现的不是很明显。因为我们用的是操作系统给我们的ContentProvider,然后我们自己获取ContentResolver对象来操作数据,只需要了解系统的响应的ContentProvider的Uri即可。
这个Demo实现了为手机上的某个账户添加新的event(并为新添加的event添加reminder),查询所有账户的所有Calendar,删除event等(修改event只需要类似地调用ContentResolver的相应的方法即可,本Demo没有实现),其他还用到了将手机设置成震动,全局定时器、焦点事件以及TimePickerDialog、DatePickerDialog等,下面上图:
Android4.0第一次使用引导
Android4.0待机
下面是本Demo的效果:
下面是AndroidCalendarProviderTestActivity.java的代码:
package org.ls; import java.text.SimpleDateFormat; import java.util.Date; import android.app.Activity; import android.app.AlertDialog; import android.content.ContentResolver; import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.provider.CalendarContract.Calendars; import android.provider.CalendarContract.Events; import android.text.method.ScrollingMovementMethod; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; public class AndroidCalendarProviderTestActivity extends Activity { private Button showCalendars; private Button addEvents; private Button queryEvents; private TextView displayEnvents; private EditText getEventIdEditText; private Button delEvent; public static final String[] EVENT_PROJECTION = new String[] { Calendars._ID, Calendars.ACCOUNT_NAME, Calendars.CALENDAR_DISPLAY_NAME }; private static final int PROJECTION_ID_INDEX = 0; private static final int PROJECTION_ACCOUNT_NAME_INDEX = 1; private static final int PROJECTION_DISPLAY_NAME_INDEX = 2; long myEventsId = 0; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); displayEnvents = (TextView) findViewById(R.id.displayevents); displayEnvents.setMovementMethod(ScrollingMovementMethod.getInstance()); showCalendars = (Button) findViewById(R.id.querycalendars); showCalendars.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Cursor cur = null; ContentResolver cr = getContentResolver(); Uri uri = Calendars.CONTENT_URI; // String selection = "((" + Calendars.ACCOUNT_NAME + // 给出查询条件,查询特定用户的日历 // " = ?) AND ("+ Calendars.ACCOUNT_TYPE + " = ?))"; // String[] selectionArgs = new String[] // {"[email protected]", "com.google"}; cur = cr.query(uri, EVENT_PROJECTION, null, null, null); // 查询条件为null,查询所有用户的所有日历 while (cur.moveToNext()) { long calID = 0; String displayName = null; String accountName = null; calID = cur.getLong(PROJECTION_ID_INDEX); displayName = cur.getString(PROJECTION_DISPLAY_NAME_INDEX); accountName = cur.getString(PROJECTION_ACCOUNT_NAME_INDEX); showMessageDialog("日历ID:" + calID + "\n" + "日历显示名称:" + "\n" + displayName + "\n" + "日历拥有者账户名称:" + "\n" + accountName); } } }); addEvents = (Button) findViewById(R.id.addevents); addEvents.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent itent=new Intent(); itent.setClass(AndroidCalendarProviderTestActivity.this,AddNewEventActivity.class); startActivity(itent); // 启动添加新event的Activity } }); queryEvents = (Button) findViewById(R.id.queryevents); queryEvents.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { ContentResolver cr = getContentResolver(); Cursor cur = cr.query(Events.CONTENT_URI, new String[] { Events._ID, Events.TITLE, Events.DESCRIPTION, Events.DTSTART, Events.DTEND }, /* Events._ID + "=" + myEventsId */null, null, null); // 注释中的条件是是查询特定ID的events displayEnvents.setText(""); while (cur.moveToNext()) { Long tempEventsId = cur.getLong(0); String tempEventsTitle = cur.getString(1); String tempEventsDecription = cur.getString(2); String tempEventsStartTime = cur.getString(3); String tempEventsEndTime = cur.getString(4); displayEnvents.append(tempEventsId + "\n"); displayEnvents.append(tempEventsTitle + " " + tempEventsDecription + "\n"); displayEnvents.append(new SimpleDateFormat( "yyyy/MM/dd hh:mm").format(new Date(Long .parseLong(tempEventsStartTime))) + "至"); displayEnvents.append(new SimpleDateFormat( "yyyy/MM/dd hh:mm").format(new Date(Long .parseLong(tempEventsEndTime))) + "\n"); } } }); getEventIdEditText = (EditText) findViewById(R.id.geteventid); delEvent = (Button) findViewById(R.id.delevent); delEvent.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Long tempEventId = 0l; try { tempEventId = Long.parseLong(getEventIdEditText.getText() .toString()); } catch (Exception e) { showMessageDialog("请先查询所有event,然后正确填写event的id~"); return; } // 另一种删除event方式 // Uri deleteUri = // ContentUris.withAppendedId(Events.CONTENT_URI, tempEventId); // int rows = getContentResolver().delete(deleteUri, null, // null); ContentResolver cr = getContentResolver(); int rows = cr.delete(Events.CONTENT_URI, Events._ID + "= ?", new String[] { tempEventId + "" }); showMessageDialog("删除了一个event:" + rows); Log.i("delete_event", "Rows deleted: " + rows); } }); } public void showMessageDialog(String info) { // 弹出消息对话框,消息的内容是info AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage(info); builder.setTitle("information"); builder.setPositiveButton("确定", null); AlertDialog alert = builder.create(); alert.show(); } }
package org.ls; import java.util.Calendar; import android.app.Activity; import android.app.AlarmManager; import android.app.AlertDialog; import android.app.DatePickerDialog; import android.app.PendingIntent; import android.app.Service; import android.app.TimePickerDialog; import android.content.ContentResolver; import android.content.ContentValues; import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.provider.CalendarContract.Events; import android.provider.CalendarContract.Reminders; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnFocusChangeListener; import android.widget.Button; import android.widget.DatePicker; import android.widget.EditText; import android.widget.TimePicker; public class AddNewEventActivity extends Activity implements OnClickListener, OnFocusChangeListener { public String eventName; public String eventDescription; public int[] eventBeginDate = new int[3]; public int[] eventBeginTime = new int[2]; public int[] eventEndDate = new int[3]; public int[] eventEndTime = new int[2]; public int reminderMinutes; private EditText eventNameText; private EditText eventDescriptionText; private EditText eventBeginDateText; private EditText eventBeginTimeText; private EditText eventEndDateText; private EditText eventEndTimeText; private EditText reminderminutesText; private Button okButton; private Button goBackButton; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.add_event); okButton = (Button) findViewById(R.id.ok); okButton.setOnClickListener(this); goBackButton = (Button) findViewById(R.id.goback); goBackButton.setOnClickListener(this); eventNameText = (EditText)findViewById(R.id.event_name); eventDescriptionText = (EditText)findViewById(R.id.event_description); eventBeginDateText = (EditText) findViewById(R.id.select_begin_date); eventBeginDateText.setFocusable(true); eventBeginDateText.setOnFocusChangeListener(this); eventBeginTimeText = (EditText) findViewById(R.id.select_begin_time); eventBeginTimeText.setFocusable(true); eventBeginTimeText.setOnFocusChangeListener(this); eventEndDateText = (EditText) findViewById(R.id.select_end_date); eventEndDateText.setFocusable(true); eventEndDateText.setOnFocusChangeListener(this); eventEndTimeText = (EditText) findViewById(R.id.select_end_time); eventEndTimeText.setFocusable(true); eventEndTimeText.setOnFocusChangeListener(this); reminderminutesText = (EditText) findViewById(R.id.reminder_minutes); } @Override public void onClick(View v) { if (v == okButton) { eventName = eventNameText.getText().toString(); eventDescription = eventDescriptionText.getText().toString(); reminderMinutes = Integer.parseInt(reminderminutesText.getText() .toString()); // 可在此处添加简单的判断用户输入新event的各项参数的合法性的判断,我假设用户输入的一定是合法的 addEvent(eventName, eventDescription, eventBeginDate, eventBeginTime, eventEndDate, eventEndTime, reminderMinutes); // 添加新event } else if (v == goBackButton) { goBack(); } } @Override public void onFocusChange(View v, boolean hasFocus) { if (v == eventBeginDateText && hasFocus == true) { Calendar c = Calendar.getInstance(); new DatePickerDialog(AddNewEventActivity.this, new DatePickerDialog.OnDateSetListener() { @Override public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) { eventBeginDateText.setText("开始日期:" + year + "-" + (monthOfYear+1) + "-" + dayOfMonth); eventBeginDate[0] = year; eventBeginDate[1] = monthOfYear; eventBeginDate[2] = dayOfMonth; } }, c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_MONTH)).show(); } else if (v == eventBeginTimeText && hasFocus == true) { Calendar c = Calendar.getInstance(); new TimePickerDialog(AddNewEventActivity.this, new TimePickerDialog.OnTimeSetListener() { @Override public void onTimeSet(TimePicker view, int hourOfDay, int minute) { eventBeginTimeText.setText("开始时间:" + hourOfDay + "时" + minute + "分"); eventBeginTime[0] = hourOfDay; eventBeginTime[1] = minute; } }, c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE), true).show(); } else if (v == eventEndDateText && hasFocus == true) { Calendar c = Calendar.getInstance(); new DatePickerDialog(AddNewEventActivity.this, new DatePickerDialog.OnDateSetListener() { @Override public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) { eventEndDateText.setText("结束日期:" + year + "-" + (monthOfYear+1) + "-" + dayOfMonth); eventEndDate[0] = year; eventEndDate[1] = monthOfYear; eventEndDate[2] = dayOfMonth; } }, c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_MONTH)).show(); } else if (v == eventEndTimeText && hasFocus == true) { Calendar c = Calendar.getInstance(); new TimePickerDialog(AddNewEventActivity.this, new TimePickerDialog.OnTimeSetListener() { @Override public void onTimeSet(TimePicker view, int hourOfDay, int minute) { eventEndTimeText.setText("结束时间:" + hourOfDay + "时" + minute + "分"); eventEndTime[0] = hourOfDay; eventEndTime[1] = minute; } }, c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE), true).show(); } } private void addEvent(String eventName, String eventDescription, int eventBeginDate[], int eventBeginTime[], int eventEndDate[], int eventEndTime[], int reminderMinutus) { long calId = 1; long startMillis = 0; long endMillis = 0; Calendar beginTime = Calendar.getInstance(); beginTime.set(eventBeginDate[0], eventBeginDate[1], eventBeginDate[2], eventBeginTime[0], eventBeginTime[1]); // 注意:月份系统会自动加1 startMillis = beginTime.getTimeInMillis(); Calendar endTime = Calendar.getInstance(); endTime.set(eventEndDate[0], eventEndDate[1], eventEndDate[2], eventEndTime[0], eventEndTime[1]); endMillis = endTime.getTimeInMillis(); ContentResolver cr = getContentResolver(); // 添加新event,步骤是固定的 ContentValues values = new ContentValues(); values.put(Events.DTSTART, startMillis); values.put(Events.DTEND, endMillis); values.put(Events.TITLE, eventName); values.put(Events.DESCRIPTION, eventDescription); values.put(Events.CALENDAR_ID, calId); values.put(Events.EVENT_TIMEZONE, "GMT+8"); Uri uri = cr.insert(Events.CONTENT_URI, values); Long myEventsId = Long.parseLong(uri.getLastPathSegment()); // 获取刚才添加的event的Id ContentResolver cr1 = getContentResolver(); // 为刚才新添加的event添加reminder ContentValues values1 = new ContentValues(); values1.put(Reminders.MINUTES, reminderMinutus); values1.put(Reminders.EVENT_ID, myEventsId); values1.put(Reminders.METHOD, Reminders.METHOD_ALERT); cr1.insert(Reminders.CONTENT_URI, values1); // 调用这个方法返回值是一个Uri setAlarmDeal(startMillis); // 设置reminder开始的时候,启动另一个activity showMessageDialog("插入成功!" + "\n" + uri.getLastPathSegment() + "\n" + uri.getAuthority()); } private void setAlarmDeal(long time) { // 设置全局定时器 Intent intent = new Intent(this, AlarmActivity.class); PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0); AlarmManager aManager = (AlarmManager) getSystemService(Service.ALARM_SERVICE); aManager.set(AlarmManager.RTC_WAKEUP, time, pi); // 当系统调用System.currentTimeMillis()方法返回值与time相同时启动pi对应的组件 } public void showMessageDialog(String info) { // 弹出消息对话框,消息的内容是info,且点击此对话框的确定按钮后会返回 AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage(info); builder.setTitle("information"); builder.setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { goBack(); } }); AlertDialog alert = builder.create(); alert.show(); } private void goBack() { // 返回 Intent itent = new Intent(); itent.setClass(AddNewEventActivity.this, AndroidCalendarProviderTestActivity.class); startActivity(itent); AddNewEventActivity.this.finish(); } }
package org.ls; import android.app.Activity; import android.content.Context; import android.media.AudioManager; import android.os.Bundle; public class AlarmActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.alarm_activity); setVibrate(); // 将手机情景模式设为震动 } private void setVibrate() { AudioManager audio = (AudioManager)getSystemService(Context.AUDIO_SERVICE); audio.setRingerMode(AudioManager.RINGER_MODE_VIBRATE); audio.setVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER, AudioManager.VIBRATE_SETTING_ON); audio.setVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION, AudioManager.VIBRATE_SETTING_ON); } }
alarm_activity.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/alarminfo" android:id="@+id/displayinfo" /> </LinearLayout>
add_event.xml:
<?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" android:id="@+id/add_new_event"> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:hint="在此填写新event的名字" android:id="@+id/event_name" android:selectAllOnFocus="true" /> <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:hint="简要描述新event" android:id="@+id/event_description" android:selectAllOnFocus="true" /> <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:hint="点击选择开始日期" android:id="@+id/select_begin_date" android:selectAllOnFocus="true" /> <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:hint="点击选择开始时间" android:id="@+id/select_begin_time" android:selectAllOnFocus="true" /> <!-- <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="选择新event的结束时间:" /> --> <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:hint="点击选择结束日期" android:id="@+id/select_end_date" android:selectAllOnFocus="true" /> <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:hint="点击选择结束时间" android:id="@+id/select_end_time" android:selectAllOnFocus="true" /> <!-- <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="为新event的添加reminder:" /> --> <!-- <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="新event提前多少分钟提醒您:" /> --> <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:hint="在此填写新event提前多少分钟提醒您:" android:selectAllOnFocus="true" android:id="@+id/reminder_minutes" /> <Button android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="确定" android:id="@+id/ok" /> <Button android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="返回" android:id="@+id/goback" /> </LinearLayout> </ScrollView>
main.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <Button android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="查询所有日历" android:id="@+id/querycalendars" /> <Button android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="添加新日历项" android:id="@+id/addevents" /> <Button android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="查询所有event" android:id="@+id/queryevents" /> <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:hint="在这里输入要删除event的id" android:id="@+id/geteventid" /> <Button android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="删除该event" android:id="@+id/delevent" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" android:id="@+id/displayevents" /> </LinearLayout>
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.ls" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="14" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:label="@string/app_name" android:name=".AndroidCalendarProviderTestActivity" > <intent-filter > <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name="AddNewEventActivity"></activity> <activity android:name="AlarmActivity"></activity> </application> <uses-permission android:name="android.permission.READ_CALENDAR"/> <uses-permission android:name="android.permission.WRITE_CALENDAR" /> </manifest>