Android多功能时钟开发案例(实战篇)

上一篇为大家介绍的是Android多功能时钟开发基础内容,大家可以回顾一下,Android多功能时钟开发案例(基础篇)

接下来进入实战,快点来学习吧。

一、时钟
在布局文件中我们看到,界面上只有一个TextView,这个TextView的作用就是显示一个系统的当前时间,同时这个时间还是一秒一秒跳的,要实现一秒一秒的跳就需要我们每隔一秒就要刷新一下,同时我们这里还考虑了切换到另一个Tab的时候,这个时间就不跳动了,这样就会减少这个对系统的占用,考虑到了这点我们在这里用到了Handler,通过handler发送的msg.what 来判断是否要刷新时间。

public class TimeView extends LinearLayout { private TextView tvTime; public TimeView(Context context) { super(context); } public TimeView(Context context, AttributeSet attrs) { super(context, attrs); } public TimeView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected void onFinishInflate() { super.onFinishInflate(); tvTime = (TextView) findViewById(R.id.tvTime); //tvTime.setText("hello"); timeHandler.sendEmptyMessage(0); } @Override protected void onVisibilityChanged(View changedView, int visibility) { super.onVisibilityChanged(changedView, visibility); //当再次切换到这个Tab时我们就再发送一次这个消息,否者就把所有的消息移除掉 if (visibility == View.VISIBLE) { timeHandler.sendEmptyMessage(0); }else{ timeHandler.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 timeHandler = new Handler(){ public void handleMessage(android.os.Message msg) { refreshTime(); //处于当前Tab的时候给自己发送信息,可以刷新 if (getVisibility() == View.VISIBLE) { //1秒钟后再次执行以下sendEmptyMessage,what参数用于区分不同的message timeHandler.sendEmptyMessageDelayed(0, 1000); } }; }; }

其实这里的Handler可以用Timer来完成亦可以达到同样的效果。
在这里要提一下的是onFinishInflate(),这在我们自定义布局的时候一定要用到的,解释以及例子在之后上传的知识点中同样有,看看那个就可以了。
二、闹钟
从第二个布局中我们可以看到,我们在这里用到了一个ListView,这是用来存储我们添加的闹钟的,既然这里用到了ListView,那么我们接着就会想到要给这个ListView一个适配器adapter,因此我们会在这里创建这么一个适配器,
private ArrayAdapter<AlarmData> adapter; 
看到这里可能又会有疑问了,AlarmData这是个什么东西?有这么一个数据类型吗??其实这里我们自定义了一个数据类型,用来专门存储一下创建的闹钟时间。我们来看一下自定义的数据类型代码吧!

// 自定义数据类型 private static class AlarmData { private long time = 0; private Calendar date; private String timeLabel = ""; 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 long getTime() { return time; } public String getTimeLabel() { return timeLabel; } public int getId() { return (int) (getTime() / 1000 / 60); } @Override public String toString() { return getTimeLabel(); } }

这个数据类型的代码其实还是很容易明白的,不多讲了。
当我们到这里的时候,我们其实还没有真正的完成,假如我们的代码已经写好了,并且可以运行了我们运行一次后,并且添加了N个闹钟,当我们退出程序,再次打开就会发现,我们之前创建的闹钟都没了,原因是我们虽然把数据临时的保存在了ListView中,但是我们并没有长时间的保存,因此我们接着就来讲讲长久的保存这些闹钟数据。

private void saveAlarmList() { Editor editor = getContext().getSharedPreferences( AlarmView.class.getName(), Context.MODE_PRIVATE).edit(); StringBuffer sb = new StringBuffer(); for (int i = 0; i < adapter.getCount(); i++) { sb.append(adapter.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 readSaveAlarmList() { SharedPreferences sp = getContext().getSharedPreferences( AlarmView.class.getName(), Context.MODE_PRIVATE); String content = sp.getString(KEY_ALARM_LIST, null); if (content != null) { String[] timeStrings = content.split(","); for (String string : timeStrings) { adapter.add(new AlarmData(Long.parseLong(string))); } } }

上面的一些陌生的类型在之后的知识点中可以查看。

接着我们来看看最关键的就是添加闹钟

private void addAlarm() { Calendar c = Calendar.getInstance(); new TPDiolog(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 (currentTime.getTimeInMillis() >= calendar.getTimeInMillis()) { calendar.setTimeInMillis(calendar.getTimeInMillis() + 24 * 60 * 60 * 1000); } AlarmData ad = new AlarmData(calendar.getTimeInMillis()); adapter.add(ad); alarmManager.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(); }

这里我们可以看到TPDiolog这个,当你自己尝试过后可能也会遇到同样的问题,那就是当你通过TimePickerDialog这个系统的时间选择控件的时候,点击确定后,会创建两条记录,这是因为我们点击确定后会调用该事件监听器的时间,在关闭这个Dialog的时候也会调用一次,所以我们在这里自己重写了一下该类的方法

TPDiolog.class

public class TPDiolog extends TimePickerDialog { public TPDiolog(Context context, OnTimeSetListener callBack, int hourOfDay, int minute, boolean is24HourView) { super(context, callBack, hourOfDay, minute, is24HourView); } //重写该方法是为了避免调用两次onTimeSet @Override protected void onStop() { //super.onStop(); } }

在之前的代码中我们还看到了一个alarmManager这一对象,这是我们为了调用系统的闹钟服务创建的实例,我们也因此而创建了一个AlarmReceiver.class

public class AlarmReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent arg1) { 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); //设置intent的启动模式 i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(i); } }

一些小的地方讲好了,最后把AlarmView.class的完整代码贴上。

package com.example.clock; import java.util.Calendar; import java.util.Date; import java.util.Iterator; import android.app.AlarmManager; import android.app.AlertDialog; import android.app.PendingIntent; import android.app.TimePickerDialog; import android.app.TimePickerDialog.OnTimeSetListener; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.util.AttributeSet; import android.view.View; import android.widget.AdapterView; import android.widget.AdapterView.OnItemLongClickListener; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.Switch; import android.widget.TimePicker; public class AlarmView extends LinearLayout { private Button btnAddAlarm; private ListView lvListAlarm; private ArrayAdapter<AlarmData> adapter; private AlarmManager alarmManager; public AlarmView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } public AlarmView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public AlarmView(Context context) { super(context); init(); } private void init() { alarmManager = (AlarmManager) getContext().getSystemService( Context.ALARM_SERVICE); } @Override protected void onFinishInflate() { super.onFinishInflate(); btnAddAlarm = (Button) findViewById(R.id.btnAddAlarm); lvListAlarm = (ListView) findViewById(R.id.lvListAlarm); adapter = new ArrayAdapter<AlarmData>(getContext(), android.R.layout.simple_list_item_1); lvListAlarm.setAdapter(adapter); readSaveAlarmList(); // adapter.add(new AlarmData(System.currentTimeMillis())); btnAddAlarm.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { addAlarm(); } }); // 长按某项删除 lvListAlarm.setOnItemLongClickListener(new OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView<?> arg0, View arg1, final int position, long arg3) { new AlertDialog.Builder(getContext()) .setTitle("操作选项") .setItems(new CharSequence[] { "删除", "删除1" }, 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 = adapter.getItem(position); adapter.remove(ad); saveAlarmList(); alarmManager.cancel(PendingIntent.getBroadcast(getContext(), ad.getId(), new Intent(getContext(), AlarmReceiver.class), 0)); } private void addAlarm() { Calendar c = Calendar.getInstance(); new TPDiolog(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 (currentTime.getTimeInMillis() >= calendar.getTimeInMillis()) { calendar.setTimeInMillis(calendar.getTimeInMillis() + 24 * 60 * 60 * 1000); } AlarmData ad = new AlarmData(calendar.getTimeInMillis()); adapter.add(ad); alarmManager.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(); } private static final String KEY_ALARM_LIST = "alarmlist"; private void saveAlarmList() { Editor editor = getContext().getSharedPreferences( AlarmView.class.getName(), Context.MODE_PRIVATE).edit(); StringBuffer sb = new StringBuffer(); for (int i = 0; i < adapter.getCount(); i++) { sb.append(adapter.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 readSaveAlarmList() { SharedPreferences sp = getContext().getSharedPreferences( AlarmView.class.getName(), Context.MODE_PRIVATE); String content = sp.getString(KEY_ALARM_LIST, null); if (content != null) { String[] timeStrings = content.split(","); for (String string : timeStrings) { adapter.add(new AlarmData(Long.parseLong(string))); } } } // 自定义数据类型 private static class AlarmData { private long time = 0; private Calendar date; private String timeLabel = ""; 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 long getTime() { return time; } public String getTimeLabel() { return timeLabel; } public int getId() { return (int) (getTime() / 1000 / 60); } @Override public String toString() { return getTimeLabel(); } } }

三、计时器
计时器的主要功能就是你先设定一个时间,然后点击开始,时间就会一秒一秒的减少,在这里我么主要用到了Timer这个系统的计时器,这代码中没有上面难懂的地方,有些地方已经给上注释了,所以直接贴代码,可能有些人会不知道Timer怎么用,之后的知识点中都会有提到。

package com.example.clock; import java.util.Timer; import java.util.TimerTask; import android.R.integer; import android.app.AlertDialog; import android.content.Context; import android.os.Handler; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; import android.util.AttributeSet; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; public class TimerView extends LinearLayout { public TimerView(Context context, AttributeSet attrs) { super(context, attrs); } public TimerView(Context context) { super(context); } @Override protected void onFinishInflate() { super.onFinishInflate(); btnStart = (Button) findViewById(R.id.btnStart); btnPause = (Button) findViewById(R.id.btnPause); btnResume = (Button) findViewById(R.id.btnResume); btnReset = (Button) findViewById(R.id.btnReset); btnStart.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { startTimer(); btnStart.setVisibility(View.GONE); btnPause.setVisibility(View.VISIBLE); btnReset.setVisibility(View.VISIBLE); } }); btnPause.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { stopTimer(); btnPause.setVisibility(View.GONE); btnResume.setVisibility(View.VISIBLE); } }); btnResume.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { startTimer(); btnPause.setVisibility(View.VISIBLE); btnResume.setVisibility(View.GONE); } }); btnReset.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { stopTimer(); etHour.setText("00"); etMin.setText("00"); etSec.setText("00"); btnReset.setVisibility(View.GONE); btnResume.setVisibility(View.GONE); btnPause.setVisibility(View.GONE); btnStart.setVisibility(View.VISIBLE); } }); 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) { /* * 这个方法是在Text改变过程中触发调用的, 它的意思就是说在原有的文本s中, * 从start开始的count个字符替换长度为before的旧文本, * 注意这里没有将要之类的字眼,也就是说一句执行了替换动作。 */ if (!TextUtils.isEmpty(s)) { int value = Integer.parseInt(s.toString()); if (value > 59) { etHour.setText("59"); } else if (value < 0) { etHour.setText("00"); } } 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("00"); } } 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("00"); } } 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 (timerTask == null) { allTimeCount = Integer.parseInt(etHour.getText().toString()) * 60 * 60 + Integer.parseInt(etMin.getText().toString()) * 60 + Integer.parseInt(etSec.getText().toString()); timerTask = new TimerTask() { @Override public void run() { allTimeCount--; handle.sendEmptyMessage(MSG_WHAT_TIME_TICK); if (allTimeCount <= 0) { handle.sendEmptyMessage(MSG_WHAT_TIME_IS_UP); stopTimer(); } } }; timer.schedule(timerTask, 1000, 1000); } } private void stopTimer(){ if (timerTask!=null) { timerTask.cancel(); timerTask=null; } } private Handler handle = new Handler(){ public void handleMessage(android.os.Message msg) { switch (msg.what) { case MSG_WHAT_TIME_TICK: int hour = allTimeCount/60/60; int min = (allTimeCount/60)%60; int sec = allTimeCount%60; etHour.setText(hour+""); etMin.setText(min+""); etSec.setText(sec+""); break; case MSG_WHAT_TIME_IS_UP: new AlertDialog.Builder(getContext()) .setTitle("Time is up!") .setMessage("Time is up!") .setNegativeButton("Cancle", null).show(); btnReset.setVisibility(View.GONE); btnResume.setVisibility(View.GONE); btnPause.setVisibility(View.GONE); btnStart.setVisibility(View.VISIBLE); break; default: break; } }; }; private static final int MSG_WHAT_TIME_IS_UP = 1; private static final int MSG_WHAT_TIME_TICK = 2; private int allTimeCount = 0; private Timer timer = new Timer(); private TimerTask timerTask = null; private Button btnStart, btnPause, btnResume, btnReset; private EditText etHour, etMin, etSec; }

