小米便签源码分析——UI包

Posted edgarrrr

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了小米便签源码分析——UI包相关的知识,希望对你有一定的参考价值。

目录

1、AlarmAlertActivity.java

2、AlarmInitReceiver.java

3、AlarmReceiver.java

4、DateTimePicker.java

5、DateTimePickerDialog.java

6、DropdownMenu.java

7、FoldersListAdapter.java

8、NoteEditActivity.java

9、NoteEditText.java

10、NoteItemData.java

11、NotesListActivity.java

12、NotesListAdapter.java

13、NotesListItem.java

14、NotesPreferenceActivity.java


1、AlarmAlertActivity.java

package net.micode.notes.ui;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.DialogInterface.OnDismissListener;
import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.PowerManager;
import android.provider.Settings;
import android.view.Window;
import android.view.WindowManager;

import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.DataUtils;

import java.io.IOException;

public class AlarmAlertActivity extends Activity implements OnClickListener, OnDismissListener 
    private long mNoteId;      //文本在数据库存储中的ID号 
    private String mSnippet;   //闹钟提示时出现的文本片段
    private static final int SNIPPET_PREW_MAX_LEN = 60;
    MediaPlayer mPlayer;

    @Override
    protected void onCreate(Bundle savedInstanceState)     
        super.onCreate(savedInstanceState);
        //Bundle类型的数据与Map类型的数据相似,都是以key-value的形式存储数据的
        //onsaveInstanceState方法是用来保存Activity的状态的
        //能从onCreate的参数savedInsanceState中获得状态数据
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        //界面显示——无标题

        final Window win = getWindow();
        win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);

        if (!isScreenOn()) 
            win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
            		//保持窗体点亮
                    | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
                    //将窗体点亮
                    | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
                    //允许窗体点亮时锁屏
                    | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
        //在手机锁屏后如果到了闹钟提示时间,点亮屏幕

        Intent intent = getIntent();

        try 
            mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1));
            mSnippet = DataUtils.getSnippetById(this.getContentResolver(), mNoteId);
            //根据ID从数据库中获取标签的内容;
            //getContentResolver()是实现数据共享,实例存储。
            mSnippet = mSnippet.length() > SNIPPET_PREW_MAX_LEN ? mSnippet.substring(0,
                    SNIPPET_PREW_MAX_LEN) + getResources().getString(R.string.notelist_string_info)
                    : mSnippet;
            //判断标签片段是否达到符合长度
         catch (IllegalArgumentException e) 
            e.printStackTrace();
            return;
        
        /*
        try
        
        	// 代码区
        
        catch(Exception e)
        
        	// 异常处理
        
                     代码区如果有错误,就会返回所写异常的处理。*/
        mPlayer = new MediaPlayer();
        if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId, Notes.TYPE_NOTE)) 
            showActionDialog();
            //弹出对话框
            playAlarmSound();
            //闹钟提示音激发
         else 
            finish();
            //完成闹钟动作
        
    

    private boolean isScreenOn() 
    	//判断屏幕是否锁屏,调用系统函数判断,最后返回值是布尔类型
        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
        return pm.isScreenOn();
    

    private void playAlarmSound() 
    	//闹钟提示音激发
        Uri url = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM);
        //调用系统的铃声管理URI,得到闹钟提示音
        int silentModeStreams = Settings.System.getInt(getContentResolver(),
                Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0);

        if ((silentModeStreams & (1 << AudioManager.STREAM_ALARM)) != 0) 
            mPlayer.setAudiostreamType(silentModeStreams);
         else 
            mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
        
        try 
            mPlayer.setDataSource(this, url);
            //方法:setDataSource(Context context, Uri uri) 
            //解释:无返回值,设置多媒体数据来源【根据 Uri】
            mPlayer.prepare();
            //准备同步
            mPlayer.setLooping(true);
            //设置是否循环播放
            mPlayer.start();
            //开始播放
         catch (IllegalArgumentException e) 
            // TODO Auto-generated catch block
            e.printStackTrace();
            //e.printStackTrace()函数功能是抛出异常, 还将显示出更深的调用信息
            //System.out.println(e),这个方法打印出异常,并且输出在哪里出现的异常
         catch (SecurityException e) 
            // TODO Auto-generated catch block
            e.printStackTrace();
         catch (IllegalStateException e) 
            // TODO Auto-generated catch block
            e.printStackTrace();
         catch (IOException e) 
            // TODO Auto-generated catch block
            e.printStackTrace();
        
    

    private void showActionDialog() 
        AlertDialog.Builder dialog = new AlertDialog.Builder(this);
        //AlertDialog的构造方法全部是Protected的
        //所以不能直接通过new一个AlertDialog来创建出一个AlertDialog。
        //要创建一个AlertDialog,就要用到AlertDialog.Builder中的create()方法
        //如这里的dialog就是新建了一个AlertDialog
        dialog.setTitle(R.string.app_name);
        //为对话框设置标题
        dialog.setMessage(mSnippet);
        //为对话框设置内容
        dialog.setPositiveButton(R.string.notealert_ok, this);
        //给对话框添加"Yes"按钮
        if (isScreenOn()) 
            dialog.setNegativeButton(R.string.notealert_enter, this);
        //对话框添加"No"按钮
        dialog.show().setOnDismissListener(this);
    

    public void onClick(DialogInterface dialog, int which) 
        switch (which) 
        //用which来选择click后下一步的操作
            case DialogInterface.BUTTON_NEGATIVE:
            	//这是取消操作
                Intent intent = new Intent(this, NoteEditActivity.class);
                //实现两个类间的数据传输
                intent.setAction(Intent.ACTION_VIEW);
                //设置动作属性
                intent.putExtra(Intent.EXTRA_UID, mNoteId);
                //实现key-value对
                //EXTRA_UID为key;mNoteId为键
                startActivity(intent);
                //开始动作
                break;
            default:
            	//这是确定操作
                break;
        
    

    public void onDismiss(DialogInterface dialog) 
    	//忽略
        stopAlarmSound();
        //停止闹钟声音
        finish();
        //完成该动作
    

    private void stopAlarmSound() 
        if (mPlayer != null) 
            mPlayer.stop();
            //停止播放
            mPlayer.release();
            //释放MediaPlayer对象
            mPlayer = null;
        
    

2、AlarmInitReceiver.java

package net.micode.notes.ui;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;

import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;


