文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2023-10-06 19:28

关注

目录

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;        }                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;    }        public long getCurrentDateInTimeMillis() {        return mDate.getTimeInMillis();    }//实现函数——得到当前的秒数        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        public void setCurrentDate(int year, int month,            int dayOfMonth, int hourOfDay, int minute) {        setCurrentYear(year);        setCurrentMonth(month);        setCurrentDay(dayOfMonth);        setCurrentHour(hourOfDay);        setCurrentMinute(minute);    }//实现函数功能——设置当前的时间,参数是各详细的变量        //下面是得到year、month、day等值    public int getCurrentYear() {        return mDate.get(Calendar.YEAR);    }        public void setCurrentYear(int year) {        if (!mInitialising && year == getCurrentYear()) {            return;        }        mDate.set(Calendar.YEAR, year);        updateDateControl();        onDateTimeChanged();    }        public int getCurrentMonth() {        return mDate.get(Calendar.MONTH);    }        public void setCurrentMonth(int month) {        if (!mInitialising && month == getCurrentMonth()) {            return;        }        mDate.set(Calendar.MONTH, month);        updateDateControl();        onDateTimeChanged();    }        public int getCurrentDay() {        return mDate.get(Calendar.DAY_OF_MONTH);    }        public void setCurrentDay(int dayOfMonth) {        if (!mInitialising && dayOfMonth == getCurrentDay()) {            return;        }        mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);        updateDateControl();        onDateTimeChanged();    }        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;            }        }    }        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();    }        public int getCurrentMinute() {        return mDate.get(Calendar.MINUTE);    }        public void setCurrentMinute(int minute) {        if (!mInitialising && minute == getCurrentMinute()) {            return;        }        mMinuteSpinner.setValue(minute);        mDate.set(Calendar.MINUTE, minute);        onDateTimeChanged();    }        public boolean is24HourView () {        return mIs24HourView;    }        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);        }// 对与小时的算法    }        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 sBgSelectorBtnsMap = new HashMap();    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 sBgSelectorSelectionMap = new HashMap();    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 sFontSizeBtnsMap = new HashMap();    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 sFontSelectorSelectionMap = new HashMap();    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();    }        @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) {                mWorkingNote = null;        if (TextUtils.equals(Intent.ACTION_VIEW, intent.getAction())) {            long noteId = intent.getLongExtra(Intent.EXTRA_UID, 0);            mUserQuery = "";            //如果用户实例化标签时,系统并未给出标签ID                        //根据键值查找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));                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);                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);                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);        }    }/    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.string.ok,                        new DialogInterface.OnClickListener() {                //建立按键监听器public void onClick(DialogInterface dialog, int which) {//点击所触发事件    deleteCurrentNote();    //  删除单签便签    finish();}                        });                //添加“YES”按钮                builder.setNegativeButton(android.R.string.cancel, null);                //添加“NO”的按钮                builder.show();                //显示对话框                break;            case R.id.menu_font_size:            //字体大小的编辑                mFontSizeSelector.setVisibility(View.VISIBLE);            // 将字体选择器置为可见                findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.VISIBLE);            // 通过id找到相应的大小                break;            case R.id.menu_list_mode:            //选择列表模式                mWorkingNote.setCheckListMode(mWorkingNote.getCheckListMode() == 0 ?                        TextNote.MODE_CHECK_LIST : 0);                break;            case R.id.menu_share:            //菜单共享                getWorkingText();                sendTo(this, mWorkingNote.getContent());                // 用sendto函数将运行文本发送到遍历的本文内                break;            case R.id.menu_send_to_desktop:            //发送到桌面                sendToDesktop();                break;            case R.id.menu_alert:            //创建提醒器                setReminder();                break;            case R.id.menu_delete_remind:            //删除日期提醒                mWorkingNote.setAlertDate(0, false);                break;            default:                break;        }        return true;    }        private void setReminder() {        DateTimePickerDialog d = new DateTimePickerDialog(this, System.currentTimeMillis());        // 建立修改时间日期的对话框        d.setOnDateTimeSetListener(new OnDateTimeSetListener() {            public void OnDateTimeSet(AlertDialog dialog, long date) {                mWorkingNote.setAlertDate(date, true);                //选择提醒的日期            }        });         //建立时间日期的监听器        d.show();        //显示对话框    }            private void sendTo(Context context, String info) {        Intent intent = new Intent(Intent.ACTION_SEND);        //建立intent链接选项        intent.putExtra(Intent.EXTRA_TEXT, info);        //将需要传递的便签信息放入text文件中        intent.setType("text/plain");        //编辑连接器的类型        context.startActivity(intent);        //在acti中进行链接    }        private void createNewNote() {        // Firstly, save current editing notes    //保存当前便签        saveNote();        // For safety, start a new NoteEditActivity        finish();        Intent intent = new Intent(this, NoteEditActivity.class);        //设置链接器        intent.setAction(Intent.ACTION_INSERT_OR_EDIT);        //该活动定义为创建或编辑        intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mWorkingNote.getFolderId());        //将运行便签的id添加到INTENT_EXTRA_FOLDER_ID标记中        startActivity(intent);        //开始activity并链接    }        private void deleteCurrentNote() {        if (mWorkingNote.existInDatabase()) {        //假如当前运行的便签内存有数据            HashSet ids = new HashSet();            long id = mWorkingNote.getNoteId();            if (id != Notes.ID_ROOT_FOLDER) {                ids.add(id);            //如果不是头文件夹建立一个hash表把便签id存起来            } else {                Log.d(TAG, "Wrong note id, should not happen");                //否则报错            }            if (!isSyncMode()) {            //在非同步模式情况下            //删除操作                if (!DataUtils.batchDeleteNotes(getContentResolver(), ids)) {                    Log.e(TAG, "Delete Note error");                }            } else {             //同步模式            //移动至垃圾文件夹的操作                if (!DataUtils.batchMoveToFolder(getContentResolver(), ids, Notes.ID_TRASH_FOLER)) {                    Log.e(TAG, "Move notes to trash folder error, should not happens");                }            }        }        mWorkingNote.markDeleted(true);        //将这些标签的删除标记置为true    }        private boolean isSyncMode() {        return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0;    }        public void onClockAlertChanged(long date, boolean set) {                if (!mWorkingNote.existInDatabase()) {        //首先保存已有的便签            saveNote();        }        if (mWorkingNote.getNoteId() > 0) {            Intent intent = new Intent(this, AlarmReceiver.class);            intent.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mWorkingNote.getNoteId()));            //若有有运行的便签就是建立一个链接器将标签id都存在uri中            PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);            AlarmManager alarmManager = ((AlarmManager) getSystemService(ALARM_SERVICE));            //设置提醒管理器            showAlertHeader();            if(!set) {                alarmManager.cancel(pendingIntent);            } else {                alarmManager.set(AlarmManager.RTC_WAKEUP, date, pendingIntent);            }            //如果用户设置了时间,就通过提醒管理器设置一个监听事项        } else {                     //没有运行的便签就报错            Log.e(TAG, "Clock alert setting error");            showToast(R.string.error_note_empty_for_clock);        }    }        public void onWidgetChanged() {        updateWidget();//更新Widget    }        public void onEditTextDelete(int index, String text) {        int childCount = mEditTextList.getChildCount();        if (childCount == 1) {            return;        }        //没有编辑框的话直接返回        for (int i = index + 1; i < childCount; i++) {            ((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text))                    .setIndex(i - 1);            //通过id把编辑框存在便签编辑框中        }        mEditTextList.removeViewAt(index);        //删除特定位置的视图        NoteEditText edit = null;        if(index == 0) {            edit = (NoteEditText) mEditTextList.getChildAt(0).findViewById(                    R.id.et_edit_text);        } else {            edit = (NoteEditText) mEditTextList.getChildAt(index - 1).findViewById(                    R.id.et_edit_text);        }        //通过id把编辑框存在空的NoteEditText中        int length = edit.length();        edit.append(text);        edit.requestFocus();//请求优先完成该此 编辑        edit.setSelection(length);//定位到length位置处的条目    }        public void onEditTextEnter(int index, String text) {                if(index > mEditTextList.getChildCount()) {            Log.e(TAG, "Index out of mEditTextList boundrary, should not happen");            //越界把偶偶        }        View view = getListItem(text, index);        mEditTextList.addView(view, index);        //建立一个新的视图并添加到编辑文本框内        NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text);        edit.requestFocus();//请求优先操作        edit.setSelection(0);//定位到起始位置        for (int i = index + 1; i < mEditTextList.getChildCount(); i++) {            ((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text))                    .setIndex(i);            //遍历子文本框并设置对应对下标        }    }        private void switchToListMode(String text) {        mEditTextList.removeAllViews();        String[] items = text.split("\n");        int index = 0;        //清空所有视图,初始化下标        for (String item : items) {            if(!TextUtils.isEmpty(item)) {                mEditTextList.addView(getListItem(item, index));                index++;                //遍历所有文本单元并添加到文本框中            }        }        mEditTextList.addView(getListItem("", index));        mEditTextList.getChildAt(index).findViewById(R.id.et_edit_text).requestFocus();        //优先请求此操作        mNoteEditor.setVisibility(View.GONE);        //便签编辑器不可见        mEditTextList.setVisibility(View.VISIBLE);        //将文本编辑框置为可见    }        private Spannable getHighlightQueryResult(String fullText, String userQuery) {        SpannableString spannable = new SpannableString(fullText == null ? "" : fullText);        //新建一个效果选项        if (!TextUtils.isEmpty(userQuery)) {            mPattern = Pattern.compile(userQuery);            //将用户的询问进行解析            Matcher m = mPattern.matcher(fullText);            //建立一个状态机检查Pattern并进行匹配            int start = 0;            while (m.find(start)) {                spannable.setSpan(                        new BackgroundColorSpan(this.getResources().getColor(    R.color.user_query_highlight)), m.start(), m.end(),                        Spannable.SPAN_INCLUSIVE_EXCLUSIVE);                //设置背景颜色                start = m.end();                //跟新起始位置            }        }        return spannable;    }        private View getListItem(String item, int index) {        View view = LayoutInflater.from(this).inflate(R.layout.note_edit_list_item, null);        //创建一个视图        final NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text);        edit.setTextAppearance(this, TextAppearanceResources.getTexAppearanceResource(mFontSizeId));        //创建一个文本编辑框并设置可见性        CheckBox cb = ((CheckBox) view.findViewById(R.id.cb_edit_item));        cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {                if (isChecked) {                    edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);                } else {                    edit.setPaintFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG);                }            }        });        //建立一个打钩框并设置监听器        if (item.startsWith(TAG_CHECKED)) {        //选择勾选            cb.setChecked(true);            edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);            item = item.substring(TAG_CHECKED.length(), item.length()).trim();        } else if (item.startsWith(TAG_UNCHECKED)) {        //选择不勾选            cb.setChecked(false);            edit.setPaintFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG);            item = item.substring(TAG_UNCHECKED.length(), item.length()).trim();        }        edit.setOnTextViewChangeListener(this);        edit.setIndex(index);        edit.setText(getHighlightQueryResult(item, mUserQuery));        //运行编辑框的监听器对该行为作出反应,并设置下标及文本内容        return view;    }        public void onTextChange(int index, boolean hasText) {        if (index >= mEditTextList.getChildCount()) {            Log.e(TAG, "Wrong index, should not happen");            return;            //越界报错        }        if(hasText) {            mEditTextList.getChildAt(index).findViewById(R.id.cb_edit_item).setVisibility(View.VISIBLE);        } else {            mEditTextList.getChildAt(index).findViewById(R.id.cb_edit_item).setVisibility(View.GONE);        }        //如果内容不为空则将其子编辑框可见性置为可见,否则不可见    }        public void onCheckListModeChanged(int oldMode, int newMode) {        if (newMode == TextNote.MODE_CHECK_LIST) {            switchToListMode(mNoteEditor.getText().toString());            //检查模式切换到列表模式        } else {            if (!getWorkingText()) {                mWorkingNote.setWorkingText(mWorkingNote.getContent().replace(TAG_UNCHECKED + " ",                        ""));            }            //若是获取到文本就改变其检查标记            mNoteEditor.setText(getHighlightQueryResult(mWorkingNote.getContent(), mUserQuery));            mEditTextList.setVisibility(View.GONE);            mNoteEditor.setVisibility(View.VISIBLE);            //修改文本编辑器的内容和可见性        }    }        private boolean getWorkingText() {        boolean hasChecked = false;        //初始化check标记        if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {        // 若模式为CHECK_LIST            StringBuilder sb = new StringBuilder();            //创建可变字符串            for (int i = 0; i < mEditTextList.getChildCount(); i++) {                View view = mEditTextList.getChildAt(i);                //遍历所有子编辑框的视图                 NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text);                if (!TextUtils.isEmpty(edit.getText())) {                //若文本不为空                    if (((CheckBox) view.findViewById(R.id.cb_edit_item)).isChecked()) {                    //该选项框已打钩                        sb.append(TAG_CHECKED).append(" ").append(edit.getText()).append("\n");                        hasChecked = true;                        //扩展字符串为已打钩并把标记置true                    } else {                        sb.append(TAG_UNCHECKED).append(" ").append(edit.getText()).append("\n");                        //扩展字符串添加未打钩                    }                }            }            mWorkingNote.setWorkingText(sb.toString());            //利用编辑好的字符串设置运行便签的内容        } else {            mWorkingNote.setWorkingText(mNoteEditor.getText().toString());            // 若不是该模式直接用编辑器中的内容设置运行中标签的内容        }        return hasChecked;    }        private boolean saveNote() {        getWorkingText();        boolean saved = mWorkingNote.saveNote();        //运行 getWorkingText()之后保存        if (saved) {                    //如英文注释所说链接RESULT_OK是为了识别保存的2种情况,一是创建后保存,二是修改后保存            setResult(RESULT_OK);        }        return saved;    }        private void sendToDesktop() {                if (!mWorkingNote.existInDatabase()) {            saveNote();            //若不存在数据也就是新的标签就保存起来先        }        if (mWorkingNote.getNoteId() > 0) {        //若是有内容            Intent sender = new Intent();            Intent shortcutIntent = new Intent(this, NoteEditActivity.class);            //建立发送到桌面的连接器            shortcutIntent.setAction(Intent.ACTION_VIEW);            //链接为一个视图            shortcutIntent.putExtra(Intent.EXTRA_UID, mWorkingNote.getNoteId());            sender.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);            sender.putExtra(Intent.EXTRA_SHORTCUT_NAME,                    makeShortcutIconTitle(mWorkingNote.getContent()));            sender.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,                    Intent.ShortcutIconResource.fromContext(this, R.drawable.icon_app));            sender.putExtra("duplicate", true);            //将便签的相关信息都添加到要发送的文件里            sender.setAction("com.android.launcher.action.INSTALL_SHORTCUT");            //设置sneder的行为是发送            showToast(R.string.info_note_enter_desktop);            sendBroadcast(sender);            //显示到桌面        } else {                        Log.e(TAG, "Send to desktop error");            showToast(R.string.error_note_empty_for_send_to_desktop);            //空便签直接报错        }    }        private String makeShortcutIconTitle(String content) {        content = content.replace(TAG_CHECKED, "");        content = content.replace(TAG_UNCHECKED, "");        return content.length() > SHORTCUT_ICON_TITLE_MAX_LEN ? content.substring(0,                SHORTCUT_ICON_TITLE_MAX_LEN) : content;        //直接设置为content中的内容并返回,有勾选和未勾选2种    }         private void showToast(int resId) {        showToast(resId, Toast.LENGTH_SHORT);    }        private void showToast(int resId, int duration) {        Toast.makeText(this, resId, duration).show();    }}

