用Android Studio编写简易闹钟(二)

一、时钟功能

          要将时间显示到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<AlarmData>(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; 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 Button btnAddAlarm;
    private ListView lvAlarmList;
    private static final String KEY_ALARM_LIST = "alarmList";
    private ArrayAdapter<AlarmData> 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中也可以在手机上调试),一下为打包的具体步奏。

  1. 打开AndroidStudio,并且打开想要生成apk文件的项目

  2.  点击工具栏上面的“Builder”

    AndroidStudio生成apk文件

  3.  点击“Builder”之后在下拉菜单里面可以看到“Genarate Singed APK”,点击这个选项!

    AndroidStudio生成apk文件

  4.  之后会要求开发者输入相关的密钥文件和密码

    AndroidStudio生成apk文件

  5.  如果开发者之前已经有了自己的密钥文件的话,此时会自动查询到以前的密钥文件(如果没有自动查询到也可以手动查找),找到之后输入密码就可以了,中途如果出错的话,那就可能是密钥文件配置不正确,需要重新申请一个密钥文件。

  6.  申请一个新的密钥文件,点击上一个图片中“Create new...”即可打开新建密钥文件的对话框,在对话框中输入相应的信息即可。

     

     

     

  7.  之后,生成一个新的密钥文件之后会自动返回选择密钥文件的对话框,此时只需要输入刚才申请的时候使用的密码就可以了,接下来就是选择生成apk文件时的一些信息,可以自己选择,但是一般建议使用默认的设置就可以。


     

  8. 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

 

  1. signingConfigs {  
  2.     debug {  
  3.         v1SigningEnabled true  
  4.         v2SigningEnabled true  
  5.     }  
  6.     release {  
  7.         v1SigningEnabled true  
  8.         v2SigningEnabled true  
  9.     }  
  10. }  

  

注意:该文只是例举出有代表性的地方进行讲解,并不是所有的程序代码都上传了。

 

  • 12
    点赞
  • 122
    收藏
    觉得还不错? 一键收藏
  • 16
    评论
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值