public class AlarmInitReceiver extends BroadcastReceiver 

    private static final String [] PROJECTION = new String [] 
        NoteColumns.ID,
        NoteColumns.ALERTED_DATE
    ;
    //对数据库的操作,调用标签ID和闹钟时间
    private static final int COLUMN_ID                = 0;
    private static final int COLUMN_ALERTED_DATE      = 1;

    @Override
    public void onReceive(Context context, Intent intent) 
        long currentDate = System.currentTimeMillis();
        //System.currentTimeMillis()产生一个当前的毫秒
        //这个毫秒其实就是自1970年1月1日0时起的毫秒数
        Cursor c = context.getContentResolver().query(Notes.CONTENT_NOTE_URI,
                PROJECTION,
                NoteColumns.ALERTED_DATE + ">? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE,
                new String[]  String.valueOf(currentDate) ,
                //将long变量currentDate转化为字符串
                null);
        //Cursor在这里的作用是通过查找数据库中的标签内容,找到和当前系统时间相等的标签

        if (c != null) 
            if (c.moveToFirst()) 
                do 
                    long alertDate = c.getLong(COLUMN_ALERTED_DATE);
                    Intent sender = new Intent(context, AlarmReceiver.class);
                    sender.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, c.getLong(COLUMN_ID)));
                    PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, sender, 0);
                    AlarmManager alermManager = (AlarmManager) context
                            .getSystemService(Context.ALARM_SERVICE);
                    alermManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent);
                 while (c.moveToNext());
            
            c.close();
        
        //然而通过网上查找资料发现,对于闹钟机制的启动,通常需要上面的几个步骤
        //如新建Intent、PendingIntent以及AlarmManager等
        //这里就是根据数据库里的闹钟时间创建一个闹钟机制
    

3、AlarmReceiver.java

package net.micode.notes.ui;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class AlarmReceiver extends BroadcastReceiver 
    @Override
    public void onReceive(Context context, Intent intent) 
        intent.setClass(context, AlarmAlertActivity.class);  
        //启动AlarmAlertActivity
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        //activity要存在于activity的栈中,而非activity的途径启动activity时必然不存在一个activity的栈
        //所以要新起一个栈装入启动的activity
        context.startActivity(intent);
    

//这是实现alarm这个功能最接近用户层的包,基于上面的两个包,
//作用还需要深究但是对于setClass和addFlags的

4、DateTimePicker.java

package net.micode.notes.ui;
 
import java.text.DateFormatSymbols;
import java.util.Calendar;

import net.micode.notes.R;


import android.content.Context;
import android.text.format.DateFormat;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.NumberPicker;