9、NoteEditText.java

package net.micode.notes.ui;import android.content.Context;import android.graphics.Rect;import android.text.Layout;import android.text.Selection;import android.text.Spanned;import android.text.TextUtils;import android.text.style.URLSpan;import android.util.AttributeSet;import android.util.Log;import android.view.ContextMenu;import android.view.KeyEvent;import android.view.MenuItem;import android.view.MenuItem.OnMenuItemClickListener;import android.view.MotionEvent;import android.widget.EditText;import net.micode.notes.R;import java.util.HashMap;import java.util.Map;//继承edittext,设置便签设置文本框public class NoteEditText extends EditText {      private static final String TAG = "NoteEditText";    private int mIndex;    private int mSelectionStartBeforeDelete;    private static final String SCHEME_TEL = "tel:" ;    private static final String SCHEME_HTTP = "http:" ;    private static final String SCHEME_EMAIL = "mailto:" ;        ///建立一个字符和整数的hash表,用于链接电话,网站,还有邮箱    private static final Map sSchemaActionResMap = new HashMap();    static {        sSchemaActionResMap.put(SCHEME_TEL, R.string.note_link_tel);        sSchemaActionResMap.put(SCHEME_HTTP, R.string.note_link_web);        sSchemaActionResMap.put(SCHEME_EMAIL, R.string.note_link_email);    }       //在NoteEditActivity中删除或添加文本的操作,可以看做是一个文本是否被变的标记,英文注释已说明的很清楚    public interface OnTextViewChangeListener {            //处理删除按键时的操作        void onEditTextDelete(int index, String text);                //处理进入按键时的操作        void onEditTextEnter(int index, String text);                void onTextChange(int index, boolean hasText);    }    private OnTextViewChangeListener mOnTextViewChangeListener;    //根据context设置文本    public NoteEditText(Context context) {        super(context, null);//用super引用父类变量        mIndex = 0;    }    //设置当前光标    public void setIndex(int index) {        mIndex = index;    }        //初始化文本修改标记    public void setOnTextViewChangeListener(OnTextViewChangeListener listener) {        mOnTextViewChangeListener = listener;    }    //AttributeSet 百度了一下是自定义空控件属性,用于维护便签动态变化的属性    //初始化便签    public NoteEditText(Context context, AttributeSet    attrs) {        super(context, attrs, android.R.attr.editTextStyle);    }    // 根据defstyle自动初始化    public NoteEditText(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        // TODO Auto-generated construct or stub    }     @Override    //view里的函数,处理手机屏幕的所有事件        public boolean onTouchEvent(MotionEvent event) {        switch (event.getAction()) {        //重写了需要处理屏幕被按下的事件            case MotionEvent.ACTION_DOWN:            //跟新当前坐标值                int x = (int) event.getX();                int y = (int) event.getY();                x -= getTotalPaddingLeft();                y -= getTotalPaddingTop();                x += getScrollX();                y += getScrollY();    //用布局控件layout根据x,y的新值设置新的位置                Layout layout = getLayout();                int line = layout.getLineForVertical(y);                int off = layout.getOffsetForHorizontal(line, x);    //更新光标新的位置                Selection.setSelection(getText(), off);                break;        }        return super.onTouchEvent(event);    }    @Override        public boolean onKeyDown(int keyCode, KeyEvent event) {        switch (keyCode) {           //根据按键的 Unicode 编码值来处理            case KeyEvent.KEYCODE_ENTER:            //“进入”按键                if (mOnTextViewChangeListener != null) {                    return false;                }                break;            case KeyEvent.KEYCODE_DEL:            //“删除”按键                mSelectionStartBeforeDelete = getSelectionStart();                break;            default:                break;        }      //继续执行父类的其他点击事件        return super.onKeyDown(keyCode, event);    }    @Override        public boolean onKeyUp(int keyCode, KeyEvent event) {        switch(keyCode) {        //根据按键的 Unicode 编码值来处理,有删除和进入2种操作            case KeyEvent.KEYCODE_DEL:                if (mOnTextViewChangeListener != null) {                //若是被修改过                    if (0 == mSelectionStartBeforeDelete && mIndex != 0) {                    //若之前有被修改并且文档不为空                        mOnTextViewChangeListener.onEditTextDelete(mIndex, getText().toString());                        //利用上文OnTextViewChangeListener对KEYCODE_DEL按键情况的删除函数进行删除                        return true;                    }                } else {                    Log.d(TAG, "OnTextViewChangeListener was not seted");                    //其他情况报错,文档的改动监听器并没有建立                }                break;            case KeyEvent.KEYCODE_ENTER:            //同上也是分为监听器是否建立2种情况                if (mOnTextViewChangeListener != null) {                    int selectionStart = getSelectionStart();                    //获取当前位置                    String text = getText().subSequence(selectionStart, length()).toString();                    //获取当前文本                    setText(getText().subSequence(0, selectionStart));                    //根据获取的文本设置当前文本                    mOnTextViewChangeListener.onEditTextEnter(mIndex + 1, text);                    //当{@link KeyEvent#KEYCODE_ENTER}添加新文本                 } else {                    Log.d(TAG, "OnTextViewChangeListener was not seted");                  //其他情况报错,文档的改动监听器并没有建立                }                break;            default:                break;        }        //继续执行父类的其他按键弹起的事件        return super.onKeyUp(keyCode, event);    }    @Override        protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {    if (mOnTextViewChangeListener != null) {    //若监听器已经建立            if (!focused && TextUtils.isEmpty(getText())) {            //获取到焦点并且文本不为空                mOnTextViewChangeListener.onTextChange(mIndex, false);                //mOnTextViewChangeListener子函数,置false隐藏事件选项            } else {                mOnTextViewChangeListener.onTextChange(mIndex, true);              //mOnTextViewChangeListener子函数,置true显示事件选项            }        }    //继续执行父类的其他焦点变化的事件        super.onFocusChanged(focused, direction, previouslyFocusedRect);    }    @Override        protected void onCreateContextMenu(ContextMenu menu) {        if (getText() instanceof Spanned) {        //有文本存在            int selStart = getSelectionStart();            int selEnd = getSelectionEnd();            //获取文本开始和结尾位置            int min = Math.min(selStart, selEnd);            int max = Math.max(selStart, selEnd);            //获取开始到结尾的最大值和最小值                        final URLSpan[] urls = ((Spanned) getText()).getSpans(min, max, URLSpan.class);            //设置url的信息的范围值            if (urls.length == 1) {                int defaultResId = 0;                for(String schema: sSchemaActionResMap.keySet()) {                //获取计划表中所有的key值                    if(urls[0].getURL().indexOf(schema) >= 0) {                    //若url可以添加则在添加后将defaultResId置为key所映射的值                        defaultResId = sSchemaActionResMap.get(schema);                        break;                    }                }                if (defaultResId == 0) {                //defaultResId == 0则说明url并没有添加任何东西,所以置为连接其他SchemaActionResMap的值                    defaultResId = R.string.note_link_other;                }                //建立菜单                menu.add(0, 0, 0, defaultResId).setOnMenuItemClickListener(                        new OnMenuItemClickListener() {                        //新建按键监听器public boolean onMenuItemClick(MenuItem item) {    // goto a new intent    urls[0].onClick(NoteEditText.this);    //根据相应的文本设置菜单的按键    return true;}                        });            }        }      //继续执行父类的其他菜单创建的事件        super.onCreateContextMenu(menu);    }}

