Android闹钟项目
王七鱼 人气:0一、闹钟功能的介绍以及界面的展示
该闹钟是根据我们手机闹钟设计的一个简单的闹钟APP,其中包含时钟、闹钟、秒表和计时器功能。用户可以对闹钟添加和删除,可以对秒表计时、暂停和重置,对计时器可以暂停、计时、继续和重置等功能。
二、介绍系统的设计界面
闹钟的布局文件代码如下
由于该闹钟系统包含时钟、闹钟、计时器、秒表四个功能,所以只要在xml文件插入TabHost控件就能实现在手机上更加简洁地展示四个功能。后面只需要在TabHost中插入四个Tab用来切换展示的界面,具体的代码实现如下:
public class MainActivity extends AppCompatActivity { private TabHost mTabHost; private StopWatchView mStopWatchView; @Override public SharedPreferences getPreferences(int mode) { return super.getPreferences(mode); } @RequiresApi(api = Build.VERSION_CODES.P) @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTabHost = (TabHost) findViewById(R.id.tabhost); mTabHost.setup(); mTabHost.addTab(mTabHost.newTabSpec("tabTime").setIndicator("时钟").setContent(R.id.tabTime)); mTabHost.addTab(mTabHost.newTabSpec("tabAlarm").setIndicator("闹钟").setContent(R.id.tabAlarm)); mTabHost.addTab(mTabHost.newTabSpec("tabTimer").setIndicator("计时器").setContent(R.id.tabTimer)); mTabHost.addTab(mTabHost.newTabSpec("tabStopWatch").setIndicator("秒表").setContent(R.id.tabStopWatch)); mStopWatchView = (StopWatchView) findViewById(R.id.tabStopWatch); } @Override protected void onDestroy() { super.onDestroy(); mStopWatchView.onDestory(); } }
一、时钟功能
因为时钟功能中,只要显示当前的日期和时钟就可以了,所以只需要插入一个TextView用来显示日期时间就可以了。
xml文件中的代码(new 一个时钟类TimeView,把时钟一块的LinearLayout换成com.example.tsclock.TimeView)
// 时钟 <com.example.tsclock.TimeView android:id="@+id/tabTime" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:id="@+id/tvTime" android:layout_width="match_parent" android:layout_height="match_parent" android:textAppearance="?android:attr/textAppearanceLarge" android:gravity="center"/> </com.example.tsclock.TimeView>
TimeView.java
要将时间显示到TabHost中,就必须先要获取其中的id,然后通过Calendar获取当前系统的时间,最后再每过1秒钟刷新一次,这样就能够再TextView中出现时间在不停的变化。
public class TimeView extends LinearLayout { private TextView tvTime; public TimeView(Context context) { super(context); } public TimeView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public TimeView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } // 在初始化之后进行的操作 @Override protected void onFinishInflate() { super.onFinishInflate(); tvTime = (TextView) findViewById(R.id.tvTime); // handler每秒执行一次 timerHandler.sendEmptyMessage(0); } // 可见属性发生变化之后 @Override protected void onVisibilityChanged(@NonNull View changedView, int visibility) { super.onVisibilityChanged(changedView, visibility); if (visibility == View.VISIBLE){ // 如果可见 则发送一个消息 timerHandler.sendEmptyMessage(0); }else{ // 如果不可见 移除所有的消息 timerHandler.removeMessages(0); } } // 重新刷新时间 private void refreshTime(){ // 呈现一个时间对象 Calendar c = Calendar.getInstance(); // 获取时分秒 tvTime.setText(String.format("%d:%d:%d",c.get(Calendar.HOUR_OF_DAY),c.get(Calendar.MINUTE),c.get(Calendar.SECOND))); } private Handler timerHandler = new Handler() { public void handleMessage(android.os.Message msg){ // 呈现出来 refreshTime(); // 如果可见 则刷新 if (getVisibility() == View.VISIBLE){ // 1000毫秒之后再制学校handlerMessage()方法 timerHandler.sendEmptyMessageDelayed(0,1000); } } }; }
二、闹钟功能
闹钟功能就相对时钟功能就复杂很多了,因为这里需要对闹钟进行增加,删除等操作,而且可能需要展示多个闹钟的时间。所以这里需要用到有一个Button控件用来增加闹钟和一个ListView控件用来展示闹钟的时间。
xml代码
// 闹钟 <com.example.tsclock.AlarmView android:id="@+id/tabAlarm" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <ListView android:id="@+id/lvAlarmList" android:layout_width="match_parent" android:layout_weight="1" android:layout_height="0dp"> </ListView> <Button android:id="@+id/btnAddAlarm" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/add_alarm" android:textColor="#FFFFFF" android:background="@color/colorBlue" /> </com.example.tsclock.AlarmView>
闹钟类,AlarmView.java
需要判断时间到了需要触发事件,需要播放音乐和震动。所以播放音乐和震动放在另一个活动中(PlayAlarmAty.java )
public class AlarmView extends LinearLayout { private Button btnAddAlarm; private ListView lvAlarmList; private ArrayAdapter<AlarmData> mAdapter; private static final String KEY_ALARM_LIST = "alarmList"; // 使用系统的闹钟服务 private AlarmManager mAlarmManager; public AlarmView(Context context) { super(context); init(); } public AlarmView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } private void init(){ // 使用闹钟服务设定闹钟 mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE); } // 在初始化之后进行的操作 @Override protected void onFinishInflate() { super.onFinishInflate(); btnAddAlarm = (Button)findViewById(R.id.btnAddAlarm); lvAlarmList = (ListView) findViewById(R.id.lvAlarmList); // 系统的简单资源 mAdapter = new ArrayAdapter<AlarmView.AlarmData>(getContext(),android.R.layout.simple_list_item_1); // 设置Adapter lvAlarmList.setAdapter(mAdapter); // 读取已经存储在SharedPreferences中的数据 readSavedAlarmList(); btnAddAlarm.setOnClickListener(new View.OnClickListener() {// 添加闹钟的点击事件 @Override public void onClick(View v) { addAlarm(); } }); // 删除闹钟 lvAlarmList.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView<?> parent, View view,final int position, long id) { new AlertDialog.Builder(getContext()).setTitle("操作选项").setItems(new CharSequence[]{"删除"}, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { switch (which){ case 0: deleteAlarm(position); break; default: break; } } }).setNegativeButton("取消",null).show(); return true; } }); } // 删除闹钟 private void deleteAlarm(int position){ AlarmData ad = mAdapter.getItem(position); // 把闹钟从闹钟列表移除 mAdapter.remove(ad); saveAlarmList(); // 移除闹钟 mAlarmManager.cancel(PendingIntent.getBroadcast(getContext(),ad.getId(),new Intent(getContext(),AlarmReceiver.class),0)); } // 添加闹钟 private void addAlarm(){ // 获取当前时间 Calendar c = Calendar.getInstance(); // 弹出一个时间的选择框 new TimePickerDialog(getContext(), new TimePickerDialog.OnTimeSetListener() { // 设置时间 @Override public void onTimeSet(TimePicker view, int hourOfDay, int minute) { // 获取当前时间 Calendar calendar = Calendar.getInstance(); calendar.set(Calendar.HOUR_OF_DAY,hourOfDay); // 设置时 calendar.set(Calendar.MINUTE,minute); // 设置分钟 calendar.set(Calendar.SECOND,0); // 秒清零 calendar.set(Calendar.MILLISECOND,0); // 毫秒值清零 // 如果设置闹钟时间小于当前时间,则往后推一天 Calendar currentTime = Calendar.getInstance(); if (calendar.getTimeInMillis() <= currentTime.getTimeInMillis()){ calendar.setTimeInMillis(calendar.getTimeInMillis()+24*60*60*1000); } AlarmData ad = new AlarmData(calendar.getTimeInMillis()); mAdapter.add(ad); //需要根据API版本来判断调用,从Android4.4(API19)开始,为了节能省电(减少系统唤醒和电池使用) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { mAlarmManager.setWindow(AlarmManager.RTC_WAKEUP, ad.getTime(), 100, // 时间误差范围 100毫秒 PendingIntent.getBroadcast(getContext(), ad.getId(), new Intent(getContext(), AlarmReceiver.class), 0)); } else { mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, ad.getTime(), 5*60*1000, PendingIntent.getBroadcast(getContext(), ad.getId(), new Intent(getContext(), AlarmReceiver.class), 0)); } saveAlarmList(); } },c.get(Calendar.HOUR_OF_DAY),c.get(Calendar.MINUTE),true).show(); } // 存储数据(存到SharedPreferences中) private void saveAlarmList(){ SharedPreferences.Editor editor = getContext().getSharedPreferences(AlarmView.class.getName(),Context.MODE_PRIVATE).edit(); // 存储数据(for循环遍历Adapter) StringBuffer sb = new StringBuffer(); //getCount表示这个adapter里面有多少item,就是有多少闹钟 for (int i = 0 ; i < mAdapter.getCount(); i++){ sb.append(mAdapter.getItem(i).getTime()).append(","); } // 所有的值传进去之后 去掉最后的逗 if (sb.length() > 1){ String content = sb.toString().substring(0,sb.length()-1); editor.putString(KEY_ALARM_LIST,content); System.out.println(content);// 输出存储的闹钟数据 }else { editor.putString(KEY_ALARM_LIST,null); } editor.commit(); } // 读取已存的数据 private void readSavedAlarmList(){ // 获取到SharedPreferences(数据内容) SharedPreferences sp = getContext().getSharedPreferences(AlarmView.class.getName(),Context.MODE_PRIVATE); String content = sp.getString(KEY_ALARM_LIST,null); // 这里需要判断,不然没闹钟数据的时候会有空指针异常 if (content != null){ //这里取得每一个闹钟的time添加到数组里 String[] timeStrings = content.split(","); // 遍历数组,把数据添加到mAdapter for (String string : timeStrings){ mAdapter.add(new AlarmData(Long.parseLong(string))); } } } //闹钟的数据,用一个类要保存,这是常用的做法 private static class AlarmData{ private long time = 0; private String timeLabel = ""; // 在外界获取时间的标签的字符串 private Calendar date; // 闹钟响起的时间 public AlarmData(long time){ this.time = time; date = Calendar.getInstance(); date.setTimeInMillis(time); timeLabel = String.format("%d月%d日 %d:%d", date.get(Calendar.MONTH)+1, date.get(Calendar.DAY_OF_MONTH), date.get(Calendar.HOUR_OF_DAY), date.get(Calendar.MINUTE)); } public AlarmData(String ad){ this.timeLabel = ad; } public void setTime(long time){ this.time = time; } public long getTime(){ return time; } public void setTimeLable(String timeLable){ this.timeLabel = timeLable; } @Override public String toString() { return getTimeLabel(); } public String getTimeLabel() { return timeLabel; } //为了给每一个闹钟设定一个标识,方便取消闹钟的使用能知道是哪一个闹钟 public int getId(){ return (int) (getTime()/1000/60); } } }
当触发事件发生时,我们就要播放我们之前准备的音乐了,但是怎么播放呢,这里我们就要用到mediaPlayer媒体播放器这个函数了,这个函数主要就是用来播放音乐或者动画等。当开始播放时,我们也需要弹出警告框,提醒用户去关闭闹钟,所以这里我们需要另外编写一个类,用来执行这些功能。
PlayAlarmAty类中有播放音乐,开启震动,弹出关闭闹钟的dialog。
public class PlayAlarmAty extends Activity { // 音乐播放器 private MediaPlayer mMediaPlayer; private Vibrator vibrator; private PowerManager.WakeLock mWakelock; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); // hide title Window win = getWindow(); WindowManager.LayoutParams winParams = win.getAttributes(); winParams.flags |= (WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); // 播放闹钟铃声 mMediaPlayer = MediaPlayer.create(this,R.raw.music); //使用create方式,创建MediaPlayer对象 mMediaPlayer.setLooping(true); // 设置是否对播放的音乐进行循环播放 mMediaPlayer.start(); startVibrator(); createDialog(); } @Override protected void onPause() { super.onPause(); finish(); // 释放锁屏 releaseWakeLock(); } @Override protected void onResume() { super.onResume(); // 唤醒屏幕 acquireWakeLock(); } @Override protected void onDestroy() { super.onDestroy(); mMediaPlayer.stop(); mMediaPlayer.release(); // 释放掉 } // 唤醒屏幕 private void acquireWakeLock() { if (mWakelock == null) { PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); mWakelock = pm.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.SCREEN_DIM_WAKE_LOCK, this.getClass() .getCanonicalName()); mWakelock.acquire(); } } // 释放锁屏 private void releaseWakeLock() { if (mWakelock != null && mWakelock.isHeld()) { mWakelock.release(); mWakelock = null; } } // 震动 private void startVibrator() { // 想设置震动大小可以通过改变pattern来设定,如果开启时间太短,震动效果可能感觉不到 vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE); long[] pattern = { 500, 1000, 500, 1000 }; // 停止 开启 停止 开启 vibrator.vibrate(pattern, 0); } private void createDialog() { new AlertDialog.Builder(this) .setIcon(R.drawable.ic_clock) .setTitle("闹钟") .setMessage("闹钟时间到了!!!") .setPositiveButton("推迟10分钟", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { tenMRemind(); mMediaPlayer.stop(); vibrator.cancel(); finish(); } }) .setNegativeButton("关闭", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { mMediaPlayer.stop(); vibrator.cancel(); finish(); } }).create().show(); } // 推迟10分钟提醒 private void tenMRemind(){ //设置时间 Calendar calendar_now = Calendar.getInstance(); calendar_now.setTimeInMillis(System.currentTimeMillis()); calendar_now.set(Calendar.HOUR_OF_DAY, calendar_now.get(Calendar.HOUR_OF_DAY)); calendar_now.set(Calendar.MINUTE, calendar_now.get(Calendar.MINUTE)+10); calendar_now.set(Calendar.SECOND, 0); calendar_now.set(Calendar.MILLISECOND, 0); //时间选择好了 Intent intent = new Intent(this, AlarmReceiver.class); //注册闹钟广播 PendingIntent sender = PendingIntent.getBroadcast( this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); AlarmManager am; am = (AlarmManager) this.getSystemService(this.ALARM_SERVICE); am.set(AlarmManager.RTC_WAKEUP, calendar_now.getTimeInMillis(), sender); } }
但是要当时间到了启动这个活动,就需要一个接收器,接受这个事件,所以有需要另一个类AlarmReceiver。
public class AlarmReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { System.out.println("闹钟执行了"); // 闹钟执行一次就取消当前所执行的闹钟 AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); // 取消闹钟 am.cancel(PendingIntent.getBroadcast(context,getResultCode(),new Intent(context,AlarmReceiver.class),0)); Intent i = new Intent(context,PlayAlarmAty.class); // 要启动的类 i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // 设置启动的模式 context.startActivity(i); } }
三、秒表功能
秒表功能包含四个功能键,分别为开始,暂停、继续和重置。所以需要四个Button,然后需要三个EditText分别用来给用户输入时分秒。具体的xml代码如下:
// 秒表 <com.example.tsclock.StopWatchView android:id="@+id/tabStopWatch" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:id="@+id/timeHour" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:textAppearance="?android:attr/textAppearanceLarge"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=":" android:textAppearance="?android:attr/textAppearanceLarge"/> <TextView android:id="@+id/timeMin" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:textAppearance="?android:attr/textAppearanceLarge"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=":" android:textAppearance="?android:attr/textAppearanceLarge"/> <TextView android:id="@+id/timeSec" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:textAppearance="?android:attr/textAppearanceLarge"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="." android:textAppearance="?android:attr/textAppearanceLarge"/> <TextView android:id="@+id/timeMSec" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:textAppearance="?android:attr/textAppearanceLarge"/> </LinearLayout> <ListView android:id="@+id/lvWatchTime" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"/> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <Button android:id="@+id/btnSWStart" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="@string/start" android:textColor="#FFFFFF" android:background="@color/colorBlue"/> <Button android:id="@+id/btnSWPause" android:layout_width="0dp" android:layout_weight="1" android:layout_height="wrap_content" android:text="@string/pause" android:textColor="#FFFFFF" android:background="@color/colorBlue"/> <Button android:id="@+id/btnSWResume" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="@string/resume" android:textColor="#FFFFFF" android:background="@color/colorBlue"/> <Button android:id="@+id/btnSWLap" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="@string/lap" android:textColor="#FFFFFF" android:background="@color/colorBlue"/> <Button android:id="@+id/btnSWReset" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="@string/reset" android:textColor="#FFFFFF" android:background="@color/colorBlue"/> </LinearLayout> </com.example.tsclock.StopWatchView>
在秒表功能中,含有四个Button,但是有时候只要显示一个或者是两个其余的就需要隐藏,所以这里就需要用到Button中的属性setVisibility(View.GONE)或者是setVisibility(View.VISIBLE),这是用来隐藏和显示Button。
有时候我们需要考虑系统的健壮性,比如当我们输入大于59的数或者是小于0的数,这时候我们需要系统检测出来,并进行修正。
需要注意的就是,当我们修改计时的时间的时候,当我们不小心将数目清空的时候,这时候就会将空指针上传,导致系统的崩溃,所以我们需要判断是不是空指针,防止越界报错。
该秒表功能有五个Button,所以需要对每个Button添加触发事件,其实startTime()函数的功能为开始计时,stopTime()函数的功能为暂停计时。所以这里需要弄清楚的就是什么时候该让那些按钮隐藏,什么时候该让那些按钮显示。
public class StopWatchView extends LinearLayout { private TextView tvHour,tvMin,tvSec,tvMSec; private Button btnSWStart,btnSWResume,btnSWReset,btnSWLap,btnSWPause; private ListView lvTimeList; private ArrayAdapter<String> adapter; private Timer mTimer = new Timer(); private TimerTask mTimerTask = null; private int tenMSec = 0; private TimerTask showTimerTask = null; private static final int MSG_WHAT_SHOW_TIME = 1; public StopWatchView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } @Override protected void onFinishInflate() { super.onFinishInflate(); tvHour = (TextView) findViewById(R.id.timeHour); tvHour.setText("0"); tvMin = (TextView) findViewById(R.id.timeMin); tvMin.setText("0"); tvSec = (TextView) findViewById(R.id.timeSec); tvSec.setText("0"); tvMSec = (TextView) findViewById(R.id.timeMSec); tvMSec.setText("0"); // 计时 btnSWLap = (Button) findViewById(R.id.btnSWLap); btnSWLap.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // 时 分 秒 毫秒 adapter.insert(String.format("%d:%d:%d.%d",tenMSec/100/60/60,tenMSec/100/60%60,tenMSec/100%60,tenMSec%100),0); } }); // 开始 btnSWStart = (Button) findViewById(R.id.btnSWStart); btnSWStart.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { startTimer(); btnSWStart.setVisibility(View.GONE); btnSWPause.setVisibility(View.VISIBLE); btnSWLap.setVisibility(View.VISIBLE); } }); // 暂停 btnSWPause = (Button) findViewById(R.id.btnSWPause); btnSWPause.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { stopTimer(); btnSWPause.setVisibility(View.GONE); btnSWResume.setVisibility(View.VISIBLE); btnSWLap.setVisibility(View.GONE); btnSWReset.setVisibility(View.VISIBLE); } }); // 继续 btnSWResume = (Button) findViewById(R.id.btnSWResume); btnSWResume.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { startTimer(); btnSWResume.setVisibility(View.GONE); btnSWPause.setVisibility(View.VISIBLE); btnSWReset.setVisibility(View.GONE); btnSWLap.setVisibility(View.VISIBLE); } }); // 重置 btnSWReset = (Button) findViewById(R.id.btnSWReset); btnSWReset.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { stopTimer(); tenMSec = 0; adapter.clear();// 重置需要清除列表 btnSWLap.setVisibility(View.GONE); btnSWPause.setVisibility(View.GONE); btnSWResume.setVisibility(View.GONE); btnSWReset.setVisibility(View.GONE); btnSWStart.setVisibility(View.VISIBLE); } }); // 设置除了开始之外 其它四个按钮不可见 btnSWLap.setVisibility(View.GONE); btnSWPause.setVisibility(View.GONE); btnSWResume.setVisibility(View.GONE); btnSWReset.setVisibility(View.GONE); lvTimeList = (ListView) findViewById(R.id.lvWatchTime); // 初始化话adapter adapter = new ArrayAdapter<String>(getContext(),android.R.layout.simple_list_item_1); lvTimeList.setAdapter(adapter); // 使用showTimerTask不断执行刷新的操作 showTimerTask = new TimerTask() { @Override public void run() { handler.sendEmptyMessage(MSG_WHAT_SHOW_TIME); } }; mTimer.schedule(showTimerTask,200,200); // 一秒钟刷新五次 } private void startTimer(){ if (mTimerTask == null){ mTimerTask = new TimerTask() {// 计时的timerTask @Override public void run() { tenMSec++; } }; mTimer.schedule(mTimerTask,10,10); // 每隔十毫秒执行一次 } } private void stopTimer(){ if (mTimerTask != null){ mTimerTask.cancel(); mTimerTask = null; } } // 呈现时间的handler private Handler handler = new Handler(){ @Override public void handleMessage(@NonNull Message msg) { super.handleMessage(msg); switch (msg.what){ case MSG_WHAT_SHOW_TIME: tvHour.setText(tenMSec/100/60/60+""); // 时 tvMin.setText(tenMSec/100/60%60+""); // 分 tvSec.setText(tenMSec/100%60+""); // 秒 tvMSec.setText(tenMSec%100+""); // 毫秒 break; default: break; } } }; public void onDestory() { mTimer.cancel(); } }
四、计时器功能
这个和上面讲了秒表比较类似,不同的是多一个Button按钮用来计时,还多一个EditView(毫秒值),另外还需要一个ListView用来显示计时的时间,详细的xml代码如下:
// 计时器 <com.example.tsclock.TimerView android:id="@+id/tabTimer" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"> <EditText android:id="@+id/etHour" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:inputType="number" android:singleLine="true" android:textAppearance="?android:attr/textAppearanceLarge"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=":" android:textAppearance="?android:attr/textAppearanceLarge"/> <EditText android:id="@+id/etMin" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:inputType="number" android:singleLine="true" android:textAppearance="?android:attr/textAppearanceLarge"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=":" android:textAppearance="?android:attr/textAppearanceLarge"/> <EditText android:id="@+id/etSec" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:inputType="number" android:singleLine="true" android:textAppearance="?android:attr/textAppearanceLarge"/> </LinearLayout> <LinearLayout android:id="@+id/btnGroup" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <Button android:id="@+id/btnStart" android:layout_width="0dp" android:text="@string/start" android:layout_height="wrap_content" android:layout_weight="1" android:textColor="#FFFFFF" android:background="@color/colorBlue"/> <Button android:id="@+id/btnPause" android:layout_width="0dp" android:text="@string/pause" android:layout_height="wrap_content" android:layout_weight="1" android:textColor="#FFFFFF" android:background="@color/colorBlue"/> <Button android:id="@+id/btnResume" android:layout_width="0dp" android:text="@string/resume" android:layout_height="wrap_content" android:layout_weight="1" android:textColor="#FFFFFF" android:background="@color/colorBlue"/> <Button android:id="@+id/btnReset" android:layout_width="0dp" android:text="@string/reset" android:layout_height="wrap_content" android:layout_weight="1" android:textColor="#FFFFFF" android:background="@color/colorBlue"/> </LinearLayout> </com.example.tsclock.TimerView>
计时器功能和秒表功能差不多
public class TimerView extends LinearLayout { private Button btnStart,btnPause,btnResume,btnReset; private EditText etHour,etMin,etSec; private Timer mTimer = new Timer(); // 计时器 private TimerTask mTimerTask = null; private int allTimerCount = 0; private static final int MSG_WHAT_TIME_IS_UP = 1; private static final int MSG_WHAT_TIME_IS_TICK = 2; // 时钟一格一格的往下走 private ListView mListView; public TimerView(Context context) { super(context); } public TimerView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } @Override protected void onFinishInflate() { super.onFinishInflate(); //开始 btnStart = (Button) findViewById(R.id.btnStart); btnStart.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { startTimer(); btnStart.setVisibility(View.GONE); btnPause.setVisibility(View.VISIBLE); btnReset.setVisibility(View.VISIBLE); etHour.setEnabled(false); etMin.setEnabled(false); etSec.setEnabled(false); } }); // 暂停 btnPause = (Button) findViewById(R.id.btnPause); btnPause.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { stopTimer(); btnPause.setVisibility(View.GONE); btnResume.setVisibility(View.VISIBLE); } }); // 继续 btnResume = (Button) findViewById(R.id.btnResume); btnResume.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { startTimer(); btnResume.setVisibility(View.GONE); btnPause.setVisibility(View.VISIBLE); } }); // 重置 btnReset = (Button) findViewById(R.id.btnReset); btnReset.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { stopTimer(); etHour.setText("00"); etMin.setText("00"); etSec.setText("00"); etHour.setEnabled(true); etMin.setEnabled(true); etSec.setEnabled(true); btnStart.setVisibility(View.VISIBLE); btnPause.setVisibility(View.GONE); btnResume.setVisibility(View.GONE); btnReset.setVisibility(View.GONE); } }); etHour = (EditText) findViewById(R.id.etHour); etMin = (EditText) findViewById(R.id.etMin); etSec = (EditText) findViewById(R.id.etSec); etHour.setText("00"); // 添加事件监听器 etHour.addTextChangedListener(new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { if (!TextUtils.isEmpty(s)){ int value = Integer.parseInt(s.toString()); if (value > 59){ etHour.setText("59"); }else if (value < 0){ etHour.setText("0"); } } checkToEnableBtnStart(); } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void afterTextChanged(Editable s) { } }); etMin.setText("00"); etMin.addTextChangedListener(new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { if (!TextUtils.isEmpty(s)){ int value = Integer.parseInt(s.toString()); if (value > 59){ etMin.setText("59"); }else if (value < 0){ etMin.setText("0"); } } checkToEnableBtnStart(); } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void afterTextChanged(Editable s) { } }); etSec.setText("00"); etSec.addTextChangedListener(new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { if (!TextUtils.isEmpty(s)){ // 当文字框中不为空 int value = Integer.parseInt(s.toString()); if (value > 59){ etSec.setText("59"); }else if (value < 0){ etSec.setText("0"); } } checkToEnableBtnStart(); } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void afterTextChanged(Editable s) { } }); btnStart.setVisibility(View.VISIBLE); // 设置开始可见 btnStart.setEnabled(false); // 不可点击(开始还没有设置时间) btnPause.setVisibility(View.GONE); // 设置暂停不可见 btnResume.setVisibility(View.GONE); btnReset.setVisibility(View.GONE); } private void checkToEnableBtnStart(){ btnStart.setEnabled((!TextUtils.isEmpty(etHour.getText()) && Integer.parseInt(etHour.getText().toString()) > 0) || (!TextUtils.isEmpty(etMin.getText()) &&Integer.parseInt(etMin.getText().toString()) > 0) || (!TextUtils.isEmpty(etSec.getText()) &&Integer.parseInt(etSec.getText().toString()) > 0)); } private void startTimer(){ if (mTimerTask == null){ // 所使用时间的计数 allTimerCount = Integer.parseInt(etHour.getText().toString())*60*60+Integer.parseInt(etMin.getText().toString())*60+Integer.parseInt(etSec.getText().toString()); mTimerTask = new TimerTask() { @Override public void run() { // run方法会被mTimer执行 // 每执行一次 计数减一 allTimerCount--; // 获取到当 mHandler.sendEmptyMessage(MSG_WHAT_TIME_IS_TICK); if (allTimerCount <= 0){ // 访问mHandler mHandler.sendEmptyMessage(MSG_WHAT_TIME_IS_UP); stopTimer(); } } }; mTimer.schedule(mTimerTask,1000,1000); // run方法每隔一秒执行一次 } } private void stopTimer(){ if (mTimerTask != null){ mTimerTask.cancel(); mTimerTask = null; } } private Handler mHandler = new Handler(){ @Override public void handleMessage(@NonNull Message msg) { super.handleMessage(msg); switch (msg.what){ case MSG_WHAT_TIME_IS_TICK: int hour = allTimerCount/60/60; int min = (allTimerCount/60)%60; int sec = allTimerCount%60; etHour.setText(hour+""); etMin.setText(min+""); etSec.setText(sec+""); break; case MSG_WHAT_TIME_IS_UP: // 执行弹出对话框操作 // 把时间停止(弹出一个对话框) new AlertDialog.Builder(getContext()).setTitle("时间到了!!!").setNegativeButton("退出",null).show(); btnPause.setVisibility(View.GONE); btnResume.setVisibility(View.GONE); btnReset.setVisibility(View.GONE); btnStart.setVisibility(View.VISIBLE); etHour.setEnabled(true); etMin.setEnabled(true); etSec.setEnabled(true); break; default: break; } } }; }
加载全部内容