public class DateTimePicker extends FrameLayout 
	//FrameLayout是布局模板之一
	//所有的子元素全部在屏幕的右上方
    private static final boolean DEFAULT_ENABLE_STATE = true;

    private static final int HOURS_IN_HALF_DAY = 12;
    private static final int HOURS_IN_ALL_DAY = 24;
    private static final int DAYS_IN_ALL_WEEK = 7;
    private static final int DATE_SPINNER_MIN_VAL = 0;
    private static final int DATE_SPINNER_MAX_VAL = DAYS_IN_ALL_WEEK - 1;
    private static final int HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW = 0;
    private static final int HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW = 23;
    private static final int HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW = 1;
    private static final int HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW = 12;
    private static final int MINUT_SPINNER_MIN_VAL = 0;
    private static final int MINUT_SPINNER_MAX_VAL = 59;
    private static final int AMPM_SPINNER_MIN_VAL = 0;
    private static final int AMPM_SPINNER_MAX_VAL = 1;
    //初始化控件
    private final NumberPicker mDateSpinner;
    private final NumberPicker mHourSpinner;
    private final NumberPicker mMinuteSpinner;
    private final NumberPicker mAmPmSpinner;
    //NumberPicker是数字选择器
    //这里定义的四个变量全部是在设置闹钟时需要选择的变量(如日期、时、分、上午或者下午)
    private Calendar mDate;
    //定义了Calendar类型的变量mDate,用于操作时间
    private String[] mDateDisplayValues = new String[DAYS_IN_ALL_WEEK];

    private boolean mIsAm;

    private boolean mIs24HourView;

    private boolean mIsEnabled = DEFAULT_ENABLE_STATE;

    private boolean mInitialising;

    private OnDateTimeChangedListener mOnDateTimeChangedListener;

    private NumberPicker.OnValueChangeListener mOnDateChangedListener = new NumberPicker.OnValueChangeListener() 
        @Override
        public void onValueChange(NumberPicker picker, int oldVal, int newVal) 
            mDate.add(Calendar.DAY_OF_YEAR, newVal - oldVal);
            updateDateControl();
            onDateTimeChanged();
        
    ;//OnValueChangeListener,这是时间改变监听器,这里主要是对日期的监听
    //将现在日期的值传递给mDate;updateDateControl是同步操作

    private NumberPicker.OnValueChangeListener mOnHourChangedListener = new NumberPicker.OnValueChangeListener() 
        //这里是对 小时(Hour) 的监听
    	@Override
        public void onValueChange(NumberPicker picker, int oldVal, int newVal) 
            boolean isDateChanged = false;
            Calendar cal = Calendar.getInstance();
            //声明一个Calendar的变量cal,便于后续的操作
            if (!mIs24HourView) 
                if (!mIsAm && oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY) 
                    cal.setTimeInMillis(mDate.getTimeInMillis());
                    cal.add(Calendar.DAY_OF_YEAR, 1);
                    isDateChanged = true;
                    //这里是对于12小时制时,晚上11点和12点交替时对日期的更改
                 else if (mIsAm && oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) 
                    cal.setTimeInMillis(mDate.getTimeInMillis());
                    cal.add(Calendar.DAY_OF_YEAR, -1);
                    isDateChanged = true;
                 
                //这里是对于12小时制时,凌晨11点和12点交替时对日期的更改
                if (oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY ||
                        oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) 
                    mIsAm = !mIsAm;
                    updateAmPmControl();
                //这里是对于12小时制时,中午11点和12点交替时对AM和PM的更改
             else 
                if (oldVal == HOURS_IN_ALL_DAY - 1 && newVal == 0) 
                    cal.setTimeInMillis(mDate.getTimeInMillis());
                    cal.add(Calendar.DAY_OF_YEAR, 1);
                    isDateChanged = true;
                    //这里是对于24小时制时,晚上11点和12点交替时对日期的更改
                 else if (oldVal == 0 && newVal == HOURS_IN_ALL_DAY - 1) 
                    cal.setTimeInMillis(mDate.getTimeInMillis());
                    cal.add(Calendar.DAY_OF_YEAR, -1);
                    isDateChanged = true;
                
             //这里是对于12小时制时,凌晨11点和12点交替时对日期的更改
            int newHour = mHourSpinner.getValue() % HOURS_IN_HALF_DAY + (mIsAm ? 0 : HOURS_IN_HALF_DAY);
            //通过数字选择器对newHour的赋值
            mDate.set(Calendar.HOUR_OF_DAY, newHour);
            //通过set函数将新的Hour值传给mDate
            onDateTimeChanged();
            if (isDateChanged) 
                setCurrentYear(cal.get(Calendar.YEAR));
                setCurrentMonth(cal.get(Calendar.MONTH));
                setCurrentDay(cal.get(Calendar.DAY_OF_MONTH));
            
        
    ;

    private NumberPicker.OnValueChangeListener mOnMinuteChangedListener = new NumberPicker.OnValueChangeListener() 
        @Override
        //这里是对 分钟(Minute)改变的监听
        public void onValueChange(NumberPicker picker, int oldVal, int newVal) 
            int minValue = mMinuteSpinner.getMinValue();
            int maxValue = mMinuteSpinner.getMaxValue();
            int offset = 0;
            //设置offset,作为小时改变的一个记录数据
            if (oldVal == maxValue && newVal == minValue) 
                offset += 1;
             else if (oldVal == minValue && newVal == maxValue) 
                offset -= 1;
            
            //如果原值为59,新值为0,则offset加1
            //如果原值为0,新值为59,则offset减1
            if (offset != 0) 
                mDate.add(Calendar.HOUR_OF_DAY, offset);
                mHourSpinner.setValue(getCurrentHour());
                updateDateControl();
                int newHour = getCurrentHourOfDay();
                if (newHour >= HOURS_IN_HALF_DAY) 
                    mIsAm = false;
                    updateAmPmControl();
                 else 
                    mIsAm = true;
                    updateAmPmControl();
                
            
            mDate.set(Calendar.MINUTE, newVal);
            onDateTimeChanged();
        
    ;

    private NumberPicker.OnValueChangeListener mOnAmPmChangedListener = new NumberPicker.OnValueChangeListener() 
        //对AM和PM的监听
    	@Override
        public void onValueChange(NumberPicker picker, int oldVal, int newVal) 
            mIsAm = !mIsAm;
            if (mIsAm) 
                mDate.add(Calendar.HOUR_OF_DAY, -HOURS_IN_HALF_DAY);
             else 
                mDate.add(Calendar.HOUR_OF_DAY, HOURS_IN_HALF_DAY);
            
            updateAmPmControl();
            onDateTimeChanged();
        
    ;

    public interface OnDateTimeChangedListener 
        void onDateTimeChanged(DateTimePicker view, int year, int month,
                int dayOfMonth, int hourOfDay, int minute);
    

    public DateTimePicker(Context context) 
        this(context, System.currentTimeMillis());
    //通过对数据库的访问,获取当前的系统时间

    public DateTimePicker(Context context, long date) 
        this(context, date, DateFormat.is24HourFormat(context));
    //上面函数的得到的是一个天文数字(1970至今的秒数),需要DateFormat将其变得有意义

    public DateTimePicker(Context context, long date, boolean is24HourView) 
        super(context);
        //获取系统时间
        mDate = Calendar.getInstance();
        mInitialising = true;
        mIsAm = getCurrentHourOfDay() >= HOURS_IN_HALF_DAY;
        inflate(context, R.layout.datetime_picker, this);
        //如果当前Activity里用到别的layout,比如对话框layout
        //还要设置这个layout上的其他组件的内容,就必须用inflate()方法先将对话框的layout找出来
        //然后再用findViewById()找到它上面的其它组件
        mDateSpinner = (NumberPicker) findViewById(R.id.date);
        mDateSpinner.setMinValue(DATE_SPINNER_MIN_VAL);
        mDateSpinner.setMaxValue(DATE_SPINNER_MAX_VAL);
        mDateSpinner.setOnValueChangedListener(mOnDateChangedListener);

        mHourSpinner = (NumberPicker) findViewById(R.id.hour);
        mHourSpinner.setOnValueChangedListener(mOnHourChangedListener);
        mMinuteSpinner = (NumberPicker) findViewById(R.id.minute);
        mMinuteSpinner.setMinValue(MINUT_SPINNER_MIN_VAL);  
        mMinuteSpinner.setMaxValue(MINUT_SPINNER_MAX_VAL);
        mMinuteSpinner.setOnLongPressUpdateInterval(100);
        mMinuteSpinner.setOnValueChangedListener(mOnMinuteChangedListener);

        String[] stringsForAmPm = new DateFormatSymbols().getAmPmStrings();
        mAmPmSpinner = (NumberPicker) findViewById(R.id.amPm);
        mAmPmSpinner.setMinValue(AMPM_SPINNER_MIN_VAL);
        mAmPmSpinner.setMaxValue(AMPM_SPINNER_MAX_VAL);
        mAmPmSpinner.setDisplayedValues(stringsForAmPm);
        mAmPmSpinner.setOnValueChangedListener(mOnAmPmChangedListener);

        // update controls to initial state
        updateDateControl();
        updateHourControl();
        updateAmPmControl();

        set24HourView(is24HourView);

        // set to current time
        setCurrentDate(date);

        setEnabled(isEnabled());

        // set the content descriptions
        mInitialising = false;
    

    @Override
    public void setEnabled(boolean enabled) 
        if (mIsEnabled == enabled) 
            return;
        
        super.setEnabled(enabled);
        mDateSpinner.setEnabled(enabled);
        mMinuteSpinner.setEnabled(enabled);
        mHourSpinner.setEnabled(enabled);
        mAmPmSpinner.setEnabled(enabled);
        mIsEnabled = enabled;
    
    //存在疑问!!!!!!!!!!!!!setEnabled的作用
    //下面的代码通过原程序的注释已经比较清晰,另外可以通过函数名来判断
    //下面的各函数主要是对上面代码引用到的各函数功能的实现
    @Override
    public boolean isEnabled() 
        return mIsEnabled;
    

    /**
     * Get the current date in millis
     *
     * @return the current date in millis
     */
    public long getCurrentDateInTimeMillis() 
        return mDate.getTimeInMillis();
    //实现函数——得到当前的秒数

    /**
     * Set the current date
     *
     * @param date The current date in millis
     */
    public void setCurrentDate(long date) 
        Calendar cal = Calendar.getInstance();
        cal.setTimeInMillis(date);
        setCurrentDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH),
                cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE));
    //实现函数功能——设置当前的时间,参数是date

    /**
     * Set the current date
     *
     * @param year The current year
     * @param month The current month
     * @param dayOfMonth The current dayOfMonth
     * @param hourOfDay The current hourOfDay
     * @param minute The current minute
     */
    public void setCurrentDate(int year, int month,
            int dayOfMonth, int hourOfDay, int minute) 
        setCurrentYear(year);
        setCurrentMonth(month);
        setCurrentDay(dayOfMonth);
        setCurrentHour(hourOfDay);
        setCurrentMinute(minute);
    //实现函数功能——设置当前的时间,参数是各详细的变量

    /**
     * Get current year
     *
     * @return The current year
     */
    //下面是得到year、month、day等值
    public int getCurrentYear() 
        return mDate.get(Calendar.YEAR);
    

    /**
     * Set current year
     *
     * @param year The current year
     */
    public void setCurrentYear(int year) 
        if (!mInitialising && year == getCurrentYear()) 
            return;
        
        mDate.set(Calendar.YEAR, year);
        updateDateControl();
        onDateTimeChanged();
    

    /**
     * Get current month in the year
     *
     * @return The current month in the year
     */
    public int getCurrentMonth() 
        return mDate.get(Calendar.MONTH);
    

    /**
     * Set current month in the year
     *
     * @param month The month in the year
     */
    public void setCurrentMonth(int month) 
        if (!mInitialising && month == getCurrentMonth()) 
            return;
        
        mDate.set(Calendar.MONTH, month);
        updateDateControl();
        onDateTimeChanged();
    

    /**
     * Get current day of the month
     *
     * @return The day of the month
     */
    public int getCurrentDay() 
        return mDate.get(Calendar.DAY_OF_MONTH);
    

    /**
     * Set current day of the month
     *
     * @param dayOfMonth The day of the month
     */
    public void setCurrentDay(int dayOfMonth) 
        if (!mInitialising && dayOfMonth == getCurrentDay()) 
            return;
        
        mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
        updateDateControl();
        onDateTimeChanged();
    

    /**
     * Get current hour in 24 hour mode, in the range (0~23)
     * @return The current hour in 24 hour mode
     */
    public int getCurrentHourOfDay() 
        return mDate.get(Calendar.HOUR_OF_DAY);
    
 
    private int getCurrentHour() 
        if (mIs24HourView)
            return getCurrentHourOfDay();
         else 
            int hour = getCurrentHourOfDay();
            if (hour > HOURS_IN_HALF_DAY) 
                return hour - HOURS_IN_HALF_DAY;
             else 
                return hour == 0 ? HOURS_IN_HALF_DAY : hour;
            
        
    

    /**
     * Set current hour in 24 hour mode, in the range (0~23)
     *
     * @param hourOfDay
     */
    public void setCurrentHour(int hourOfDay) 
        if (!mInitialising && hourOfDay == getCurrentHourOfDay()) 
            return;
        
        mDate.set(Calendar.HOUR_OF_DAY, hourOfDay);
        if (!mIs24HourView) 
            if (hourOfDay >= HOURS_IN_HALF_DAY) 
                mIsAm = false;
                if (hourOfDay > HOURS_IN_HALF_DAY) 
                    hourOfDay -= HOURS_IN_HALF_DAY;
                
             else 
                mIsAm = true;
                if (hourOfDay == 0) 
                    hourOfDay = HOURS_IN_HALF_DAY;
                
            
            updateAmPmControl();
        
        mHourSpinner.setValue(hourOfDay);
        onDateTimeChanged();
    

    /**
     * Get currentMinute
     *
     * @return The Current Minute
     */
    public int getCurrentMinute() 
        return mDate.get(Calendar.MINUTE);
    

    /**
     * Set current minute
     */
    public void setCurrentMinute(int minute) 
        if (!mInitialising && minute == getCurrentMinute()) 
            return;
        
        mMinuteSpinner.setValue(minute);
        mDate.set(Calendar.MINUTE, minute);
        onDateTimeChanged();
    

    /**
     * @return true if this is in 24 hour view else false.
     */
    public boolean is24HourView () 
        return mIs24HourView;
    

    /**
     * Set whether in 24 hour or AM/PM mode.
     *
     * @param is24HourView True for 24 hour mode. False for AM/PM mode.
     */
    public void set24HourView(boolean is24HourView) 
        if (mIs24HourView == is24HourView) 
            return;
        
        mIs24HourView = is24HourView;
        mAmPmSpinner.setVisibility(is24HourView ? View.GONE : View.VISIBLE);
        int hour = getCurrentHourOfDay();
        updateHourControl();
        setCurrentHour(hour);
        updateAmPmControl();
    

    private void updateDateControl() 
        Calendar cal = Calendar.getInstance();
        cal.setTimeInMillis(mDate.getTimeInMillis());
        cal.add(Calendar.DAY_OF_YEAR, -DAYS_IN_ALL_WEEK / 2 - 1);
        mDateSpinner.setDisplayedValues(null);
        for (int i = 0; i < DAYS_IN_ALL_WEEK; ++i) 
            cal.add(Calendar.DAY_OF_YEAR, 1);
            mDateDisplayValues[i] = (String) DateFormat.format("MM.dd EEEE", cal);
        
        mDateSpinner.setDisplayedValues(mDateDisplayValues);
        mDateSpinner.setValue(DAYS_IN_ALL_WEEK / 2);
        mDateSpinner.invalidate();
    // 对于星期几的算法

    private void updateAmPmControl() 
        if (mIs24HourView) 
            mAmPmSpinner.setVisibility(View.GONE);
         else 
            int index = mIsAm ? Calendar.AM : Calendar.PM;
            mAmPmSpinner.setValue(index);
            mAmPmSpinner.setVisibility(View.VISIBLE);
        // 对于上下午操作的算法
    

    private void updateHourControl() 
        if (mIs24HourView) 
            mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW);
            mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW);
         else 
            mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW);
            mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW);
        // 对与小时的算法
    

    /**
     * Set the callback that indicates the 'Set' button has been pressed.
     * @param callback the callback, if null will do nothing
     */
    public void setOnDateTimeChangedListener(OnDateTimeChangedListener callback) 
        mOnDateTimeChangedListener = callback;
    

    private void onDateTimeChanged() 
        if (mOnDateTimeChangedListener != null) 
            mOnDateTimeChangedListener.onDateTimeChanged(this, getCurrentYear(),
                    getCurrentMonth(), getCurrentDay(), getCurrentHourOfDay(), getCurrentMinute());
          
    