10、NoteItemData.java

package net.micode.notes.ui;import android.content.Context;import android.database.Cursor;import android.text.TextUtils;import net.micode.notes.data.Contact;import net.micode.notes.data.Notes;import net.micode.notes.data.Notes.NoteColumns;import net.micode.notes.tool.DataUtils;public class NoteItemData {    static final String [] PROJECTION = new String [] {        NoteColumns.ID,        NoteColumns.ALERTED_DATE,        NoteColumns.BG_COLOR_ID,        NoteColumns.CREATED_DATE,        NoteColumns.HAS_ATTACHMENT,        NoteColumns.MODIFIED_DATE,        NoteColumns.NOTES_COUNT,        NoteColumns.PARENT_ID,        NoteColumns.SNIPPET,        NoteColumns.TYPE,        NoteColumns.WIDGET_ID,        NoteColumns.WIDGET_TYPE,    };    //常量标记和数据就不一一标记了,意义翻译基本就知道    private static final int ID_COLUMN                    = 0;    private static final int ALERTED_DATE_COLUMN          = 1;    private static final int BG_COLOR_ID_COLUMN           = 2;    private static final int CREATED_DATE_COLUMN          = 3;    private static final int HAS_ATTACHMENT_COLUMN        = 4;    private static final int MODIFIED_DATE_COLUMN         = 5;    private static final int NOTES_COUNT_COLUMN           = 6;    private static final int PARENT_ID_COLUMN             = 7;    private static final int SNIPPET_COLUMN               = 8;    private static final int TYPE_COLUMN                  = 9;    private static final int WIDGET_ID_COLUMN             = 10;    private static final int WIDGET_TYPE_COLUMN           = 11;    private long mId;    private long mAlertDate;    private int mBgColorId;    private long mCreatedDate;    private boolean mHasAttachment;    private long mModifiedDate;    private int mNotesCount;    private long mParentId;    private String mSnippet;    private int mType;    private int mWidgetId;    private int mWidgetType;    private String mName;    private String mPhoneNumber;    private boolean mIsLastItem;    private boolean mIsFirstItem;    private boolean mIsOnlyOneItem;    private boolean mIsOneNoteFollowingFolder;    private boolean mIsMultiNotesFollowingFolder;    //初始化NoteItemData,主要利用光标cursor获取的东西    public NoteItemData(Context context, Cursor  cursor) {    //getxxx为转换格式        mId = cursor.getLong(ID_COLUMN);        mAlertDate = cursor.getLong(ALERTED_DATE_COLUMN);        mBgColorId = cursor.getInt(BG_COLOR_ID_COLUMN);        mCreatedDate = cursor.getLong(CREATED_DATE_COLUMN);        mHasAttachment = (cursor.getInt(HAS_ATTACHMENT_COLUMN) > 0) ? true : false;        mModifiedDate = cursor.getLong(MODIFIED_DATE_COLUMN);        mNotesCount = cursor.getInt(NOTES_COUNT_COLUMN);        mParentId = cursor.getLong(PARENT_ID_COLUMN);        mSnippet = cursor.getString(SNIPPET_COLUMN);        mSnippet = mSnippet.replace(NoteEditActivity.TAG_CHECKED, "").replace(                NoteEditActivity.TAG_UNCHECKED, "");        mType = cursor.getInt(TYPE_COLUMN);        mWidgetId = cursor.getInt(WIDGET_ID_COLUMN);        mWidgetType = cursor.getInt(WIDGET_TYPE_COLUMN);        //初始化电话号码的信息        mPhoneNumber = "";        if (mParentId == Notes.ID_CALL_RECORD_FOLDER) {            mPhoneNumber = DataUtils.getCallNumberByNoteId(context.getContentResolver(), mId);            if (!TextUtils.isEmpty(mPhoneNumber)) {//mphonenumber里有符合字符串,则用contart功能连接                mName = Contact.getContact(context, mPhoneNumber);                if (mName == null) {                    mName = mPhoneNumber;                }            }        }        if (mName == null) {            mName = "";        }        checkPostion(cursor);    }    ///根据鼠标的位置设置标记,和位置    private void checkPostion(Cursor cursor) {    //初始化几个标记,cursor具体功能笔记中已提到,不一一叙述        mIsLastItem = cursor.isLast() ? true : false;        mIsFirstItem = cursor.isFirst() ? true : false;        mIsOnlyOneItem = (cursor.getCount() == 1);        //初始化“多重子文件”“单一子文件”2个标记        mIsMultiNotesFollowingFolder = false;        mIsOneNoteFollowingFolder = false;        //主要是设置上诉2标记        if (mType == Notes.TYPE_NOTE && !mIsFirstItem) {//若是note格式并且不是第一个元素            int position = cursor.getPosition();            if (cursor.moveToPrevious()) {//获取光标位置后看上一行                if (cursor.getInt(TYPE_COLUMN) == Notes.TYPE_FOLDER                        || cursor.getInt(TYPE_COLUMN) == Notes.TYPE_SYSTEM) {//若光标满足系统或note格式                    if (cursor.getCount() > (position + 1)) {                        mIsMultiNotesFollowingFolder = true;//若是数据行数大于但前位置+1则设置成正确                    } else {                        mIsOneNoteFollowingFolder = true;//否则单一文件夹标记为true                    }                }                if (!cursor.moveToNext()) {//若不能再往下走则报错                    throw new IllegalStateException("cursor move to previous but can't move back");                }            }        }    }///以下都是获取标记没什么好说的,不过倒数第二个需要说明下,很具体看下面    public boolean isOneFollowingFolder() {        return mIsOneNoteFollowingFolder;    }    public boolean isMultiFollowingFolder() {        return mIsMultiNotesFollowingFolder;    }    public boolean isLast() {        return mIsLastItem;    }    public String getCallName() {        return mName;    }    public boolean isFirst() {        return mIsFirstItem;    }    public boolean isSingle() {        return mIsOnlyOneItem;    }    public long getId() {        return mId;    }    public long getAlertDate() {        return mAlertDate;    }    public long getCreatedDate() {        return mCreatedDate;    }    public boolean hasAttachment() {        return mHasAttachment;    }    public long getModifiedDate() {        return mModifiedDate;    }    public int getBgColorId() {        return mBgColorId;    }    public long getParentId() {        return mParentId;    }    public int getNotesCount() {        return mNotesCount;    }    public long getFolderId () {        return mParentId;    }    public int getType() {        return mType;    }    public int getWidgetType() {        return mWidgetType;    }    public int getWidgetId() {        return mWidgetId;    }    public String getSnippet() {        return mSnippet;    }    public boolean hasAlert() {        return (mAlertDate > 0);    }    //若数据父id为保存至文件夹模式的id且满足电话号码单元不为空,则isCallRecord为true    public boolean isCallRecord() {        return (mParentId == Notes.ID_CALL_RECORD_FOLDER && !TextUtils.isEmpty(mPhoneNumber));    }    public static int getNoteType(Cursor cursor) {        return cursor.getInt(TYPE_COLUMN);    }}

11、NotesListActivity.java

package net.micode.notes.ui;import android.app.Activity;import android.app.AlertDialog;import android.app.Dialog;import android.appwidget.AppWidgetManager;import android.content.AsyncQueryHandler;import android.content.ContentResolver;import android.content.ContentValues;import android.content.Context;import android.content.DialogInterface;import android.content.Intent;import android.content.SharedPreferences;import android.database.Cursor;import android.os.AsyncTask;import android.os.Bundle;import android.preference.PreferenceManager;import android.text.Editable;import android.text.TextUtils;import android.text.TextWatcher;import android.util.Log;import android.view.ActionMode;import android.view.ContextMenu;import android.view.ContextMenu.ContextMenuInfo;import android.view.Display;import android.view.HapticFeedbackConstants;import android.view.LayoutInflater;import android.view.Menu;import android.view.MenuItem;import android.view.MenuItem.OnMenuItemClickListener;import android.view.MotionEvent;import android.view.View;import android.view.View.OnClickListener;import android.view.View.OnCreateContextMenuListener;import android.view.View.OnTouchListener;import android.view.inputmethod.InputMethodManager;import android.widget.AdapterView;import android.widget.AdapterView.OnItemClickListener;import android.widget.AdapterView.OnItemLongClickListener;import android.widget.Button;import android.widget.EditText;import android.widget.ListView;import android.widget.PopupMenu;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.NoteColumns;import net.micode.notes.gtask.remote.GTaskSyncService;import net.micode.notes.model.WorkingNote;import net.micode.notes.tool.BackupUtils;import net.micode.notes.tool.DataUtils;import net.micode.notes.tool.ResourceParser;import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute;import net.micode.notes.widget.NoteWidgetProvider_2x;import net.micode.notes.widget.NoteWidgetProvider_4x;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.util.HashSet;//主界面,一进入就是这个界面public class NotesListActivity extends Activity implements OnClickListener, OnItemLongClickListener {   //没有用特定的标签加注释。。。感觉没有什么用    private static final int FOLDER_NOTE_LIST_QUERY_TOKEN = 0;    private static final int FOLDER_LIST_QUERY_TOKEN      = 1;    private static final int MENU_FOLDER_DELETE = 0;    private static final int MENU_FOLDER_VIEW = 1;    private static final int MENU_FOLDER_CHANGE_NAME = 2;    private static final String PREFERENCE_ADD_INTRODUCTION = "net.micode.notes.introduction";  //单行超过80个字符    private enum ListEditState {        NOTE_LIST, SUB_FOLDER, CALL_RECORD_FOLDER    };    private ListEditState mState;    private BackgroundQueryHandler mBackgroundQueryHandler;    private NotesListAdapter mNotesListAdapter;    private ListView mNotesListView;    private Button mAddNewNote;    private boolean mDispatch;    private int mOriginY;    private int mDispatchY;    private TextView mTitleBar;    private long mCurrentFolderId;    private ContentResolver mContentResolver;    private ModeCallback mModeCallBack;    private static final String TAG = "NotesListActivity";    public static final int NOTES_LISTVIEW_SCROLL_RATE = 30;    private NoteItemData mFocusNoteDataItem;    private static final String NORMAL_SELECTION = NoteColumns.PARENT_ID + "=?";    private static final String ROOT_FOLDER_SELECTION = "(" + NoteColumns.TYPE + "<>"            + Notes.TYPE_SYSTEM + " AND " + NoteColumns.PARENT_ID + "=?)" + " OR ("            + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND "            + NoteColumns.NOTES_COUNT + ">0)";    private final static int REQUEST_CODE_OPEN_NODE = 102;    private final static int REQUEST_CODE_NEW_NODE  = 103;    @Override    // 创建类    protected void onCreate(final Bundle savedInstanceState) {  //需要是final类型   根据程序上下文环境,Java关键字final有“这是无法改变的”或者“终态的”含义,它可以修饰非抽象类、非抽象类成员方法和变量。你可能出于两种理解而需要阻止改变:设计或效率。        // final类不能被继承,没有子类,final类中的方法默认是final的。        //final方法不能被子类的方法覆盖,但可以被继承。        //final成员变量表示常量,只能被赋值一次,赋值后值不再改变。        //final不能用于修饰构造方法。        super.onCreate(savedInstanceState); // 调用父类的onCreate函数        setContentView(R.layout.note_list);        initResources();                setAppInfoFromRawRes();    }    @Override    // 返回一些子模块完成的数据交给主Activity处理    protected void onActivityResult(int requestCode, int resultCode, Intent data) {        // 结果值 和 要求值 符合要求     if (resultCode == RESULT_OK                && (requestCode == REQUEST_CODE_OPEN_NODE || requestCode == REQUEST_CODE_NEW_NODE)) {            mNotesListAdapter.changeCursor(null);        } else {            super.onActivityResult(requestCode, resultCode, data);            // 调用 Activity 的onActivityResult()        }    }    private void setAppInfoFromRawRes() {    // Android平台给我们提供了一个SharedPreferences类,它是一个轻量级的存储类,特别适合用于保存软件配置参数。        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);        if (!sp.getBoolean(PREFERENCE_ADD_INTRODUCTION, false)) {            StringBuilder sb = new StringBuilder();            InputStream in = null;            try {                // 把资源文件放到应用程序的/raw/raw下,那么就可以在应用中使用getResources获取资源后,            // 以openRawResource方法(不带后缀的资源文件名)打开这个文件。             in = getResources().openRawResource(R.raw.introduction);                if (in != null) {                    InputStreamReader isr = new InputStreamReader(in);                    BufferedReader br = new BufferedReader(isr);                    char [] buf = new char[1024];  // 自行定义的数值,使用者不知道有什么意义                    int len = 0;                    while ((len = br.read(buf)) > 0) {                        sb.append(buf, 0, len);                    }                } else {                    Log.e(TAG, "Read introduction file error");                    return;                }            } catch (IOException e) {                e.printStackTrace();                return;            } finally {                if (in != null) {                    try {                        in.close();                    } catch (IOException e) {                        // TODO Auto-generated catch block                        e.printStackTrace();                    }                }            }                        // 创建空的WorkingNote            WorkingNote note = WorkingNote.createEmptyNote(this, Notes.ID_ROOT_FOLDER,                    AppWidgetManager.INVALID_APPWIDGET_ID, Notes.TYPE_WIDGET_INVALIDE,                    ResourceParser.RED);            note.setWorkingText(sb.toString());            if (note.saveNote()) {            // 更新保存note的信息                sp.edit().putBoolean(PREFERENCE_ADD_INTRODUCTION, true).commit();            } else {                Log.e(TAG, "Save introduction note error");                return;            }        }    }    @Override    protected void onStart() {        super.onStart();        startAsyncNotesListQuery();    }    // 初始化资源    private void initResources() {        mContentResolver = this.getContentResolver(); // 获取应用程序的数据,得到类似数据表的东西        mBackgroundQueryHandler = new BackgroundQueryHandler(this.getContentResolver());        mCurrentFolderId = Notes.ID_ROOT_FOLDER;                // findViewById 是安卓编程的定位函数,主要是引用.R文件里的引用名        mNotesListView = (ListView) findViewById(R.id.notes_list); // 绑定XML中的ListView,作为Item的容器        mNotesListView.addFooterView(LayoutInflater.from(this).inflate(R.layout.note_list_footer, null),                null, false);        mNotesListView.setOnItemClickListener(new OnListItemClickListener());        mNotesListView.setOnItemLongClickListener(this);        mNotesListAdapter = new NotesListAdapter(this);        mNotesListView.setAdapter(mNotesListAdapter);        mAddNewNote = (Button) findViewById(R.id.btn_new_note);// 在activity中要获取该按钮        mAddNewNote.setOnClickListener(this);        mAddNewNote.setOnTouchListener(new NewNoteOnTouchListener());        mDispatch = false;        mDispatchY = 0;        mOriginY = 0;        mTitleBar = (TextView) findViewById(R.id.tv_title_bar);        mState = ListEditState.NOTE_LIST;        mModeCallBack = new ModeCallback();    }    // 继承自ListView.MultiChoiceModeListener 和 OnMenuItemClickListener    private class ModeCallback implements ListView.MultiChoiceModeListener, OnMenuItemClickListener {        private DropdownMenu mDropDownMenu;        private ActionMode mActionMode;        private MenuItem mMoveMenu;        public boolean onCreateActionMode(ActionMode mode, Menu menu) {            getMenuInflater().inflate(R.menu.note_list_options, menu);            menu.findItem(R.id.delete).setOnMenuItemClickListener(this);            mMoveMenu = menu.findItem(R.id.move);            if (mFocusNoteDataItem.getParentId() == Notes.ID_CALL_RECORD_FOLDER                    || DataUtils.getUserFolderCount(mContentResolver) == 0) {                mMoveMenu.setVisible(false);            } else {                mMoveMenu.setVisible(true);                mMoveMenu.setOnMenuItemClickListener(this);            }            mActionMode = mode;            mNotesListAdapter.setChoiceMode(true);            mNotesListView.setLongClickable(false);            mAddNewNote.setVisibility(View.GONE);            View customView = LayoutInflater.from(NotesListActivity.this).inflate(                    R.layout.note_list_dropdown_menu, null);            mode.setCustomView(customView);            mDropDownMenu = new DropdownMenu(NotesListActivity.this,                    (Button) customView.findViewById(R.id.selection_menu),                    R.menu.note_list_dropdown);            mDropDownMenu.setOnDropdownMenuItemClickListener(new PopupMenu.OnMenuItemClickListener(){                public boolean onMenuItemClick(final MenuItem item) {                    mNotesListAdapter.selectAll(!mNotesListAdapter.isAllSelected());                    updateMenu();                    return true;                }            });            return true;        }        // 更新菜单        private void updateMenu() {            int selectedCount = mNotesListAdapter.getSelectedCount();            // Update dropdown menu            String format = getResources().getString(R.string.menu_select_title, selectedCount);            mDropDownMenu.setTitle(format); // 更改            MenuItem item = mDropDownMenu.findItem(R.id.action_select_all);            if (item != null) {                if (mNotesListAdapter.isAllSelected()) {                    item.setChecked(true);                    item.setTitle(R.string.menu_deselect_all);                } else {                    item.setChecked(false);                    item.setTitle(R.string.menu_select_all);                }            }        }        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {            // TODO Auto-generated method stub            return false;        }        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {            // TODO Auto-generated method stub            return false;        }        public void onDestroyActionMode(ActionMode mode) {            mNotesListAdapter.setChoiceMode(false);            mNotesListView.setLongClickable(true);            mAddNewNote.setVisibility(View.VISIBLE);        }        public void finishActionMode() {            mActionMode.finish();        }        public void onItemCheckedStateChanged(ActionMode mode, int position, long id,                boolean checked) {            mNotesListAdapter.setCheckedItem(position, checked);            updateMenu();        }        public boolean onMenuItemClick(MenuItem item) {            if (mNotesListAdapter.getSelectedCount() == 0) {                Toast.makeText(NotesListActivity.this, getString(R.string.menu_select_none),                        Toast.LENGTH_SHORT).show();                return true;            }            switch (item.getItemId()) {                case R.id.delete:                    AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);                    builder.setTitle(getString(R.string.alert_title_delete));                    builder.setIcon(android.R.drawable.ic_dialog_alert);                    builder.setMessage(getString(R.string.alert_message_delete_notes,                 mNotesListAdapter.getSelectedCount()));                    builder.setPositiveButton(android.R.string.ok,                 new DialogInterface.OnClickListener() {                     public void onClick(DialogInterface dialog, int which) {                         batchDelete();                     }                 });                    builder.setNegativeButton(android.R.string.cancel, null);                    builder.show();                    break;                case R.id.move:                    startQueryDestinationFolders();                    break;                default:                    return false;            }            return true;        }    }    private class NewNoteOnTouchListener implements OnTouchListener {        public boolean onTouch(View v, MotionEvent event) {            switch (event.getAction()) {                case MotionEvent.ACTION_DOWN: {                    Display display = getWindowManager().getDefaultDisplay();                    int screenHeight = display.getHeight();                    int newNoteViewHeight = mAddNewNote.getHeight();                    int start = screenHeight - newNoteViewHeight;                    int eventY = start + (int) event.getY();                                        if (mState == ListEditState.SUB_FOLDER) {                        eventY -= mTitleBar.getHeight();                        start -= mTitleBar.getHeight();                    }                                        if (event.getY() < (event.getX() * (-0.12) + 94)) {                        View view = mNotesListView.getChildAt(mNotesListView.getChildCount() - 1    - mNotesListView.getFooterViewsCount());                        if (view != null && view.getBottom() > start    && (view.getTop() < (start + 94))) {mOriginY = (int) event.getY();mDispatchY = eventY;event.setLocation(event.getX(), mDispatchY);mDispatch = true;return mNotesListView.dispatchTouchEvent(event);                        }                    }                    break;                }                case MotionEvent.ACTION_MOVE: {                    if (mDispatch) {                        mDispatchY += (int) event.getY() - mOriginY;                        event.setLocation(event.getX(), mDispatchY);                        return mNotesListView.dispatchTouchEvent(event);                    }                    break;                }                default: {                    if (mDispatch) {                        event.setLocation(event.getX(), mDispatchY);                        mDispatch = false;                        return mNotesListView.dispatchTouchEvent(event);                    }                    break;                }            }            return false;        }    };    private void startAsyncNotesListQuery() {        String selection = (mCurrentFolderId == Notes.ID_ROOT_FOLDER) ? ROOT_FOLDER_SELECTION                : NORMAL_SELECTION;        mBackgroundQueryHandler.startQuery(FOLDER_NOTE_LIST_QUERY_TOKEN, null,                Notes.CONTENT_NOTE_URI, NoteItemData.PROJECTION, selection, new String[] {                    String.valueOf(mCurrentFolderId)                }, NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC");    }    private final class BackgroundQueryHandler extends AsyncQueryHandler {        public BackgroundQueryHandler(ContentResolver contentResolver) {            super(contentResolver);        }        @Override        protected void onQueryComplete(int token, Object cookie, Cursor cursor) {            switch (token) {                case FOLDER_NOTE_LIST_QUERY_TOKEN:                    mNotesListAdapter.changeCursor(cursor);                    break;                case FOLDER_LIST_QUERY_TOKEN:                    if (cursor != null && cursor.getCount() > 0) {                        showFolderListMenu(cursor);                    } else {                        Log.e(TAG, "Query folder failed");                    }                    break;                default:                    return;            }        }    }    private void showFolderListMenu(Cursor cursor) {        AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);        builder.setTitle(R.string.menu_title_select_folder);        final FoldersListAdapter adapter = new FoldersListAdapter(this, cursor);        builder.setAdapter(adapter, new DialogInterface.OnClickListener() {            public void onClick(DialogInterface dialog, int which) {                DataUtils.batchMoveToFolder(mContentResolver,                        mNotesListAdapter.getSelectedItemIds(), adapter.getItemId(which));                Toast.makeText(                        NotesListActivity.this,                        getString(R.string.format_move_notes_to_folder,    mNotesListAdapter.getSelectedCount(),    adapter.getFolderName(NotesListActivity.this, which)),                        Toast.LENGTH_SHORT).show();                mModeCallBack.finishActionMode();            }        });        builder.show();    }    private void createNewNote() {        Intent intent = new Intent(this, NoteEditActivity.class);        intent.setAction(Intent.ACTION_INSERT_OR_EDIT);        intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mCurrentFolderId);        this.startActivityForResult(intent, REQUEST_CODE_NEW_NODE);    }    private void batchDelete() {        new AsyncTask>() {            protected HashSet doInBackground(Void... unused) {                HashSet widgets = mNotesListAdapter.getSelectedWidget();                if (!isSyncMode()) {                    // if not synced, delete notes directly                    if (DataUtils.batchDeleteNotes(mContentResolver, mNotesListAdapter.getSelectedItemIds())) {                    } else {                        Log.e(TAG, "Delete notes error, should not happens");                    }                } else {                    // in sync mode, we'll move the deleted note into the trash                    // folder                    if (!DataUtils.batchMoveToFolder(mContentResolver, mNotesListAdapter.getSelectedItemIds(), Notes.ID_TRASH_FOLER)) {                        Log.e(TAG, "Move notes to trash folder error, should not happens");                    }                }                return widgets;            }            @Override            protected void onPostExecute(HashSet widgets) {                if (widgets != null) {                    for (AppWidgetAttribute widget : widgets) {                        if (widget.widgetId != AppWidgetManager.INVALID_APPWIDGET_ID    && widget.widgetType != Notes.TYPE_WIDGET_INVALIDE) {updateWidget(widget.widgetId, widget.widgetType);                        }                    }                }                mModeCallBack.finishActionMode();            }        }.execute();    }    private void deleteFolder(long folderId) {        if (folderId == Notes.ID_ROOT_FOLDER) {            Log.e(TAG, "Wrong folder id, should not happen " + folderId);            return;        }        HashSet ids = new HashSet();        ids.add(folderId);        HashSet widgets = DataUtils.getFolderNoteWidget(mContentResolver,                folderId);        if (!isSyncMode()) {            // if not synced, delete folder directly            DataUtils.batchDeleteNotes(mContentResolver, ids);        } else {            // in sync mode, we'll move the deleted folder into the trash folder            DataUtils.batchMoveToFolder(mContentResolver, ids, Notes.ID_TRASH_FOLER);        }        if (widgets != null) {            for (AppWidgetAttribute widget : widgets) {                if (widget.widgetId != AppWidgetManager.INVALID_APPWIDGET_ID                        && widget.widgetType != Notes.TYPE_WIDGET_INVALIDE) {                    updateWidget(widget.widgetId, widget.widgetType);                }            }        }    }    private void openNode(NoteItemData data) {        Intent intent = new Intent(this, NoteEditActivity.class);        intent.setAction(Intent.ACTION_VIEW);        intent.putExtra(Intent.EXTRA_UID, data.getId());        this.startActivityForResult(intent, REQUEST_CODE_OPEN_NODE);    }    private void openFolder(NoteItemData data) {        mCurrentFolderId = data.getId();        startAsyncNotesListQuery();        if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) {            mState = ListEditState.CALL_RECORD_FOLDER;            mAddNewNote.setVisibility(View.GONE);        } else {            mState = ListEditState.SUB_FOLDER;        }        if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) {            mTitleBar.setText(R.string.call_record_folder_name);        } else {            mTitleBar.setText(data.getSnippet());        }        mTitleBar.setVisibility(View.VISIBLE);    }    public void onClick(View v) {        switch (v.getId()) {            case R.id.btn_new_note:                createNewNote();                break;            default:                break;        }    }    private void showSoftInput() {        InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);        if (inputMethodManager != null) {            inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);        }    }    private void hideSoftInput(View view) {        InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);        inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);    }    private void showCreateOrModifyFolderDialog(final boolean create) {        final AlertDialog.Builder builder = new AlertDialog.Builder(this);        View view = LayoutInflater.from(this).inflate(R.layout.dialog_edit_text, null);        final EditText etName = (EditText) view.findViewById(R.id.et_foler_name);        showSoftInput();        if (!create) {            if (mFocusNoteDataItem != null) {                etName.setText(mFocusNoteDataItem.getSnippet());                builder.setTitle(getString(R.string.menu_folder_change_name));            } else {                Log.e(TAG, "The long click data item is null");                return;            }        } else {            etName.setText("");            builder.setTitle(this.getString(R.string.menu_create_folder));        }        builder.setPositiveButton(android.R.string.ok, null);        builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {            public void onClick(DialogInterface dialog, int which) {                hideSoftInput(etName);            }        });        final Dialog dialog = builder.setView(view).show();        final Button positive = (Button)dialog.findViewById(android.R.id.button1);        positive.setOnClickListener(new OnClickListener() {            public void onClick(View v) {                hideSoftInput(etName);                String name = etName.getText().toString();                if (DataUtils.checkVisibleFolderName(mContentResolver, name)) {                    Toast.makeText(NotesListActivity.this, getString(R.string.folder_exist, name),Toast.LENGTH_LONG).show();                    etName.setSelection(0, etName.length());                    return;                }                if (!create) {                    if (!TextUtils.isEmpty(name)) {                        ContentValues values = new ContentValues();                        values.put(NoteColumns.SNIPPET, name);                        values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER);                        values.put(NoteColumns.LOCAL_MODIFIED, 1);                        mContentResolver.update(Notes.CONTENT_NOTE_URI, values, NoteColumns.ID    + "=?", new String[] {String.valueOf(mFocusNoteDataItem.getId())                        });                    }                } else if (!TextUtils.isEmpty(name)) {                    ContentValues values = new ContentValues();                    values.put(NoteColumns.SNIPPET, name);                    values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER);                    mContentResolver.insert(Notes.CONTENT_NOTE_URI, values);                }                dialog.dismiss();            }        });        if (TextUtils.isEmpty(etName.getText())) {            positive.setEnabled(false);        }                etName.addTextChangedListener(new TextWatcher() {            public void beforeTextChanged(CharSequence s, int start, int count, int after) {                // TODO Auto-generated method stub            }            public void onTextChanged(CharSequence s, int start, int before, int count) {                if (TextUtils.isEmpty(etName.getText())) {                    positive.setEnabled(false);                } else {                    positive.setEnabled(true);                }            }            public void afterTextChanged(Editable s) {                // TODO Auto-generated method stub            }        });    }        @Override    public void onBackPressed() { switch (mState) {            case SUB_FOLDER:                mCurrentFolderId = Notes.ID_ROOT_FOLDER;                mState = ListEditState.NOTE_LIST;                startAsyncNotesListQuery();                mTitleBar.setVisibility(View.GONE);                break;            case CALL_RECORD_FOLDER:                mCurrentFolderId = Notes.ID_ROOT_FOLDER;                mState = ListEditState.NOTE_LIST;                mAddNewNote.setVisibility(View.VISIBLE);                mTitleBar.setVisibility(View.GONE);                startAsyncNotesListQuery();                break;            case NOTE_LIST:                super.onBackPressed();                break;            default:                break;        }    }        private void updateWidget(int appWidgetId, int appWidgetType) {        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);        if (appWidgetType == Notes.TYPE_WIDGET_2X) {            intent.setClass(this, NoteWidgetProvider_2x.class);        } else if (appWidgetType == 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[] {            appWidgetId        });        sendBroadcast(intent);        setResult(RESULT_OK, intent);    }        private final OnCreateContextMenuListener mFolderOnCreateContextMenuListener = new OnCreateContextMenuListener() {        public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {            if (mFocusNoteDataItem != null) {                menu.setHeaderTitle(mFocusNoteDataItem.getSnippet());                menu.add(0, MENU_FOLDER_VIEW, 0, R.string.menu_folder_view);                menu.add(0, MENU_FOLDER_DELETE, 0, R.string.menu_folder_delete);                menu.add(0, MENU_FOLDER_CHANGE_NAME, 0, R.string.menu_folder_change_name);            }        }    };    @Override    public void onContextMenuClosed(Menu menu) {        if (mNotesListView != null) {            mNotesListView.setOnCreateContextMenuListener(null);        }        super.onContextMenuClosed(menu);    }        @Override    public boolean onContextItemSelected(MenuItem item) {        if (mFocusNoteDataItem == null) {            Log.e(TAG, "The long click data item is null");            return false;        }        switch (item.getItemId()) {            case MENU_FOLDER_VIEW:                openFolder(mFocusNoteDataItem);//打开对应文件                break;            case MENU_FOLDER_DELETE:                AlertDialog.Builder builder = new AlertDialog.Builder(this);//设置确认是否删除的对话框                builder.setTitle(getString(R.string.alert_title_delete));                builder.setIcon(android.R.drawable.ic_dialog_alert);                builder.setMessage(getString(R.string.alert_message_delete_folder));                builder.setPositiveButton(android.R.string.ok,                        new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog, int which) {    deleteFolder(mFocusNoteDataItem.getId());}                        });                builder.setNegativeButton(android.R.string.cancel, null);                builder.show();//显示对话框                break;            case MENU_FOLDER_CHANGE_NAME:                showCreateOrModifyFolderDialog(false);                break;            default:                break;        }        return true;    }    @Override    public boolean onPrepareOptionsMenu(Menu menu) {        menu.clear();        if (mState == ListEditState.NOTE_LIST) {            getMenuInflater().inflate(R.menu.note_list, menu);            // set sync or sync_cancel            menu.findItem(R.id.menu_sync).setTitle(                    GTaskSyncService.isSyncing() ? R.string.menu_sync_cancel : R.string.menu_sync);        } else if (mState == ListEditState.SUB_FOLDER) {            getMenuInflater().inflate(R.menu.sub_folder, menu);        } else if (mState == ListEditState.CALL_RECORD_FOLDER) {            getMenuInflater().inflate(R.menu.call_record_folder, menu);        } else {            Log.e(TAG, "Wrong state:" + mState);        }        return true;    }    @Override    public boolean onOptionsItemSelected(MenuItem item) {        switch (item.getItemId()) {            case R.id.menu_new_folder: {                showCreateOrModifyFolderDialog(true);                break;            }            case R.id.menu_export_text: {                exportNoteToText();                break;            }            case R.id.menu_sync: {                if (isSyncMode()) {                    if (TextUtils.equals(item.getTitle(), getString(R.string.menu_sync))) {                        GTaskSyncService.startSync(this);                    } else {                        GTaskSyncService.cancelSync(this);                    }                } else {                    startPreferenceActivity();                }                break;            }            case R.id.menu_setting: {                startPreferenceActivity();                break;            }            case R.id.menu_new_note: {                createNewNote();                break;            }            case R.id.menu_search:                onSearchRequested();                break;            default:                break;        }        return true;    }        @Override    public boolean onSearchRequested() {        startSearch(null, false, null , false);        return true;    }        private void exportNoteToText() {        final BackupUtils backup = BackupUtils.getInstance(NotesListActivity.this);        new AsyncTask() {            @Override            protected Integer doInBackground(Void... unused) {                return backup.exportToText();            }            @Override            protected void onPostExecute(Integer result) {                if (result == BackupUtils.STATE_SD_CARD_UNMOUONTED) {                    AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);                    builder.setTitle(NotesListActivity.this.getString(R.string.failed_sdcard_export));                    builder.setMessage(NotesListActivity.this.getString(R.string.error_sdcard_unmounted));                    builder.setPositiveButton(android.R.string.ok, null);                    builder.show();                } else if (result == BackupUtils.STATE_SUCCESS) {                    AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);                    builder.setTitle(NotesListActivity.this.getString(R.string.success_sdcard_export));                    builder.setMessage(NotesListActivity.this.getString(R.string.format_exported_file_location, backup        .getExportedTextFileName(), backup.getExportedTextFileDir()));                    builder.setPositiveButton(android.R.string.ok, null);                    builder.show();                } else if (result == BackupUtils.STATE_SYSTEM_ERROR) {                    AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);                    builder.setTitle(NotesListActivity.this.getString(R.string.failed_sdcard_export));                    builder.setMessage(NotesListActivity.this.getString(R.string.error_sdcard_export));                    builder.setPositiveButton(android.R.string.ok, null);                    builder.show();                }            }        }.execute();    }        private boolean isSyncMode() {        return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0;    }        private void startPreferenceActivity() {        Activity from = getParent() != null ? getParent() : this;        Intent intent = new Intent(from, NotesPreferenceActivity.class);        from.startActivityIfNeeded(intent, -1);    }        private class OnListItemClickListener implements OnItemClickListener {        public void onItemClick(AdapterView parent, View view, int position, long id) {            if (view instanceof NotesListItem) {                NoteItemData item = ((NotesListItem) view).getItemData();                if (mNotesListAdapter.isInChoiceMode()) {                    if (item.getType() == Notes.TYPE_NOTE) {                        position = position - mNotesListView.getHeaderViewsCount();                        mModeCallBack.onItemCheckedStateChanged(null, position, id,    !mNotesListAdapter.isSelectedItem(position));                    }                    return;                }                switch (mState) {                    case NOTE_LIST:                        if (item.getType() == Notes.TYPE_FOLDER    || item.getType() == Notes.TYPE_SYSTEM) {openFolder(item);                        } else if (item.getType() == Notes.TYPE_NOTE) {openNode(item);                        } else {Log.e(TAG, "Wrong note type in NOTE_LIST");                        }                        break;                    case SUB_FOLDER:                    case CALL_RECORD_FOLDER:                        if (item.getType() == Notes.TYPE_NOTE) {openNode(item);                        } else {Log.e(TAG, "Wrong note type in SUB_FOLDER");                        }                        break;                    default:                        break;                }            }        }    }        private void startQueryDestinationFolders() {        String selection = NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>? AND " + NoteColumns.ID + "<>?";        selection = (mState == ListEditState.NOTE_LIST) ? selection:            "(" + selection + ") OR (" + NoteColumns.ID + "=" + Notes.ID_ROOT_FOLDER + ")";        mBackgroundQueryHandler.startQuery(FOLDER_LIST_QUERY_TOKEN,                null,                Notes.CONTENT_NOTE_URI,                FoldersListAdapter.PROJECTION,                selection,                new String[] {                        String.valueOf(Notes.TYPE_FOLDER),                        String.valueOf(Notes.ID_TRASH_FOLER),                        String.valueOf(mCurrentFolderId)                },                NoteColumns.MODIFIED_DATE + " DESC");    }        public boolean onItemLongClick(AdapterView parent, View view, int position, long id) {        if (view instanceof NotesListItem) {            mFocusNoteDataItem = ((NotesListItem) view).getItemData();            if (mFocusNoteDataItem.getType() == Notes.TYPE_NOTE && !mNotesListAdapter.isInChoiceMode()) {                if (mNotesListView.startActionMode(mModeCallBack) != null) {                    mModeCallBack.onItemCheckedStateChanged(null, position, id, true);                    mNotesListView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);                } else {                    Log.e(TAG, "startActionMode fails");                }            } else if (mFocusNoteDataItem.getType() == Notes.TYPE_FOLDER) {                mNotesListView.setOnCreateContextMenuListener(mFolderOnCreateContextMenuListener);            }        }        return false;    }}

12、NotesListAdapter.java

package net.micode.notes.ui;import android.content.Context;import android.database.Cursor;import android.util.Log;import android.view.View; import android.view.ViewGroup;import android.widget.CursorAdapter;import net.micode.notes.data.Notes;import java.util.Collection;import java.util.HashMap;import java.util.HashSet;import java.util.Iterator;public class NotesListAdapter extends CursorAdapter {    private static final String TAG = "NotesListAdapter";    private Context mContext;    private HashMap mSelectedIndex;    private int mNotesCount;    //便签数    private boolean mChoiceMode;   //选择模式标记        public static class AppWidgetAttribute {        public int widgetId;        public int widgetType;    };        public NotesListAdapter(Context context) {        super(context, null);  //父类对象置空        mSelectedIndex = new HashMap();  //新建选项下标的hash表        mContext = context;        mNotesCount = 0;    }    @Override        public View newView(Context context, Cursor cursor, ViewGroup parent) {        return new NotesListItem(context);    }        @Override    public void bindView(View view, Context context, Cursor cursor) {        if (view instanceof NotesListItem) {        //若view是NotesListItem的一个实例            NoteItemData itemData = new NoteItemData(context, cursor);            ((NotesListItem) view).bind(context, itemData, mChoiceMode,                    isSelectedItem(cursor.getPosition()));           //则新建一个项目选项并且用bind跟将view和鼠标,内容,便签数据捆绑在一起        }    }        public void setCheckedItem(final int position, final boolean checked) {        mSelectedIndex.put(position, checked);        //根据定位和是否勾选设置下标        notifyDataSetChanged();        //在修改后刷新activity    }        public boolean isInChoiceMode() {        return mChoiceMode;    }        public void setChoiceMode(boolean mode) {        mSelectedIndex.clear();        mChoiceMode = mode;    }        public void selectAll(boolean checked) {        Cursor cursor = getCursor();        //获取光标位置        for (int i = 0; i < getCount(); i++) {            if (cursor.moveToPosition(i)) {                if (NoteItemData.getNoteType(cursor) == Notes.TYPE_NOTE) {                    setCheckedItem(i, checked);                }            }        }        //遍历所有光标可用的位置在判断为便签类型之后勾选单项框    }        public HashSet getSelectedItemIds() {        HashSet itemSet = new HashSet();        //建立hash表        for (Integer position : mSelectedIndex.keySet()) {        //遍历所有的关键            if (mSelectedIndex.get(position) == true) {            //若光标位置可用                Long id = getItemId(position);                if (id == Notes.ID_ROOT_FOLDER) {                //原文件不需要添加                    Log.d(TAG, "Wrong item id, should not happen");                } else {                    itemSet.add(id);                }                //则将id该下标假如选项集合中}        }        return itemSet;    }        public HashSet getSelectedWidget() {        HashSet itemSet = new HashSet();        for (Integer position : mSelectedIndex.keySet()) {            if (mSelectedIndex.get(position) == true) {                Cursor c = (Cursor) getItem(position);                //以上4句和getSelectedItemIds一样,不再重复                if (c != null) {                //光标位置可用的话就建立新的Widget属性并编辑下标和类型,最后添加到选项集中                    AppWidgetAttribute widget = new AppWidgetAttribute();                    NoteItemData item = new NoteItemData(mContext, c);                    widget.widgetId = item.getWidgetId();                    widget.widgetType = item.getWidgetType();                    itemSet.add(widget);                                    } else {                    Log.e(TAG, "Invalid cursor");                    return null;                }            }        }        return itemSet;    }        public int getSelectedCount() {        Collection values = mSelectedIndex.values();        //首先获取选项下标的值        if (null == values) {            return 0;        }        Iterator iter = values.iterator();        //初始化叠加器        int count = 0;        while (iter.hasNext()) {            if (true == iter.next()) {            //若value值为真计数+1                count++;            }        }        return count;    }        public boolean isAllSelected() {        int checkedCount = getSelectedCount();        return (checkedCount != 0 && checkedCount == mNotesCount);        //获取选项数看是否等于便签的个数    }        public boolean isSelectedItem(final int position) {        if (null == mSelectedIndex.get(position)) {            return false;        }        return mSelectedIndex.get(position);    }    @Override        protected void onContentChanged() {        super.onContentChanged();        //执行基类函数        calcNotesCount();    }    @Override        public void changeCursor(Cursor cursor) {        super.changeCursor(cursor);      //执行基类函数        calcNotesCount();    }        private void calcNotesCount() {        mNotesCount = 0;        for (int i = 0; i < getCount(); i++) {        //获取总数同时遍历            Cursor c = (Cursor) getItem(i);            if (c != null) {                if (NoteItemData.getNoteType(c) == Notes.TYPE_NOTE) {                    mNotesCount++;                   //若该位置不为空并且文本类型为便签就+1                }            } else {                Log.e(TAG, "Invalid cursor");                return;            }            //否则报错        }    } }

13、NotesListItem.java

package net.micode.notes.ui;import android.content.Context;import android.text.format.DateUtils;import android.view.View;import android.widget.CheckBox;import android.widget.ImageView;import android.widget.LinearLayout;import android.widget.TextView;import net.micode.notes.R;import net.micode.notes.data.Notes;import net.micode.notes.tool.DataUtils;import net.micode.notes.tool.ResourceParser.NoteItemBgResources;//创建便签列表项目选项public class NotesListItem extends LinearLayout {    private ImageView mAlert;//闹钟图片    private TextView mTitle;  //    private TextView mTime;   //时间    private TextView mCallName;    //    private NoteItemData mItemData; //标签数据    private CheckBox mCheckBox;    //打钩框        public NotesListItem(Context context) {        super(context);   //super()它的主要作用是调整调用父类构造函数的顺序        inflate(context, R.layout.note_item, this);//Inflate可用于将一个xml中定义的布局控件找出来,这里的xml是r。layout        //findViewById用于从contentView中查找指定ID的View,转换出来的形式根据需要而定;        mAlert = (ImageView) findViewById(R.id.iv_alert_icon);        mTitle = (TextView) findViewById(R.id.tv_title);        mTime = (TextView) findViewById(R.id.tv_time);        mCallName = (TextView) findViewById(R.id.tv_name);        mCheckBox = (CheckBox) findViewById(android.R.id.checkbox);    }  ///根据data的属性对各个控件的属性的控制,主要是可见性Visibility,内容setText,格式setTextAppearance    public void bind(Context context, NoteItemData data, boolean choiceMode, boolean checked) {        if (choiceMode && data.getType() == Notes.TYPE_NOTE) {            mCheckBox.setVisibility(View.VISIBLE);  ///设置可见行为可见            mCheckBox.setChecked(checked);         ///格子打钩        } else {            mCheckBox.setVisibility(View.GONE);        }        mItemData = data;        ///设置控件属性,一共三种情况,由data的id和父id是否与保存到文件夹的id一致来决定        if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) {            mCallName.setVisibility(View.GONE);            mAlert.setVisibility(View.VISIBLE);            //设置该textview的style            mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);            //settext为设置内容            mTitle.setText(context.getString(R.string.call_record_folder_name)                    + context.getString(R.string.format_folder_files_count, data.getNotesCount()));            mAlert.setImageResource(R.drawable.call_record);        } else if (data.getParentId() == Notes.ID_CALL_RECORD_FOLDER) {            mCallName.setVisibility(View.VISIBLE);            mCallName.setText(data.getCallName());            mTitle.setTextAppearance(context,R.style.TextAppearanceSecondaryItem);            mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet()));            ///关于闹钟的设置            if (data.hasAlert()) {                mAlert.setImageResource(R.drawable.clock);//图片来源的设置                mAlert.setVisibility(View.VISIBLE);            } else {                mAlert.setVisibility(View.GONE);            }        } else {            mCallName.setVisibility(View.GONE);            mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);            ///设置title格式            if (data.getType() == Notes.TYPE_FOLDER) {                mTitle.setText(data.getSnippet()                        + context.getString(R.string.format_folder_files_count,    data.getNotesCount()));                mAlert.setVisibility(View.GONE);            } else {                mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet()));                if (data.hasAlert()) {                    mAlert.setImageResource(R.drawable.clock);///设置图片来源                    mAlert.setVisibility(View.VISIBLE);                } else {                    mAlert.setVisibility(View.GONE);                }            }        }        ///设置内容,获取相关时间,从data里编辑的日期中获取        mTime. setText(DateUtils.getRelativeTimeSpanString(data.getModifiedDate()));               setBackground(data);    }    //根据data的文件属性来设置背景    private void setBackground(NoteItemData data) {        int id = data.getBgColorId();        //,若是note型文件,则4种情况,对于4种不同情况的背景来源        if (data.getType() == Notes.TYPE_NOTE) {        //单个数据并且只有一个子文件夹            if (data.isSingle() || data.isOneFollowingFolder()) {                setBackgroundResource(NoteItemBgResources.getNoteBgSingleRes(id));            } else if (data.isLast()) {//是最后一个数据                setBackgroundResource(NoteItemBgResources.getNoteBgLastRes(id));            } else if (data.isFirst() || data.isMultiFollowingFolder()) {//是一个数据并有多个子文件夹                setBackgroundResource(NoteItemBgResources.getNoteBgFirstRes(id));            } else {                setBackgroundResource(NoteItemBgResources.getNoteBgNormalRes(id));            }        } else {        //若不是note直接调用文件夹的背景来源            setBackgroundResource(NoteItemBgResources.getFolderBgRes());        }    }    public NoteItemData getItemData() {        return mItemData;    }}

14、NotesPreferenceActivity.java

package net.micode.notes.ui;import android.accounts.Account;import android.accounts.AccountManager;import android.app.ActionBar;import android.app.AlertDialog;import android.content.BroadcastReceiver;import android.content.ContentValues;import android.content.Context;import android.content.DialogInterface;import android.content.Intent;import android.content.IntentFilter;import android.content.SharedPreferences;import android.os.Bundle;import android.preference.Preference;import android.preference.Preference.OnPreferenceClickListener;import android.preference.PreferenceActivity;import android.preference.PreferenceCategory;import android.text.TextUtils;import android.text.format.DateFormat;import android.view.LayoutInflater;import android.view.Menu;import android.view.MenuItem;import android.view.View;import android.widget.Button;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.NoteColumns;import net.micode.notes.gtask.remote.GTaskSyncService;public class NotesPreferenceActivity extends PreferenceActivity {    public static final String PREFERENCE_NAME = "notes_preferences";  //优先名     public static final String PREFERENCE_SYNC_ACCOUNT_NAME = "pref_key_account_name";  //同步账号    public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time";   //同步时间    public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear";    private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key";   //同步密码    private static final String AUTHORITIES_FILTER_KEY = "authorities";   //本地密码    private PreferenceCategory mAccountCategory;     //账户分组    private GTaskReceiver mReceiver;  //同步任务接收器    private Account[] mOriAccounts;  //账户    private boolean mHasAddedAccount; //账户的hash标记    @Override        protected void onCreate(Bundle icicle) {    //先执行父类的创建函数        super.onCreate(icicle);                        getActionBar().setDisplayHomeAsUpEnabled(true);        //给左上角图标的左边加上一个返回的图标         addPreferencesFromResource(R.xml.preferences);        //添加xml来源并显示 xml        mAccountCategory = (PreferenceCategory) findPreference(PREFERENCE_SYNC_ACCOUNT_KEY);        //根据同步账户关键码来初始化分组        mReceiver = new GTaskReceiver();        IntentFilter filter = new IntentFilter();        filter.addAction(GTaskSyncService.GTASK_SERVICE_BROADCAST_NAME);        registerReceiver(mReceiver, filter);        //初始化同步组件        mOriAccounts = null;        View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null);        //获取listvivew,ListView的作用:用于列出所有选择         getListView().addHeaderView(header, null, true);        //在listview组件上方添加其他组件    }    @Override        protected void onResume() {    //先执行父类 的交互实现        super.onResume();        // need to set sync account automatically if user has added a new        // account        if (mHasAddedAccount) {        //若用户新加了账户则自动设置同步账户            Account[] accounts = getGoogleAccounts();            //获取google同步账户            if (mOriAccounts != null && accounts.length > mOriAccounts.length) {                //若原账户不为空且当前账户有增加            for (Account accountNew : accounts) {                    boolean found = false;                    for (Account accountOld : mOriAccounts) {                        if (TextUtils.equals(accountOld.name, accountNew.name)) {                        //更新账户found = true;break;                        }                    }                    if (!found) {                        setSyncAccount(accountNew.name);                        //若是没有找到旧的账户,那么同步账号中就只添加新账户                        break;                    }                }            }        }        refreshUI();        //刷新标签界面    }    @Override        protected void onDestroy() {        if (mReceiver != null) {            unregisterReceiver(mReceiver);            //注销接收器        }        super.onDestroy();        //执行父类的销毁动作    }        private void loadAccountPreference() {        mAccountCategory.removeAll();        //销毁所有的分组        Preference accountPref = new Preference(this);        //建立首选项        final String defaultAccount = getSyncAccountName(this);        accountPref.setTitle(getString(R.string.preferences_account_title));        accountPref.setSummary(getString(R.string.preferences_account_summary));        //设置首选项的大和小        accountPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {            public boolean onPreferenceClick(Preference preference) {            //建立监听器                if (!GTaskSyncService.isSyncing()) {                    if (TextUtils.isEmpty(defaultAccount)) {                        // the first time to set account                    //若是第一次建立账户显示选择账户提示对话框                        showSelectAccountAlertDialog();                    } else {                        // if the account has already been set, we need to promp                        // user about the risk                    //若是已经建立则显示修改对话框并进行修改操作                        showChangeAccountConfirmAlertDialog();                    }                } else {                //若在没有同步的情况下,则在toast中显示不能修改                    Toast.makeText(NotesPreferenceActivity.this,R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT).show();                }                return true;            }        });        //根据新建首选项编辑新的账户分组        mAccountCategory.addPreference(accountPref);    }        private void loadSyncButton() {        Button syncButton = (Button) findViewById(R.id.preference_sync_button);        TextView lastSyncTimeView = (TextView) findViewById(R.id.prefenerece_sync_status_textview);        //获取同步按钮控件和最终同步时间的的窗口        // set button state        //设置按钮的状态        if (GTaskSyncService.isSyncing()) {        //若是在同步状态下            syncButton.setText(getString(R.string.preferences_button_sync_cancel));            syncButton.setOnClickListener(new View.OnClickListener() {                public void onClick(View v) {                    GTaskSyncService.cancelSync(NotesPreferenceActivity.this);                }            });            //设置按钮显示的文本为“取消同步”以及监听器        } else {            syncButton.setText(getString(R.string.preferences_button_sync_immediately));            syncButton.setOnClickListener(new View.OnClickListener() {                public void onClick(View v) {                    GTaskSyncService.startSync(NotesPreferenceActivity.this);                }            });          //若是不同步则设置按钮显示的文本为“立即同步”以及对应监听器        }        syncButton.setEnabled(!TextUtils.isEmpty(getSyncAccountName(this)));        //设置按键可用还是不可用        // set last sync time        // 设置最终同步时间        if (GTaskSyncService.isSyncing()) {        //若是在同步的情况下            lastSyncTimeView.setText(GTaskSyncService.getProgressString());            lastSyncTimeView.setVisibility(View.VISIBLE);            // 根据当前同步服务器设置时间显示框的文本以及可见性        } else {        //若是非同步情况            long lastSyncTime = getLastSyncTime(this);            if (lastSyncTime != 0) {                lastSyncTimeView.setText(getString(R.string.preferences_last_sync_time,                        DateFormat.format(getString(R.string.preferences_last_sync_time_format),    lastSyncTime)));                lastSyncTimeView.setVisibility(View.VISIBLE);                //则根据最后同步时间的信息来编辑时间显示框的文本内容和可见性            } else {             //若时间为空直接设置为不可见状态                lastSyncTimeView.setVisibility(View.GONE);            }        }    }         private void refreshUI() {        loadAccountPreference();        loadSyncButton();    }        private void showSelectAccountAlertDialog() {        AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);        //创建一个新的对话框        View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);        TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);        titleTextView.setText(getString(R.string.preferences_dialog_select_account_title));        TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle);        subtitleTextView.setText(getString(R.string.preferences_dialog_select_account_tips));        //设置以及子的内容        dialogBuilder.setCustomTitle(titleView);        dialogBuilder.setPositiveButton(null, null);        //设置对话框的自定义,建立一个YES的按钮        Account[] accounts = getGoogleAccounts();        String defAccount = getSyncAccountName(this);        //获取同步账户信息        mOriAccounts = accounts;        mHasAddedAccount = false;        if (accounts.length > 0) {        //若账户不为空            CharSequence[] items = new CharSequence[accounts.length];            final CharSequence[] itemMapping = items;            int checkedItem = -1;            int index = 0;            for (Account account : accounts) {                if (TextUtils.equals(account.name, defAccount)) {                    checkedItem = index;                    //在账户列表中查询到所需账户                }                items[index++] = account.name;            }            dialogBuilder.setSingleChoiceItems(items, checkedItem,            //在对话框建立一个单选的复选框                    new DialogInterface.OnClickListener() {                        public void onClick(DialogInterface dialog, int which) {setSyncAccount(itemMapping[which].toString());dialog.dismiss();//取消对话框refreshUI();                        }                        //设置点击后执行的事件,包括检录新同步账户和刷新标签界面                    });            //建立对话框网络版的监听器        }        View addAccountView = LayoutInflater.from(this).inflate(R.layout.add_account_text, null);        dialogBuilder.setView(addAccountView);        //给新加账户对话框设置自定义样式        final AlertDialog dialog = dialogBuilder.show();        //显示对话框        addAccountView.setOnClickListener(new View.OnClickListener() {            public void onClick(View v) {                mHasAddedAccount = true;                //将新加账户的hash置true                Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS");                //建立网络建立组件                intent.putExtra(AUTHORITIES_FILTER_KEY, new String[] {                    "gmail-ls"                });                startActivityForResult(intent, -1);                //跳回上一个选项                dialog.dismiss();            }        });        //建立新加账户对话框的监听器    }        private void showChangeAccountConfirmAlertDialog() {        AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);        //创建一个新的对话框        View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);        TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);        titleTextView.setText(getString(R.string.preferences_dialog_change_account_title,                getSyncAccountName(this)));        TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle);        subtitleTextView.setText(getString(R.string.preferences_dialog_change_account_warn_msg));        //根据同步修改的账户信息设置以及子的内容        dialogBuilder.setCustomTitle(titleView);      //设置对话框的自定义        CharSequence[] menuItemArray = new CharSequence[] {                getString(R.string.preferences_menu_change_account),                getString(R.string.preferences_menu_remove_account),                getString(R.string.preferences_menu_cancel)        };        //定义一些标记字符串        dialogBuilder.setItems(menuItemArray, new DialogInterface.OnClickListener() {        //设置对话框要显示的一个list,用于显示几个命令时,即change,remove,cancel            public void onClick(DialogInterface dialog, int which) {            //按键功能,由which来决定                if (which == 0) {                //进入账户选择对话框                    showSelectAccountAlertDialog();                } else if (which == 1) {                //删除账户并且跟新便签界面                    removeSyncAccount();                    refreshUI();                }            }        });        dialogBuilder.show();        //显示对话框    }        private Account[] getGoogleAccounts() {        AccountManager accountManager = AccountManager.get(this);        return accountManager.getAccountsByType("com.google");    }        private void setSyncAccount(String account) {        if (!getSyncAccountName(this).equals(account)) {        //假如该账号不在同步账号列表中            SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);            SharedPreferences.Editor editor = settings.edit();            //编辑共享的首选项            if (account != null) {                editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, account);            } else {                editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, "");            }            //将该账号加入到首选项中                        editor.commit();            //提交修改的数据                        setLastSyncTime(this, 0);          //将最后同步时间清零            // clean up local gtask related info            new Thread(new Runnable() {                public void run() {                    ContentValues values = new ContentValues();                    values.put(NoteColumns.GTASK_ID, "");                    values.put(NoteColumns.SYNC_ID, 0);                    getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null);                }            }).start();            //重置当地同步任务的信息            Toast.makeText(NotesPreferenceActivity.this,                    getString(R.string.preferences_toast_success_set_accout, account),                    Toast.LENGTH_SHORT).show();            //将toast的文本信息置为“设置账户成功”并显示出来        }    }        private void removeSyncAccount() {        SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);        SharedPreferences.Editor editor = settings.edit();        //设置共享首选项                if (settings.contains(PREFERENCE_SYNC_ACCOUNT_NAME)) {            editor.remove(PREFERENCE_SYNC_ACCOUNT_NAME);            //假如当前首选项中有账户就删除        }        if (settings.contains(PREFERENCE_LAST_SYNC_TIME)) {            editor.remove(PREFERENCE_LAST_SYNC_TIME);            //删除当前首选项中有账户时间        }        editor.commit();        //提交更新后的数据                // clean up local gtask related info        new Thread(new Runnable() {            public void run() {                ContentValues values = new ContentValues();                values.put(NoteColumns.GTASK_ID, "");                values.put(NoteColumns.SYNC_ID, 0);                getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null);            }        }).start();      //重置当地同步任务的信息    }            public static String getSyncAccountName(Context context) {        SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,                Context.MODE_PRIVATE);        return settings.getString(PREFERENCE_SYNC_ACCOUNT_NAME, "");    }        public static void setLastSyncTime(Context context, long time) {        SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,                Context.MODE_PRIVATE);        SharedPreferences.Editor editor = settings.edit();        // 从共享首选项中找到相关账户并获取其编辑器        editor.putLong(PREFERENCE_LAST_SYNC_TIME, time);        editor.commit();        //编辑最终同步时间并提交更新    }        public static long getLastSyncTime(Context context) {        SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,                Context.MODE_PRIVATE);        return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0);    }        private class GTaskReceiver extends BroadcastReceiver {        @Override        public void onReceive(Context context, Intent intent) {            refreshUI();            if (intent.getBooleanExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_IS_SYNCING, false)) {                //获取随广播而来的Intent中的同步服务的数据            TextView syncStatus = (TextView) findViewById(R.id.prefenerece_sync_status_textview);                syncStatus.setText(intent                        .getStringExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_PROGRESS_MSG));                //通过获取的数据在设置系统的状态            }        }    }        public boolean onOptionsItemSelected(MenuItem item) {        switch (item.getItemId()) {        //根据选项的id选择,这里只有一个主页            case android.R.id.home:                Intent intent = new Intent(this, NotesListActivity.class);                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);                startActivity(intent);                return true;                //在主页情况下在创建连接组件intent,发出清空的信号并开始一个相应的activity            default:                return false;        }    }}

来源地址:https://blog.csdn.net/edgarrr/article/details/127589226

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