写在前面:
安卓的学习也有半年多了,期间也曾写过博客,但大多都是一些琐碎的笔记,基本没用任何参考价值,这几天闲来无事,便想做个小项目来玩玩,巩固一下基本知识,并且完整的记录下来整个开发的过程,以作留念。
/————————我是华丽的分割线—————————-/
本次项目选择了可定时提示的备忘录。原理是利用系统每分钟发送一条时间改变的广播,通过接受这条广播来判断是否为用户设置的时间,如果是则与用户交互提醒用户。
首先,在Eclipse中创建一个Android项目,取名为Notification
类:MainActivity.java
此Activity主要用于用户添加备忘录页面的转跳与已添加备忘录的展示。
在其布局文件activity_main.xml中加入一个Button和一个TextView 。目前还没有实现多事件的共同设置,所以暂时使用TextView显示事件。
因为储存的数据比较少,这里采用SharedPreferences进行存储,在Activity的onCreate()方法中进行成员变量的初始化操作:
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); init(); sp = getSharedPreferences("UserNote",MODE_PRIVATE); }
public void init() { button = (Button) findViewById(R.id.bt); title = (TextView) findViewById(R.id.main_title); note = (TextView) findViewById(R.id.main_note); time = (TextView)findViewById(R.id.main_time); }
在onStart()中对按钮bt添加点击事件监听,并通过startActivity(intent)启动AddActivity转跳到添加的Activity:
protected void onStart() { super.onStart(); button.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { Intent intent = new Intent(MainActivity.this, AddActivity.class); startActivity(intent); } }); }
在onResume()方法中,获取SharedPerfences中的数据并更新TextView的内容:
protected void onResume(){ super.onResume(); strTime = sp.getString("time","null"); strTitle = sp.getString("title","null"); strNote = sp.getString("text","null"); title.setText(strTitle); note.setText(strNote); time.setText(strTime); System.out.println("onResume"); }
MainActivity.java整体代码如下:
package com.example.notification; import android.app.Activity; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.support.v7.app.ActionBarActivity; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; public class MainActivity extends Activity { private Button button; private SharedPreferences sp; private TextView title, note,time; private String strTitle,strNote,strTime; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); init(); sp = getSharedPreferences("UserNote",MODE_PRIVATE); } @Override protected void onStart() { super.onStart(); button.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { Intent intent = new Intent(MainActivity.this, AddActivity.class); startActivity(intent); } }); } protected void onResume(){ super.onResume(); strTime = sp.getString("time","null"); strTitle = sp.getString("title","null"); strNote = sp.getString("text","null"); title.setText(strTitle); note.setText(strNote); time.setText(strTime); System.out.println("onResume"); } public void init() { button = (Button) findViewById(R.id.bt); title = (TextView) findViewById(R.id.main_title); note = (TextView) findViewById(R.id.main_note); time = (TextView)findViewById(R.id.main_time); } }
该Activity布局文件activity_main.xml如下
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.example.notification.MainActivity" android:background="#fcf3ea" > <Button android:id="@+id/bt" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="添加备忘录" android:textSize="20dp" /> <TextView android:id="@+id/setting" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/bt" android:textSize="20dp" android:text="" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignTop="@+id/setting" android:layout_toRightOf="@+id/setting" android:orientation="vertical" > <TextView android:id="@+id/main_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20dp" android:text="" /> <TextView android:id="@+id/main_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20dp" android:text="" /> <TextView android:id="@+id/main_note" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20dp" android:text="" /> </LinearLayout> </RelativeLayout>
类:AddActivity.java
该Activity为用户添加备忘录事件的Activity,用户通过EditText和DatePicker和TimePicker进行数据的输入,单击按钮由SharedPerfences记录用户输入的数据.在onCreate()中进行初始化,在onStart()中对按钮添加监听,并且将时间选择器和用户输入的内容存入SharedPerfences,这里应当注意的是,时间选择器选择的数据如果为一位数如1月 则记录的日期为1 并非01,这点与SimpleDateFormat生成的不一致,所以我们需要自己建立一个类来规范我们的时间,这个类取名为MyTime,在存储完毕用户键入的数据以后,Activity会启动一个服务NotifyService,并且调用finish()关闭自身。
AddActivity代码如下:
package com.example.notification; import java.text.SimpleDateFormat; import java.util.Date; import android.annotation.SuppressLint; import android.app.Activity; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.DatePicker; import android.widget.EditText; import android.widget.TimePicker; import android.widget.Toast; public class AddActivity extends Activity { private Button conButton; private TimePicker tp; private DatePicker dp; private EditText etTitle, etNote; private String title, text; private SharedPreferences sp; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_add); init(); } protected void onStart() { super.onStart(); conButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { saveTime(); } }); } @SuppressLint("NewApi") public void init() { conButton = (Button) findViewById(R.id.add_bt_confirm); tp = (TimePicker) findViewById(R.id.timePicker1); dp = (DatePicker) findViewById(R.id.datePicker1); etTitle = (EditText) findViewById(R.id.add_et_title); etNote = (EditText) findViewById(R.id.add_et_note); tp.setIs24HourView(true); dp.setCalendarViewShown(false); sp = getSharedPreferences("UserNote", MODE_PRIVATE); } public void saveTime() { text = etNote.getText().toString(); title = etTitle.getText().toString(); int day = dp.getDayOfMonth(); int mon = dp.getMonth()+1;//getMonth 返回从0开始 System.out.println("setMon is"+mon); int year = dp.getYear(); int hour = tp.getCurrentHour(); int min = tp.getCurrentMinute(); SimpleDateFormat sdf = new SimpleDateFormat("HHmm"); String time = MyTime.getTime(year,mon,day,hour, min); System.out.println(time); SharedPreferences.Editor editor = sp.edit(); editor.putString("text", text); editor.putString("time", time); editor.putString("title",title); editor.commit(); System.out.println(time); Intent intent = new Intent(AddActivity.this, NotifyService.class); startService(intent); Log.i("ADD","intent完毕"); finish(); } }
MyTime.java如下
package com.example.notification; public class MyTime { public MyTime() { // TODO 自动生成的构造函数存根 } public static String getTime(int year,int mon,int day,int hour, int min) { String mins,mons,days,hours; if(hour<10) hours = 0+String.valueOf(hour); else hours = String.valueOf(hour); if(min<10) mins =0+ String.valueOf(min); else mins = String.valueOf(min); if(mon<10) mons =0+ String.valueOf(mon); else mons = String.valueOf(mon); if(day<10) days = 0+String.valueOf(mon); else days = String.valueOf(day); return year+":"+mons+":"+days+":"+hours+":"+mins; } }
服务NotifyService.java
新建服务需要在AndroidManifest.xml进行注册,在节点下加入以下代码进行注册
<service android:name=".NotifyService" ></service>
该服务主要功能为判断当前时间与用户储存时间是否一致,并进行相应动作,在onCreate()方法动态注册一个广播接收器TimeRecevier,该广播接收器接收系统时间变化的广播,该广播每分钟由系统发出一次,并在OnDestroy()方法中注销该广播接收器。具体ACTION如下(要接收该广播,必须进行动态注册):
private String ACTION = Intent.ACTION_TIME_TICK;
在onStartCommand()方法中,利用SimpleDateFormat将当前系统时间格式化与本地存储一致的格式,当前时间可以通过new Date()获取(new Date()实质上就是System.currentTimeMillis());然后进行时间的比对,如果一致,则通知栏通知用户,并且启动一个可以在锁屏状态下唤醒屏幕的Activity。这里值得注意的是,在服务中启动一个Activity必须为调用的intent添加一个Flag如下:
ActIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
最后调用stopSelf()方法,关闭自身.
NotifyService.java代码如下:
package com.example.notification; import java.text.SimpleDateFormat; import java.util.Date; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.os.IBinder; import android.util.Log; public class NotifyService extends Service { private NotificationManager nm; private boolean isRec = false; private boolean isFirst = true; private String ACTION = Intent.ACTION_TIME_TICK; private SharedPreferences sp; private IntentFilter ifter; private TimeReceiver receiver; @Override public IBinder onBind(Intent arg0) { // TODO 自动生成的方法存根 return null; } @Override public void onCreate() { // TODO 自动生成的方法存根 super.onCreate(); sp = getSharedPreferences("UserNote", MODE_PRIVATE); nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); ifter = new IntentFilter(); ifter.addAction(ACTION); receiver = new TimeReceiver(); if (isRec == false) { registerReceiver(receiver, ifter); isRec = true; } Log.i("Service","onCreate"); } @Override public void onDestroy() { super.onDestroy(); System.out.println("服务拜拜"); unregisterReceiver(receiver); } @SuppressWarnings("deprecation") @Override public int onStartCommand(Intent intent, int flags, int startId) { // TODO 自动生成的方法存根 Log.i("Service","onStart"); if (isFirst) { isFirst = false; } else { SimpleDateFormat sdf = new SimpleDateFormat("yyyy:MM:dd:HH:mm"); String curTime = sdf.format(new Date()); System.out.println(curTime); String time =sp.getString("time", "0000"); System.out.println("curTime"+curTime+" setTime"+time); if (curTime.equals(time)) { String text = sp.getString("text", "默认事件"); System.out.println("onStart"); Notification notify = new Notification(R.drawable.ic_launcher, "小贴士", System.currentTimeMillis()); PendingIntent pi = PendingIntent.getActivity(this,0, new Intent(this,MainActivity.class),0); notify.setLatestEventInfo(this, "小贴士提醒您", time+" "+text, pi); notify.defaults = Notification.DEFAULT_ALL; nm.notify(1044, notify); Intent ActIntent = new Intent(this,DialogActivity.class); ActIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(ActIntent); stopSelf(); } } return super.onStartCommand(intent, flags, startId); } }
类,广播接收器:TimeReceiver.java
整个接收器的作用只有一个,就是每次接收到广播,便启动服务NotifyService。
TimeReceiver.java如下
package com.example.notification; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; public class TimeReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { System.out.println("recevice"); context.startService(new Intent(context,NotifyService.class)); } }
类DialogActivity.java
该Activity为用户预设的时间到了通知用户的Activity,该Activity有如下特性:
1.可以点亮屏幕。
2.可以在锁屏状态下启动。
实现这两个特点并不困难,只需要加入这两句话:
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
另外,值得一提的是,QQ客户端的锁屏聊天窗口就是这样实现的(如图):
这个窗口看似是在弹窗之上。其实本身只是一个Activity,玄机就在他的背景图片,他是一个以系统壁纸为背景的没有标题栏的Activity。因此为了不让Activity突然弹出看起来那么突兀,我们要自定义一个Theme。在AndroidManifest.xml的节点下加入以下代码
<activity android:name=".DialogActivity" android:label="提醒您" android:theme="@android:style/Theme.Wallpaper.NoTitleBar" >
在res/values/styles.xml加入自定义的Style
<style name="FullScreenTheme" parent="@android:style/Theme.Light.NoTitleBar"> <item name="android:windowBackground">@android:color/transparent</item> <item name="android:colorBackgroundCacheHint">@null</item> <item name="android:windowNoTitle">true</item> <item name="android:windowIsTranslucent">true</item> </style>
完成
感谢你的阅读,在此我们就完成了定时提醒备忘录这个小项目,当然这个项目还有很多不足,只是一个功能的实现,如果有兴趣你可以根据源代码添加想要的功能例如多条事件的添加,还可以加上桌面组件.
源码下载地址:
http://download.csdn.net/detail/wingichoy/8717199