5、DateTimePickerDialog.java

package net.micode.notes.ui;

import java.util.Calendar;

import net.micode.notes.R;
import net.micode.notes.ui.DateTimePicker;
import net.micode.notes.ui.DateTimePicker.OnDateTimeChangedListener;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.text.format.DateFormat;
import android.text.format.DateUtils;

public class DateTimePickerDialog extends AlertDialog implements OnClickListener 

    private Calendar mDate = Calendar.getInstance();
    //创建一个Calendar类型的变量 mDate,方便时间的操作
    private boolean mIs24HourView;
    private OnDateTimeSetListener mOnDateTimeSetListener;
    //声明一个时间日期滚动选择控件 mOnDateTimeSetListener
    private DateTimePicker mDateTimePicker;
    //DateTimePicker控件,控件一般用于让用户可以从日期列表中选择单个值。
    //运行时,单击控件边上的下拉箭头,会显示为两个部分:一个下拉列表,一个用于选择日期的

    public interface OnDateTimeSetListener 
        void OnDateTimeSet(AlertDialog dialog, long date);
    

    public DateTimePickerDialog(Context context, long date) 
    	//对该界面对话框的实例化
        super(context);
        //对数据库的操作
        mDateTimePicker = new DateTimePicker(context);
        setView(mDateTimePicker);
        //添加一个子视图
        mDateTimePicker.setOnDateTimeChangedListener(new OnDateTimeChangedListener() 
            public void onDateTimeChanged(DateTimePicker view, int year, int month,
                    int dayOfMonth, int hourOfDay, int minute) 
                mDate.set(Calendar.YEAR, year);
                mDate.set(Calendar.MONTH, month);
                mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
                mDate.set(Calendar.HOUR_OF_DAY, hourOfDay);
                mDate.set(Calendar.MINUTE, minute);
                //将视图中的各选项设置为系统当前时间
                updateTitle(mDate.getTimeInMillis());
            
        );
        mDate.setTimeInMillis(date);
        //得到系统时间
        mDate.set(Calendar.SECOND, 0);
        //将秒数设置为0
        mDateTimePicker.setCurrentDate(mDate.getTimeInMillis());
        setButton(context.getString(R.string.datetime_dialog_ok), this);
        setButton2(context.getString(R.string.datetime_dialog_cancel), (OnClickListener)null);
        //设置按钮
        set24HourView(DateFormat.is24HourFormat(this.getContext()));
        //时间标准化打印
        updateTitle(mDate.getTimeInMillis());
    

    public void set24HourView(boolean is24HourView) 
        mIs24HourView = is24HourView;
    

    public void setOnDateTimeSetListener(OnDateTimeSetListener callBack) 
        mOnDateTimeSetListener = callBack;
    //将时间日期滚动选择控件实例化

    private void updateTitle(long date) 
        int flag =
            DateUtils.FORMAT_SHOW_YEAR |
            DateUtils.FORMAT_SHOW_DATE |
            DateUtils.FORMAT_SHOW_TIME;
        flag |= mIs24HourView ? DateUtils.FORMAT_24HOUR : DateUtils.FORMAT_24HOUR;
        setTitle(DateUtils.formatDateTime(this.getContext(), date, flag));
    //android开发中常见日期管理工具类(API)——DateUtils:按照上下午显示时间

    public void onClick(DialogInterface arg0, int arg1) 
        if (mOnDateTimeSetListener != null) 
            mOnDateTimeSetListener.OnDateTimeSet(this, mDate.getTimeInMillis());
        
    //第一个参数arg0是接收到点击事件的对话框
    //第二个参数arg1是该对话框上的按钮

