要将时间显示到TabHost中,就必须先要或许其中的id,然后通过Calendar获取当前系统的时间,最后再每过1秒钟刷新一次,这样就能够再TextView中出现时间在不停的变化。
private TextView tvTime;
//重写tvTime函数
public TimeView(Context context,AttributeSet attrs,int defStyle){
super(context,attrs,defStyle);
}
public TimeView(Context context,AttributeSet attrs){
super(context,attrs);
}
public TimeView(Context context){
super(context);
}
//这是个初始化函数,需要一开始就获取TextView的ID,然后再调用timerHandler函数
@Override
protected void onFinishInflate() {
super.onFinishInflate();
tvTime = (TextView)findViewById(R.id.tvTime);
//tvTime.setText("Hello");
timerHandler.sendEmptyMessage(0);
}
@Override
protected void onVisibilityChanged(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("%04d年 %d月 %d 日 %02d:%02d:%02d",
c.get(Calendar.YEAR),c.get(Calendar.MONTH)+1, //获取的月份是当前月份的前一个月,所以需要加1
c.get(Calendar.DATE),c.get(Calendar.HOUR_OF_DAY),
c.get(Calendar.MINUTE),c.get(Calendar.SECOND)));
}
private Handler timerHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
refreshTime();
if(getVisibility() == View.VISIBLE){
timerHandler.sendEmptyMessageDelayed(0,1000); //每过1000毫秒就刷新一次
}
};
};
这个功能实现比较简单,只是用到了几个函数,这里需要注意的就是Calendar所获取的月份是当前月份的前一个月,需要对获取的月份加1。
这个就相对比较复杂了,这里需要判断时间到了需要触发事件,需要播放音乐等。下面的代码是timeView中的核心代码,其中onFinishInflate函数是系统定义的初始化函数,而onItemLongClickListener监听事件的函数,这个作用就是当你长按闹钟时间时,就会弹出选项,你可以添加闹钟或者是删除闹钟。
public class AlarmView extends LinearLayout {
@Override
protected void onFinishInflate() {
super.onFinishInflate();
btnAddAlarm = (Button)findViewById(R.id.btnAddAlarm);
lvAlarmList = (ListView)findViewById(R.id.lvAlarmList);
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;
case 1:
addAlarm();
default:
break;
}
}
}).setNegativeButton("取消",null).show();
return true;
}
});
adapter = new ArrayAdapter(getContext(),android.R.layout.simple_list_item_1);
lvAlarmList.setAdapter(adapter);
readSaveAlarmList();
//adapter.add(new AlarmData(System.currentTimeMillis()));
alarmManager = (AlarmManager)getContext().getSystemService(Context.ALARM_SERVICE);
calendar = Calendar.getInstance();
btnAddAlarm = (Button)findViewById(R.id.btnAddAlarm);
btnAddAlarm.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
addAlarm();
}
});
}
private void deleteAlarm(int position){
AlarmData ad = adapter.getItem(position);
adapter.remove(ad);
adapter.notifyDataSetChanged();
saveAlarmList();
alarmManager.cancel(PendingIntent.getBroadcast(getContext(),ad.getId(),new Intent(getContext(),AlarmReceiver.class),0));
}
private void addAlarm(){
Calendar c = Calendar.getInstance();
c.setTimeInMillis(System.currentTimeMillis());
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());
adapter.add(ad);
adapter.notifyDataSetChanged();
/*alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
ad.getId(),
24*60*60*1000,
PendingIntent.getBroadcast(getContext(),
ad.getId(),
new Intent(getContext(),AlarmReceiver.class),0));*/
alarmManager.set(AlarmManager.RTC_WAKEUP,
ad.getTime(),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 void saveAlarmList(){
Editor editor;
editor = getContext().getSharedPreferences(AlarmView.class.getName(), Context.MODE_PRIVATE).edit();
StringBuffer sb = new StringBuffer();
for (int i =0; i1){
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 Button btnAddAlarm;
private ListView lvAlarmList;
private static final String KEY_ALARM_LIST = "alarmList";
private ArrayAdapter adapter;
private AlarmManager alarmManager;
private Calendar calendar;
private static class AlarmData{
private String timeLable = "";
private long time = 0;
private Calendar date;
public AlarmData(long time){
this.time = time;
date = Calendar.getInstance();
date.setTimeInMillis(time);
timeLable = String.format("%02d月%02d日 %02d:%02d",
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.timeLable = ad;
}
public void setTime(long time){
this.time = time;
}
public long getTime(){
return time;
}
public void setTimeLable(String timeLable){
this.timeLable = timeLable;
}
public String getTimeLable(){
return timeLable;
}
@Override
public String toString() {
return getTimeLable();
}
public int getId(){
return (int)(getTime()/1000/60);
}
}
}
当触发事件发生时,我们就要播放我们之前准备的音乐了,但是怎么播放呢,这里我们就要用到mediaPlayer媒体播放器这个函数了,这个函数主要就是用来播放音乐或者动画等。当开始播放时,我们也需要弹出警告框,提醒用户去关闭闹钟,所以这里我们需要另外编写一个类,用来执行这些功能。
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.alarm_player_aty);
mediaPlayer = new MediaPlayer();
mediaPlayer = MediaPlayer.create(this, R.raw.music);
mediaPlayer.start();
// ATTENTION: This was auto-generated to implement the App Indexing API.
// See https://g.co/AppIndexing/AndroidStudio for more information.
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setDefaults(NotificationCompat.DEFAULT_ALL);
builder.setContentTitle("闹钟响了!");
builder.setContentText("闹钟想起来了!");
builder.setSmallIcon(android.R.drawable.ic_lock_idle_alarm);
Notification notification = builder.build();
notification.flags = Notification.FLAG_ONGOING_EVENT;
notificationManager.notify(0x101,notification);
notificationManager.cancel(0);
close_btn = (Button)findViewById(R.id.close_btn);
close_btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mediaPlayer.stop();
finish();
}
});
}
@Override
protected void onPause() {
super.onPause();
finish();
}
@Override
protected void onDestroy() {
super.onDestroy();
mediaPlayer.stop();
mediaPlayer.release();
}
当闹钟激活时,你就需要一个接收器,接受这个事件。所以有需要另一个类Receiver
public class AlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
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,PlayAlarm.class);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
}
在秒表功能中,含有四个Button,但是有时候只要显示一个或者是两个其余的就需要隐藏,所以这里就需要用到Button中的属性setVisibility(View.GONE)或者是setVisibility(View.VISIBLE),这是用来隐藏和显示Button。
protected void onFinishInflate() {
super.onFinishInflate();
btnStart = (Button)findViewById(R.id.btnStart);
btnPause = (Button)findViewById(R.id.btnPause);
btnReset = (Button)findViewById(R.id.btnReset);
btnResume = (Button)findViewById(R.id.btnResume);
btnStart.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
startTimer();
btnStart.setVisibility(View.GONE);
btnPause.setVisibility(View.VISIBLE);
btnReset.setVisibility(View.VISIBLE);
}
});
有时候我们需要考虑系统的健壮性,比如当我们输入大于59的数或者是小于0的数,这时候我们需要系统检测出来,并进行修正,如下代码就是对上述的修正:
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("00");
}
}
checkToEnableBtnStart();
}
需要注意的就是,当我们修改计时的时间的时候,当我们不小心将数目清空的时候,这时候就会将空指针上传,导致系统的崩溃,所以我们需要判断是不是空指针,防止越界报错。
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));
}
该秒表功能有四个Button,所以需要对每个Button添加触发事件,其实startTime()函数的功能为开始计时,stopTime()函数的功能为暂停计时。所以这里需要弄清楚的就是什么时候该让那些按钮隐藏,什么时候该让那些按钮显示。
btnStart.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
startTimer();
btnStart.setVisibility(View.GONE);
btnPause.setVisibility(View.VISIBLE);
btnReset.setVisibility(View.VISIBLE);
}
});
btnPause.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
stopTimer();
btnPause.setVisibility(View.GONE);
btnResume.setVisibility(View.VISIBLE);
}
});
btnResume.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
startTimer();
btnResume.setVisibility(View.GONE);
btnPause.setVisibility(View.VISIBLE);
}
});
btnReset.setOnClickListener(new View.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);
}
});
由于计时器功能和秒表功能差不多,这里就不重复介绍了。
当我们写完程序之后,需要在手机上运行,我们就必须要打包(当然,在Android studio中也可以在手机上调试),一下为打包的具体步奏。
打开AndroidStudio,并且打开想要生成apk文件的项目
点击工具栏上面的“Builder”
点击“Builder”之后在下拉菜单里面可以看到“Genarate Singed APK”,点击这个选项!
之后会要求开发者输入相关的密钥文件和密码
如果开发者之前已经有了自己的密钥文件的话,此时会自动查询到以前的密钥文件(如果没有自动查询到也可以手动查找),找到之后输入密码就可以了,中途如果出错的话,那就可能是密钥文件配置不正确,需要重新申请一个密钥文件。
申请一个新的密钥文件,点击上一个图片中“Create new...”即可打开新建密钥文件的对话框,在对话框中输入相应的信息即可。
之后,生成一个新的密钥文件之后会自动返回选择密钥文件的对话框,此时只需要输入刚才申请的时候使用的密码就可以了,接下来就是选择生成apk文件时的一些信息,可以自己选择,但是一般建议使用默认的设置就可以。
Android Studio 最后Signature Version (签名版本选择) ,在打正式包的时候发现多了个签名版本选择:
从图中可以看到多了签名版本的选择,因为刚开始默认勾选的v2(Full APK Signature),没多想一路下一步下去,
结果在测试机上(5.0.1)一直都安装失败,想着和那个选择签名版本有关系,那就查查吧。
问题描述(v1和v2)
Android 7.0中引入了APK Signature Scheme v2,v1呢是jar Signature来自JDK
V1:应该是通过ZIP条目进行验证,这样APK 签署后可进行许多修改 - 可以移动甚至重新压缩文件。
V2:验证压缩文件的所有字节,而不是单个 ZIP 条目,因此,在签名后无法再更改(包括 zipalign)。正因如此,现在在编译过程中,我们将压缩、调整和签署合并成一步完成。好处显而易见,更安全而且新的签名可缩短在设备上进行验证的时间(不需要费时地解压缩然后验证),从而加快应用安装速度。
解决方案一
v1和v2的签名使用
只勾选v1签名并不会影响什么,但是在7.0上不会使用更安全的验证方式
只勾选V2签名7.0以下会直接安装完显示未安装,7.0以上则使用了V2的方式验证
同时勾选V1和V2则所有机型都没问题
解决方案二
在app的build.gradle的android标签下加入如下
[java] view plain copy
注意:该文只是例举出有代表性的地方进行讲解,并不是所有的程序代码都上传了。