四、秒表
最后的秒表相信大家都不陌生,用到的知识正好之前的三个都有讲到,只要明白前三个后,这个就不难理解了。

package com.example.clock; import java.util.ArrayList; import java.util.Timer; import java.util.TimerTask; import android.content.Context; import android.os.Handler; import android.util.AttributeSet; import android.view.View; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.TextView; public class StopWatchView extends LinearLayout { private TextView tvHour,tvMin,tvSec,tvMsec; private Button btnStart,btnPause,btnResume,btnReset,btnLap; private ListView lvTimeList; private ArrayAdapter<String> adapter; public StopWatchView(Context context, 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"); btnStart = (Button) findViewById(R.id.btnSWStart); btnPause = (Button) findViewById(R.id.btnSWPause); btnResume = (Button) findViewById(R.id.btnSWResume); btnLap = (Button) findViewById(R.id.btnSWLap); btnReset = (Button) findViewById(R.id.btnSWReset); btnStart.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { startTimer(); btnStart.setVisibility(View.GONE); btnPause.setVisibility(View.VISIBLE); btnLap.setVisibility(View.VISIBLE); } }); btnPause.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { stopTimer(); btnPause.setVisibility(View.GONE); btnResume.setVisibility(View.VISIBLE); btnLap.setVisibility(View.GONE); btnReset.setVisibility(View.VISIBLE); } }); btnResume.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { startTimer(); btnResume.setVisibility(View.GONE); btnPause.setVisibility(View.VISIBLE); btnLap.setVisibility(View.VISIBLE); btnReset.setVisibility(View.GONE); } }); btnReset.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { stopTimer(); tenMSecs = 0; adapter.clear(); btnReset.setVisibility(View.GONE); btnLap.setVisibility(View.GONE); btnPause.setVisibility(View.GONE); btnResume.setVisibility(View.GONE); btnStart.setVisibility(View.VISIBLE); } }); btnLap.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { adapter.insert(String.format("%d:%d:%d.%d", tenMSecs/100/60/60,tenMSecs/100/60%60,tenMSecs/100%60,tenMSecs%100), 0); } }); btnLap.setVisibility(View.GONE); btnPause.setVisibility(View.GONE); btnResume.setVisibility(View.GONE); btnReset.setVisibility(View.GONE); lvTimeList = (ListView) findViewById(R.id.lvWatchTime); adapter = new ArrayAdapter<String>(getContext(), android.R.layout.simple_list_item_1); lvTimeList.setAdapter(adapter); showTimerTask = new TimerTask() { @Override public void run() { handle.sendEmptyMessage(MSG_WHAT_SHOW_TIME); } }; timer.schedule(showTimerTask, 200, 200); } private void startTimer(){ if (timerTask == null) { timerTask = new TimerTask() { @Override public void run() { tenMSecs++; } }; timer.schedule(timerTask, 10, 10); } } private void stopTimer(){ if (timerTask != null) { timerTask.cancel(); timerTask = null; } } private int tenMSecs = 0; private Timer timer =new Timer(); private TimerTask timerTask = null; private TimerTask showTimerTask = null; private static final int MSG_WHAT_SHOW_TIME = 1; private Handler handle = new Handler(){ public void handleMessage(android.os.Message msg) { switch (msg.what) { case MSG_WHAT_SHOW_TIME: tvHour.setText(tenMSecs/100/60/60+""); tvMin.setText(tenMSecs/100/60%60+""); tvSec.setText(tenMSecs/100%60+""); tvMsec.setText(tenMSecs%100+""); break; default: break; } }; }; public void onDestroy() { timer.cancel(); } }

到此为止,自己的第一个实战算是完成了,但是就是界面很low,这个只是把基本的功能实现了,但是在界面上没有做很大的完善,在之后的实战中会慢慢改进的。

源码下载:Android多功能时钟

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

时间: 2024-07-28 20:25:58

Android多功能时钟开发案例(实战篇)的相关文章

Android多功能时钟开发案例(实战篇)_Android

上一篇为大家介绍的是Android多功能时钟开发基础内容,大家可以回顾一下,Android多功能时钟开发案例(基础篇) 接下来进入实战,快点来学习吧. 一.时钟在布局文件中我们看到,界面上只有一个TextView,这个TextView的作用就是显示一个系统的当前时间,同时这个时间还是一秒一秒跳的,要实现一秒一秒的跳就需要我们每隔一秒就要刷新一下,同时我们这里还考虑了切换到另一个Tab的时候,这个时间就不跳动了,这样就会减少这个对系统的占用,考虑到了这点我们在这里用到了Handler,通过hand

Android多功能时钟开发案例(基础篇)

本文我们进入Android多功能时钟开发实战学习,具体的效果可以参考手机上的时钟,内容如下 首先我们来看一看布局文件layout_main.xml 整个布局: <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/container" android:layout_width="match_parent" androi

iOS时钟开发案例分享_IOS

本文实例为大家介绍了iOS时钟开发过程,供大家参考,具体内容如下 思路就是利用CALayer的隐式动画来实现.因为UIView的非根层也就是手动创建的layer在其属性发生变化时会默认会产生动画效果,这些属性也叫作可动画属性.比如bounds.backgroundColor.position. 时钟里面表盘就是一个UIView,而三根针就是三个手动创建的layer. 先在storyboard上弄一个UIImageView,设置表盘图片 然后在viewDidLoad中初始化三根针,并设置定时器,获

微信小程序自定义多功能模态对话框案例实战

   这段时间刚好在做微信小程序相关项目开发,发现微信官方提供的小程序弹窗功能有限, 有些功能根本没法实现,这篇文章主要介绍微信小程序项目实战之自定义模态对话框wxDialog,处理一些复杂的弹窗逻辑业务,采用了仿原生.wxui写法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下哈~~~ 先来个demo预览页面: api配置参数如下: var config = { type: '', //设置弹窗显示类型 ->默认:0 (0表示信息框,1表示页面层) title: '', //标题 conte

利用Angularjs和Bootstrap前端开发案例实战_javascript技巧

我们将利用Angularjs 和 Bootstrap,开发一个前端应用实例,通过这一次简单的项目实战,引领大家进入AngularJS前端开发的殿堂,并向大家介绍一下几个知识点:  1.MVC 基础,通过项目实例,让大家初步体会MVC设计模式的应用.  2.构建我们第一个AngularJS应用,通过一个实际用例的开发,大家可以对前端开发获得一定的感性认识.  3.初步了解AngularJS三个最重要的组成部件,他们分别是Model, View, 和Controller.  4.初步了解Angula

Android指纹识别功能深入浅出分析到实战(6.0以下系统解决方案)_Android

指纹识别这个名词听起来并不陌生,但是实际开发过程中用得并不多.Google从Android6.0(api23)开始才提供标准指纹识别支持,并对外提供指纹识别相关的接口.本文除了能适配6.0及以上系统,主要还提供6.0以下设备适配解决方案. 指纹识别用途 大概列举几个指纹识别的用途 系统解锁 应用锁 支付认证 普通的登录认证 指纹识别Google官方文档 官方标准库 Google提供的与指纹识别相关的核心类不多,主类是FingerprintManager,主类依赖三个内部类,如下图所示:  Fin

《Android传感器开发与智能设备案例实战》——第1章,第1.1节智能手机操作系统介绍

第1章 Android开发技术基础Android传感器开发与智能设备案例实战Android是科技界巨头谷歌公司推出的一款智能设备操作系统,是以Linux开源系统架构为基础的.Android功能十分强大.从2008年推出到现在,Android一直在全球智能手机操作系统市场中占居第一的宝座.本章简单介绍Android的发展历程和背景,并介绍智能设备和Android的密切关系,为读者步入本书后面知识的学习打下基础. 1.1 智能手机操作系统介绍在Android系统诞生之前,智能手机这个新鲜事物大大丰富

《Android开发案例驱动教程》

<Android开发案例驱动教程>   作者:关东升,赵志荣Java或C++程序员转变成为Android程序员 采用案例驱动模式展开讲解知识点,即介绍案例->案例涉及技术->展开知识点->总结的方式 本书作者从事多年一线开发和培训,讲解知识点力求细致,深入浅出 目    录 前言 第1章  Android操作系统概述 1 1.1  Android历史介绍 1 1.2  Android架构 1 1.3  Android平台介绍 2 1.4  现有智能手机操作系统比较 4 第2章

SpringBoot开发案例之整合mail队列篇

前言 前段时间搞了个SpringBoot开发案例之整合mail发送服务,也是基于目前各项目平台的邮件发送功能做一个抽离和整合. 问题 以发送方为例,比如网易的反垃圾邮件政策,过多或者频率过快的发送都会被判定为垃圾邮件,即使发再多的邮件也无济于事.当然如果是自建邮件服务器,也是要考虑发送服务的并发能力. 以接收方邮件为例,比如腾讯邮箱就有类似说明:如果内容涉嫌大量群发,并且被多数用户投诉为垃圾邮件,也就通过不了收件方的"安检",如下图: 方案 为了解决以上实际场景中遇到的问题,使得其更像