6、DropdownMenu.java

package net.micode.notes.ui;

import android.content.Context;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.PopupMenu;
import android.widget.PopupMenu.OnMenuItemClickListener;

import net.micode.notes.R;

public class DropdownMenu 
    private Button mButton;
    private PopupMenu mPopupMenu;
    //声明一个下拉菜单
    private Menu mMenu;

    public DropdownMenu(Context context, Button button, int menuId) 
        mButton = button;
        mButton.setBackgroundResource(R.drawable.dropdown_icon);
        //设置这个view的背景
        mPopupMenu = new PopupMenu(context, mButton);
        mMenu = mPopupMenu.getMenu();
        mPopupMenu.getMenuInflater().inflate(menuId, mMenu);
        //MenuInflater是用来实例化Menu目录下的Menu布局文件
        //根据ID来确认menu的内容选项
        mButton.setOnClickListener(new OnClickListener() 
            public void onClick(View v) 
                mPopupMenu.show();
            
        );
    

    public void setOnDropdownMenuItemClickListener(OnMenuItemClickListener listener) 
        if (mPopupMenu != null) 
            mPopupMenu.setOnMenuItemClickListener(listener);
        //设置菜单的监听
    

    public MenuItem findItem(int id) 
        return mMenu.findItem(id);
    //对于菜单选项的初始化,根据索引搜索菜单需要的选项

    public void setTitle(CharSequence title) 
        mButton.setText(title);
    //布局文件,设置标题

7、FoldersListAdapter.java

package net.micode.notes.ui;

import android.content.Context;
import android.database.Cursor;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.LinearLayout;
import android.widget.TextView;

import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;


public class FoldersListAdapter extends CursorAdapter 
	//CursorAdapter是Cursor和ListView的接口
	//FoldersListAdapter继承了CursorAdapter的类
	//主要作用是便签数据库和用户的交互
	//这里就是用folder(文件夹)的形式展现给用户
    public static final String [] PROJECTION = 
        NoteColumns.ID,
        NoteColumns.SNIPPET
    ;//调用数据库中便签的ID和片段

    public static final int ID_COLUMN   = 0;
    public static final int NAME_COLUMN = 1;

    public FoldersListAdapter(Context context, Cursor c) 
        super(context, c);
        // TODO Auto-generated constructor stub
    //数据库操作

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) 
        //ViewGroup是容器
    	return new FolderListItem(context);
    //创建一个文件夹,对于各文件夹中子标签的初始化

    @Override
    public void bindView(View view, Context context, Cursor cursor) 
        if (view instanceof FolderListItem) 
            String folderName = (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context
                    .getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN);
            ((FolderListItem) view).bind(folderName);
        
    //将各个布局文件绑定起来

    public String getFolderName(Context context, int position) 
        Cursor cursor = (Cursor) getItem(position);
        return (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context
                .getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN);
    //根据数据库中标签的ID得到标签的各项内容

    private class FolderListItem extends LinearLayout 
        private TextView mName;

        public FolderListItem(Context context) 
            super(context);
            //操作数据库
            inflate(context, R.layout.folder_list_item, this);
            //根据布局文件的名字等信息将其找出来
            mName = (TextView) findViewById(R.id.tv_folder_name);
        

        public void bind(String name) 
            mName.setText(name);
        
    


8、NoteEditActivity.java

package net.micode.notes.ui;
 
import android.app.Activity;
import android.app.AlarmManager;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.app.SearchManager;
import android.appwidget.AppWidgetManager;
import android.content.ContentUris;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Paint;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.text.style.BackgroundColorSpan;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.TextNote;
import net.micode.notes.model.WorkingNote;
import net.micode.notes.model.WorkingNote.NoteSettingChangedListener;
import net.micode.notes.tool.DataUtils;
import net.micode.notes.tool.ResourceParser;
import net.micode.notes.tool.ResourceParser.TextAppearanceResources;
import net.micode.notes.ui.DateTimePickerDialog.OnDateTimeSetListener;
import net.micode.notes.ui.NoteEditText.OnTextViewChangeListener;
import net.micode.notes.widget.NoteWidgetProvider_2x;
import net.micode.notes.widget.NoteWidgetProvider_4x;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class NoteEditActivity extends Activity implements OnClickListener,
        NoteSettingChangedListener, OnTextViewChangeListener 
	//该类主要是针对标签的编辑
	//继承了系统内部许多和监听有关的类
    private class HeadViewHolder 
        public TextView tvModified;

        public ImageView ivAlertIcon;

        public TextView tvAlertDate;

        public ImageView ibSetBgColor;
    
    //使用Map实现数据存储
    private static final Map<Integer, Integer> sBgSelectorBtnsMap = new HashMap<Integer, Integer>();
    static 
        sBgSelectorBtnsMap.put(R.id.iv_bg_yellow, ResourceParser.YELLOW);
        sBgSelectorBtnsMap.put(R.id.iv_bg_red, ResourceParser.RED);
        sBgSelectorBtnsMap.put(R.id.iv_bg_blue, ResourceParser.BLUE);
        sBgSelectorBtnsMap.put(R.id.iv_bg_green, ResourceParser.GREEN);
        sBgSelectorBtnsMap.put(R.id.iv_bg_white, ResourceParser.WHITE);
        //put函数是将指定值和指定键相连
    

    private static final Map<Integer, Integer> sBgSelectorSelectionMap = new HashMap<Integer, Integer>();
    static 
        sBgSelectorSelectionMap.put(ResourceParser.YELLOW, R.id.iv_bg_yellow_select);
        sBgSelectorSelectionMap.put(ResourceParser.RED, R.id.iv_bg_red_select);
        sBgSelectorSelectionMap.put(ResourceParser.BLUE, R.id.iv_bg_blue_select);
        sBgSelectorSelectionMap.put(ResourceParser.GREEN, R.id.iv_bg_green_select);
        sBgSelectorSelectionMap.put(ResourceParser.WHITE, R.id.iv_bg_white_select);
        //put函数是将指定值和指定键相连
    

    private static final Map<Integer, Integer> sFontSizeBtnsMap = new HashMap<Integer, Integer>();
    static 
        sFontSizeBtnsMap.put(R.id.ll_font_large, ResourceParser.TEXT_LARGE);
        sFontSizeBtnsMap.put(R.id.ll_font_small, ResourceParser.TEXT_SMALL);
        sFontSizeBtnsMap.put(R.id.ll_font_normal, ResourceParser.TEXT_MEDIUM);
        sFontSizeBtnsMap.put(R.id.ll_font_super, ResourceParser.TEXT_SUPER);
        //put函数是将指定值和指定键相连
    

    private static final Map<Integer, Integer> sFontSelectorSelectionMap = new HashMap<Integer, Integer>();
    static 
        sFontSelectorSelectionMap.put(ResourceParser.TEXT_LARGE, R.id.iv_large_select);
        sFontSelectorSelectionMap.put(ResourceParser.TEXT_SMALL, R.id.iv_small_select);
        sFontSelectorSelectionMap.put(ResourceParser.TEXT_MEDIUM, R.id.iv_medium_select);
        sFontSelectorSelectionMap.put(ResourceParser.TEXT_SUPER, R.id.iv_super_select);
        //put函数是将指定值和指定键相连
    

    private static final String TAG = "NoteEditActivity";

    private HeadViewHolder mNoteHeaderHolder;

    private View mHeadViewPanel;
    //私有化一个界面操作mHeadViewPanel,对表头的操作
    private View mNoteBgColorSelector;
    //私有化一个界面操作mNoteBgColorSelector,对背景颜色的操作
    private View mFontSizeSelector;
    //私有化一个界面操作mFontSizeSelector,对标签字体的操作
    private EditText mNoteEditor;
    //声明编辑控件,对文本操作
    private View mNoteEditorPanel;
    //私有化一个界面操作mNoteEditorPanel,文本编辑的控制板
    //private WorkingNote mWorkingNote;
    public WorkingNote mWorkingNote;
    //对模板WorkingNote的初始化
    private SharedPreferences mSharedPrefs;
    //私有化SharedPreferences的数据存储方式
    //它的本质是基于XML文件存储key-value键值对数据
    private int mFontSizeId;
    //用于操作字体的大小
    private static final String PREFERENCE_FONT_SIZE = "pref_font_size";

    private static final int SHORTCUT_ICON_TITLE_MAX_LEN = 10;

    public static final String TAG_CHECKED = String.valueOf('\\u221A');
    public static final String TAG_UNCHECKED = String.valueOf('\\u25A1');

    private LinearLayout mEditTextList;
    //线性布局
    private String mUserQuery;
    private Pattern mPattern;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        this.setContentView(R.layout.note_edit);
        //对数据库的访问操作
        if (savedInstanceState == null && !initActivityState(getIntent())) 
            finish();
            return;
        
        initResources();
    

    /**
     * Current activity may be killed when the memory is low. Once it is killed, for another time
     * user load this activity, we should restore the former state
     */
    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) 
        super.onRestoreInstanceState(savedInstanceState);
        if (savedInstanceState != null && savedInstanceState.containsKey(Intent.EXTRA_UID)) 
            Intent intent = new Intent(Intent.ACTION_VIEW);
            intent.putExtra(Intent.EXTRA_UID, savedInstanceState.getLong(Intent.EXTRA_UID));
            if (!initActivityState(intent)) 
                finish();
                return;
            
            Log.d(TAG, "Restoring from killed activity");
        //为防止内存不足时程序的终止,在这里有一个保存现场的函数
    

    private boolean initActivityState(Intent intent) 
        /**
         * If the user specified the @link Intent#ACTION_VIEW but not provided with id,
         * then jump to the NotesListActivity
         */
        mWorkingNote = null;
        if (TextUtils.equals(Intent.ACTION_VIEW, intent.getAction())) 
            long noteId = intent.getLongExtra(Intent.EXTRA_UID, 0);
            mUserQuery = "";
            //如果用户实例化标签时,系统并未给出标签ID
            /**
             * Starting from the searched result
             */
            //根据键值查找ID
            if (intent.hasExtra(SearchManager.EXTRA_DATA_KEY)) 
                noteId = Long.parseLong(intent.getStringExtra(SearchManager.EXTRA_DATA_KEY));
                mUserQuery = intent.getStringExtra(SearchManager.USER_QUERY);
            
            //如果ID在数据库中未找到
            if (!DataUtils.visibleInNoteDatabase(getContentResolver(), noteId, Notes.TYPE_NOTE)) 
                Intent jump = new Intent(this, NotesListActivity.class);
                startActivity(jump);
                //程序将跳转到上面声明的intent——jump
                showToast(R.string.error_note_not_exist);
                finish();
                return false;
             
            //ID在数据库中找到
            else 
                mWorkingNote = WorkingNote.load(this, noteId);
                if (mWorkingNote == null) 
                    Log.e(TAG, "load note failed with note id" + noteId);
                    //打印出红色的错误信息
                    finish();
                    return false;
                
            
            //setSoftInputMode——软键盘输入模式
            getWindow().setSoftInputMode(
                    WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN
                            | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
         else if(TextUtils.equals(Intent.ACTION_INSERT_OR_EDIT, intent.getAction())) 
            // intent.getAction()
        	// 大多用于broadcast发送广播时给机制(intent)设置一个action,就是一个字符串 
        	// 用户可以通过receive(接受)intent,通过 getAction得到的字符串,来决定做什么
            long folderId = intent.getLongExtra(Notes.INTENT_EXTRA_FOLDER_ID, 0);
            int widgetId = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_ID,
                    AppWidgetManager.INVALID_APPWIDGET_ID);
            int widgetType = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_TYPE,
                    Notes.TYPE_WIDGET_INVALIDE);
            int bgResId = intent.getIntExtra(Notes.INTENT_EXTRA_BACKGROUND_ID,
                    ResourceParser.getDefaultBgId(this));
            // intent.getInt(Long、String)Extra是对各变量的语法分析
            // Parse call-record note
            String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
            long callDate = intent.getLongExtra(Notes.INTENT_EXTRA_CALL_DATE, 0);
            if (callDate != 0 && phoneNumber != null) 
                if (TextUtils.isEmpty(phoneNumber)) 
                    Log.w(TAG, "The call record number is null");
                
                long noteId = 0;
                if ((noteId = DataUtils.getNoteIdByPhoneNumberAndCallDate(getContentResolver(),
                        phoneNumber, callDate)) > 0) 
                    mWorkingNote = WorkingNote.load(this, noteId);
                    if (mWorkingNote == null) 
                        Log.e(TAG, "load call note failed with note id" + noteId);
                        finish();
                        return false;
                    
                    //将电话号码与手机的号码簿相关
                 else 
                    mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId,
                            widgetType, bgResId);
                    mWorkingNote.convertToCallNote(phoneNumber, callDate);
                    //
                
             else 
                mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId, widgetType,
                        bgResId);
            //创建一个新的WorkingNote

            getWindow().setSoftInputMode(
                    WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
                            | WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
         else 
            Log.e(TAG, "Intent not specified action, should not support");
            finish();
            return false;
        
        mWorkingNote.setOnSettingStatusChangedListener(this);
        return true;
    

    @Override
    protected void onResume() 
        super.onResume();
        initNoteScreen();
    

    private void initNoteScreen() 
    	//对界面的初始化操作
        mNoteEditor.setTextAppearance(this, TextAppearanceResources
                .getTexAppearanceResource(mFontSizeId));
        //设置外观
        if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) 
            switchToListMode(mWorkingNote.getContent());
         else 
            mNoteEditor.setText(getHighlightQueryResult(mWorkingNote.getContent(), mUserQuery));
            mNoteEditor.setSelection(mNoteEditor.getText().length());
        
        for (Integer id : sBgSelectorSelectionMap.keySet()) 
            findViewById(sBgSelectorSelectionMap.get(id)).setVisibility(View.GONE);
        
        mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId());
        mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId());

        mNoteHeaderHolder.tvModified.setText(DateUtils.formatDateTime(this,
                mWorkingNote.getModifiedDate(), DateUtils.FORMAT_SHOW_DATE
                        | DateUtils.FORMAT_NUMERIC_DATE | DateUtils.FORMAT_SHOW_TIME
                        | DateUtils.FORMAT_SHOW_YEAR));

        /**
         * TODO: Add the menu for setting alert. Currently disable it because the DateTimePicker
         * is not ready
         */
        showAlertHeader();
    
    //设置闹钟的显示
    private void showAlertHeader() 
        if (mWorkingNote.hasClockAlert()) 
            long time = System.currentTimeMillis();
            if (time > mWorkingNote.getAlertDate()) 
                mNoteHeaderHolder.tvAlertDate.setText(R.string.note_alert_expired);
             
            //如果系统时间大于了闹钟设置的时间,那么闹钟失效
            else 
                mNoteHeaderHolder.tvAlertDate.setText(DateUtils.getRelativeTimeSpanString(
                        mWorkingNote.getAlertDate(), time, DateUtils.MINUTE_IN_MILLIS));
            
            mNoteHeaderHolder.tvAlertDate.setVisibility(View.VISIBLE);
            mNoteHeaderHolder.ivAlertIcon.setVisibility(View.VISIBLE);
            //显示闹钟开启的图标
         else 
            mNoteHeaderHolder.tvAlertDate.setVisibility(View.GONE);
            mNoteHeaderHolder.ivAlertIcon.setVisibility(View.GONE);
        ;
    

    @Override
    protected void onNewIntent(Intent intent) 
        super.onNewIntent(intent);
        initActivityState(intent);
    

    @Override
    protected void onSaveInstanceState(Bundle outState) 
        super.onSaveInstanceState(outState);
        /**
         * For new note without note id, we should firstly save it to
         * generate a id. If the editing note is not worth saving, there
         * is no id which is equivalent to create new note
         */
        if (!mWorkingNote.existInDatabase()) 
            saveNote();
        
        //在创建一个新的标签时,先在数据库中匹配
        //如果不存在,那么先在数据库中存储
        outState.putLong(Intent.EXTRA_UID, mWorkingNote.getNoteId());
        Log.d(TAG, "Save working note id: " + mWorkingNote.getNoteId() + " onSaveInstanceState");
    

    @Override
    //MotionEvent是对屏幕触控的传递机制
    public boolean dispatchTouchEvent(MotionEvent ev) 
        if (mNoteBgColorSelector.getVisibility() == View.VISIBLE
                && !inRangeOfView(mNoteBgColorSelector, ev)) 
            mNoteBgColorSelector.setVisibility(View.GONE);
            return true;
        //颜色选择器在屏幕上可见

        if (mFontSizeSelector.getVisibility() == View.VISIBLE
                && !inRangeOfView(mFontSizeSelector, ev)) 
            mFontSizeSelector.setVisibility(View.GONE);
            return true;
        //字体大小选择器在屏幕上可见
        return super.dispatchTouchEvent(ev);
    
    //对屏幕触控的坐标进行操作
    private boolean inRangeOfView(View view, MotionEvent ev) 
        int []location = new int[2];
        view.getLocationOnScreen(location);
        int x = location[0];
        int y = location[1];
        if (ev.getX() < x
                || ev.getX() > (x + view.getWidth())
                || ev.getY() < y
                || ev.getY() > (y + view.getHeight()))
        //如果触控的位置超出了给定的范围,返回false
        
                    return false;
                
        return true;
    

    private void initResources() 
        mHeadViewPanel = findViewById(R.id.note_title);
        mNoteHeaderHolder = new HeadViewHolder();
        mNoteHeaderHolder.tvModified = (TextView) findViewById(R.id.tv_modified_date);
        mNoteHeaderHolder.ivAlertIcon = (ImageView) findViewById(R.id.iv_alert_icon);
        mNoteHeaderHolder.tvAlertDate = (TextView) findViewById(R.id.tv_alert_date);
        mNoteHeaderHolder.ibSetBgColor = (ImageView) findViewById(R.id.btn_set_bg_color);
        mNoteHeaderHolder.ibSetBgColor.setOnClickListener(this);
        mNoteEditor = (EditText) findViewById(R.id.note_edit_view);
        mNoteEditorPanel = findViewById(R.id.sv_note_edit);
        mNoteBgColorSelector = findViewById(R.id.note_bg_color_selector);
        for (int id : sBgSelectorBtnsMap.keySet()) 
            ImageView iv = (ImageView) findViewById(id);
            iv.setOnClickListener(this);
        //对标签各项属性内容的初始化

        mFontSizeSelector = findViewById(R.id.font_size_selector);
        for (int id : sFontSizeBtnsMap.keySet()) 
            View view = findViewById(id);
            view.setOnClickListener(this);
        ;//对字体大小的选择
        mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
        mFontSizeId = mSharedPrefs.getInt(PREFERENCE_FONT_SIZE, ResourceParser.BG_DEFAULT_FONT_SIZE);
        /**
         * HACKME: Fix bug of store the resource id in shared preference.
         * The id may larger than the length of resources, in this case,
         * return the @link ResourceParser#BG_DEFAULT_FONT_SIZE
         */
        if(mFontSizeId >= TextAppearanceResources.getResourcesSize()) 
            mFontSizeId = ResourceParser.BG_DEFAULT_FONT_SIZE;
        
        mEditTextList = (LinearLayout) findViewById(R.id.note_edit_list);
    

    @Override
    protected void onPause() 
        super.onPause();
        if(saveNote()) 
            Log.d(TAG, "Note data was saved with length:" + mWorkingNote.getContent().length());
        
        clearSettingState();
    
    //和桌面小工具的同步
    private void updateWidget() 
        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
        if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_2X) 
            intent.setClass(this, NoteWidgetProvider_2x.class);
         else if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_4X) 
            intent.setClass(this, NoteWidgetProvider_4x.class);
         else 
            Log.e(TAG, "Unspported widget type");
            return;
        

        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] 
            mWorkingNote.getWidgetId()
        );

        sendBroadcast(intent);
        setResult(RESULT_OK, intent);
    

    public void onClick(View v) 
        int id = v.getId();
        if (id == R.id.btn_set_bg_color) 
            mNoteBgColorSelector.setVisibility(View.VISIBLE);
            findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(
                    -                    View.VISIBLE);
         else if (sBgSelectorBtnsMap.containsKey(id)) 
            findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(
                    View.GONE);
            mWorkingNote.setBgColorId(sBgSelectorBtnsMap.get(id));
            mNoteBgColorSelector.setVisibility(View.GONE);
         else if (sFontSizeBtnsMap.containsKey(id)) 
            findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.GONE);
            mFontSizeId = sFontSizeBtnsMap.get(id);
            mSharedPrefs.edit().putInt(PREFERENCE_FONT_SIZE, mFontSizeId).commit();
            findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.VISIBLE);
            if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) 
                getWorkingText();
                switchToListMode(mWorkingNote.getContent());
             else 
                mNoteEditor.setTextAppearance(this,
                        TextAppearanceResources.getTexAppearanceResource(mFontSizeId));
            
            mFontSizeSelector.setVisibility(View.GONE);
        
    //************************存在问题

    @Override
    public void onBackPressed() 
        if(clearSettingState()) 
            return;
        

        saveNote();
        super.onBackPressed();
    

    private boolean clearSettingState() 
        if (mNoteBgColorSelector.getVisibility() == View.VISIBLE) 
            mNoteBgColorSelector.setVisibility(View.GONE);
            return true;
         else if (mFontSizeSelector.getVisibility() == View.VISIBLE) 
            mFontSizeSelector.setVisibility(View.GONE);
            return true;
        
        return false;
    

    public void onBackgroundColorChanged() 
        findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(
                View.VISIBLE);
        mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId());
        mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId());
    

    @Override
    //对选择菜单的准备
    public boolean onPrepareOptionsMenu(Menu menu) 
        if (isFinishing()) 
            return true;
        
        clearSettingState();
        menu.clear();
        if (mWorkingNote.getFolderId() == Notes.ID_CALL_RECORD_FOLDER) 
            getMenuInflater().inflate(R.menu.call_note_edit, menu);
            // MenuInflater是用来实例化Menu目录下的Menu布局文件的
         else 
            getMenuInflater().inflate(R.menu.note_edit, menu);
        
        if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) 
            menu.findItem(R.id.menu_list_mode).setTitle(R.string.menu_normal_mode);
         else 
            menu.findItem(R.id.menu_list_mode).setTitle(R.string.menu_list_mode);
        
        if (mWorkingNote.hasClockAlert()) 
            menu.findItem(R.id.menu_alert).setVisible(false);
         else 
            menu.findItem(R.id.menu_delete_remind).setVisible(false);
        
        return true;
    

    @Override
    /*
     * 函数功能:动态改变菜单选项内容
     * 函数实现:如下注释
     */
    public boolean onOptionsItemSelected(MenuItem item) 
        switch (item.getItemId()) 
        //根据菜单的id来编剧相关项目
            case R.id.menu_new_note:
            	//创建一个新的便签
                createNewNote();
                break;
            case R.id.menu_delete:
            	//删除便签
                AlertDialog.Builder builder = new AlertDialog.Builder(this);
                //创建关于删除操作的对话框
                builder.setTitle(getString(R.string.alert_title_delete));
                // 设置标签的标题为alert_title_delete
                builder.setIcon(android.R.drawable.ic_dialog_alert);
                //设置对话框图标
                builder.setMessage(getString(R.string.alert_message_delete_note));
                //设置对话框内容
                builder.setPositiveButton(android.R

便签项目需求分析与建议-NABCD模型

我们吃饭要排队的项目为便签APP,以下是项目需求分析与建议的NABCD模型:

N(Need需求):

我们的APP面向广大群体,适合所有人使用。现在越来越少的人使用纸质材料来记录。这款软件正是满足用户记录的需求,你可以在这里记录下所有你想记录的东西,不管是某个时刻的心情、灵感,还是看到喜欢的文字、图片,随手记录下来,简单又方便。

A(Approach做法):

我们整个项目一共三个开发人员,自学能力强的人员负责项目的一些高难度的突破性人物,擅长使用模板框架的人员负责界面的开发美化,还有了解数据传输的人员进行后台数据的处理工作。同时,我们三个人对完成项目有着充分的激情和信心,包括对产品的推广的我们都有能力完成。

B(Benefit好处):

我们的产品页面设计简洁,安装简单,操作便捷,能够更好的记录生活,用户通过使用我们的产品,生活会更加有条理,多姿多彩,养成记录的习惯,爱上记录和表达。过段时间用户在回头看看自己曾经的便签,会有一种别样的感觉。

C(Competitors竞争):

与我们的产品相似的的竞争者有很多。并且都很成熟,好多都还能支持语音、视频记事。有些还可以直接与其他软件关联,在其他软件看到的内容可以直接同步到便签APP中。虽然我们的竞争者有很多,但我们相信通过仔细分析用户的需求,我们能设计出最适合用户的产品,给用户最贴心的体验。

D(Delivery交付):

项目完成之后,将初代产品进行内部的使用测试,通过解决测试反馈的问题,完善产品。申请团队专利,可以先在朋友同学之间使用,相互推荐,同时也可以在网上进行相应的宣传。

如果产品的使用反响很好的话,再进行下一步的宣传。

以上是关于小米便签源码分析——UI包的主要内容,如果未能解决你的问题,请参考以下文章

Android 程序目录及UI的简介

Android源码深入源码分析UI的绘制流程 附:Android源码解析资料分享

Android源码深入源码分析UI的绘制流程 附:Android源码解析资料分享

Android源码深入源码分析UI的绘制流程 附:Android源码解析资料分享

在Win10以及SDK为33的环境下——小米便签项目的搭建

[Android源码分析] - 异步通信Handler机制