小米便签源码分析——UI包
Posted edgarrrr
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了小米便签源码分析——UI包相关的知识,希望对你有一定的参考价值。
目录
14、NotesPreferenceActivity.java
1、AlarmAlertActivity.java
package net.micode.notes.ui;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.DialogInterface.OnDismissListener;
import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.PowerManager;
import android.provider.Settings;
import android.view.Window;
import android.view.WindowManager;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.DataUtils;
import java.io.IOException;
public class AlarmAlertActivity extends Activity implements OnClickListener, OnDismissListener
private long mNoteId; //文本在数据库存储中的ID号
private String mSnippet; //闹钟提示时出现的文本片段
private static final int SNIPPET_PREW_MAX_LEN = 60;
MediaPlayer mPlayer;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
//Bundle类型的数据与Map类型的数据相似,都是以key-value的形式存储数据的
//onsaveInstanceState方法是用来保存Activity的状态的
//能从onCreate的参数savedInsanceState中获得状态数据
requestWindowFeature(Window.FEATURE_NO_TITLE);
//界面显示——无标题
final Window win = getWindow();
win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
if (!isScreenOn())
win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
//保持窗体点亮
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
//将窗体点亮
| WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
//允许窗体点亮时锁屏
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
//在手机锁屏后如果到了闹钟提示时间,点亮屏幕
Intent intent = getIntent();
try
mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1));
mSnippet = DataUtils.getSnippetById(this.getContentResolver(), mNoteId);
//根据ID从数据库中获取标签的内容;
//getContentResolver()是实现数据共享,实例存储。
mSnippet = mSnippet.length() > SNIPPET_PREW_MAX_LEN ? mSnippet.substring(0,
SNIPPET_PREW_MAX_LEN) + getResources().getString(R.string.notelist_string_info)
: mSnippet;
//判断标签片段是否达到符合长度
catch (IllegalArgumentException e)
e.printStackTrace();
return;
/*
try
// 代码区
catch(Exception e)
// 异常处理
代码区如果有错误,就会返回所写异常的处理。*/
mPlayer = new MediaPlayer();
if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId, Notes.TYPE_NOTE))
showActionDialog();
//弹出对话框
playAlarmSound();
//闹钟提示音激发
else
finish();
//完成闹钟动作
private boolean isScreenOn()
//判断屏幕是否锁屏,调用系统函数判断,最后返回值是布尔类型
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
return pm.isScreenOn();
private void playAlarmSound()
//闹钟提示音激发
Uri url = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM);
//调用系统的铃声管理URI,得到闹钟提示音
int silentModeStreams = Settings.System.getInt(getContentResolver(),
Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0);
if ((silentModeStreams & (1 << AudioManager.STREAM_ALARM)) != 0)
mPlayer.setAudiostreamType(silentModeStreams);
else
mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
try
mPlayer.setDataSource(this, url);
//方法:setDataSource(Context context, Uri uri)
//解释:无返回值,设置多媒体数据来源【根据 Uri】
mPlayer.prepare();
//准备同步
mPlayer.setLooping(true);
//设置是否循环播放
mPlayer.start();
//开始播放
catch (IllegalArgumentException e)
// TODO Auto-generated catch block
e.printStackTrace();
//e.printStackTrace()函数功能是抛出异常, 还将显示出更深的调用信息
//System.out.println(e),这个方法打印出异常,并且输出在哪里出现的异常
catch (SecurityException e)
// TODO Auto-generated catch block
e.printStackTrace();
catch (IllegalStateException e)
// TODO Auto-generated catch block
e.printStackTrace();
catch (IOException e)
// TODO Auto-generated catch block
e.printStackTrace();
private void showActionDialog()
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
//AlertDialog的构造方法全部是Protected的
//所以不能直接通过new一个AlertDialog来创建出一个AlertDialog。
//要创建一个AlertDialog,就要用到AlertDialog.Builder中的create()方法
//如这里的dialog就是新建了一个AlertDialog
dialog.setTitle(R.string.app_name);
//为对话框设置标题
dialog.setMessage(mSnippet);
//为对话框设置内容
dialog.setPositiveButton(R.string.notealert_ok, this);
//给对话框添加"Yes"按钮
if (isScreenOn())
dialog.setNegativeButton(R.string.notealert_enter, this);
//对话框添加"No"按钮
dialog.show().setOnDismissListener(this);
public void onClick(DialogInterface dialog, int which)
switch (which)
//用which来选择click后下一步的操作
case DialogInterface.BUTTON_NEGATIVE:
//这是取消操作
Intent intent = new Intent(this, NoteEditActivity.class);
//实现两个类间的数据传输
intent.setAction(Intent.ACTION_VIEW);
//设置动作属性
intent.putExtra(Intent.EXTRA_UID, mNoteId);
//实现key-value对
//EXTRA_UID为key;mNoteId为键
startActivity(intent);
//开始动作
break;
default:
//这是确定操作
break;
public void onDismiss(DialogInterface dialog)
//忽略
stopAlarmSound();
//停止闹钟声音
finish();
//完成该动作
private void stopAlarmSound()
if (mPlayer != null)
mPlayer.stop();
//停止播放
mPlayer.release();
//释放MediaPlayer对象
mPlayer = null;
2、AlarmInitReceiver.java
package net.micode.notes.ui;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
public class AlarmInitReceiver extends BroadcastReceiver
private static final String [] PROJECTION = new String []
NoteColumns.ID,
NoteColumns.ALERTED_DATE
;
//对数据库的操作,调用标签ID和闹钟时间
private static final int COLUMN_ID = 0;
private static final int COLUMN_ALERTED_DATE = 1;
@Override
public void onReceive(Context context, Intent intent)
long currentDate = System.currentTimeMillis();
//System.currentTimeMillis()产生一个当前的毫秒
//这个毫秒其实就是自1970年1月1日0时起的毫秒数
Cursor c = context.getContentResolver().query(Notes.CONTENT_NOTE_URI,
PROJECTION,
NoteColumns.ALERTED_DATE + ">? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE,
new String[] String.valueOf(currentDate) ,
//将long变量currentDate转化为字符串
null);
//Cursor在这里的作用是通过查找数据库中的标签内容,找到和当前系统时间相等的标签
if (c != null)
if (c.moveToFirst())
do
long alertDate = c.getLong(COLUMN_ALERTED_DATE);
Intent sender = new Intent(context, AlarmReceiver.class);
sender.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, c.getLong(COLUMN_ID)));
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, sender, 0);
AlarmManager alermManager = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
alermManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent);
while (c.moveToNext());
c.close();
//然而通过网上查找资料发现,对于闹钟机制的启动,通常需要上面的几个步骤
//如新建Intent、PendingIntent以及AlarmManager等
//这里就是根据数据库里的闹钟时间创建一个闹钟机制
3、AlarmReceiver.java
package net.micode.notes.ui;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class AlarmReceiver extends BroadcastReceiver
@Override
public void onReceive(Context context, Intent intent)
intent.setClass(context, AlarmAlertActivity.class);
//启动AlarmAlertActivity
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//activity要存在于activity的栈中,而非activity的途径启动activity时必然不存在一个activity的栈
//所以要新起一个栈装入启动的activity
context.startActivity(intent);
//这是实现alarm这个功能最接近用户层的包,基于上面的两个包,
//作用还需要深究但是对于setClass和addFlags的
4、DateTimePicker.java
package net.micode.notes.ui;
import java.text.DateFormatSymbols;
import java.util.Calendar;
import net.micode.notes.R;
import android.content.Context;
import android.text.format.DateFormat;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.NumberPicker;
public class DateTimePicker extends FrameLayout
//FrameLayout是布局模板之一
//所有的子元素全部在屏幕的右上方
private static final boolean DEFAULT_ENABLE_STATE = true;
private static final int HOURS_IN_HALF_DAY = 12;
private static final int HOURS_IN_ALL_DAY = 24;
private static final int DAYS_IN_ALL_WEEK = 7;
private static final int DATE_SPINNER_MIN_VAL = 0;
private static final int DATE_SPINNER_MAX_VAL = DAYS_IN_ALL_WEEK - 1;
private static final int HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW = 0;
private static final int HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW = 23;
private static final int HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW = 1;
private static final int HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW = 12;
private static final int MINUT_SPINNER_MIN_VAL = 0;
private static final int MINUT_SPINNER_MAX_VAL = 59;
private static final int AMPM_SPINNER_MIN_VAL = 0;
private static final int AMPM_SPINNER_MAX_VAL = 1;
//初始化控件
private final NumberPicker mDateSpinner;
private final NumberPicker mHourSpinner;
private final NumberPicker mMinuteSpinner;
private final NumberPicker mAmPmSpinner;
//NumberPicker是数字选择器
//这里定义的四个变量全部是在设置闹钟时需要选择的变量(如日期、时、分、上午或者下午)
private Calendar mDate;
//定义了Calendar类型的变量mDate,用于操作时间
private String[] mDateDisplayValues = new String[DAYS_IN_ALL_WEEK];
private boolean mIsAm;
private boolean mIs24HourView;
private boolean mIsEnabled = DEFAULT_ENABLE_STATE;
private boolean mInitialising;
private OnDateTimeChangedListener mOnDateTimeChangedListener;
private NumberPicker.OnValueChangeListener mOnDateChangedListener = new NumberPicker.OnValueChangeListener()
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal)
mDate.add(Calendar.DAY_OF_YEAR, newVal - oldVal);
updateDateControl();
onDateTimeChanged();
;//OnValueChangeListener,这是时间改变监听器,这里主要是对日期的监听
//将现在日期的值传递给mDate;updateDateControl是同步操作
private NumberPicker.OnValueChangeListener mOnHourChangedListener = new NumberPicker.OnValueChangeListener()
//这里是对 小时(Hour) 的监听
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal)
boolean isDateChanged = false;
Calendar cal = Calendar.getInstance();
//声明一个Calendar的变量cal,便于后续的操作
if (!mIs24HourView)
if (!mIsAm && oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY)
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, 1);
isDateChanged = true;
//这里是对于12小时制时,晚上11点和12点交替时对日期的更改
else if (mIsAm && oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1)
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, -1);
isDateChanged = true;
//这里是对于12小时制时,凌晨11点和12点交替时对日期的更改
if (oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY ||
oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1)
mIsAm = !mIsAm;
updateAmPmControl();
//这里是对于12小时制时,中午11点和12点交替时对AM和PM的更改
else
if (oldVal == HOURS_IN_ALL_DAY - 1 && newVal == 0)
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, 1);
isDateChanged = true;
//这里是对于24小时制时,晚上11点和12点交替时对日期的更改
else if (oldVal == 0 && newVal == HOURS_IN_ALL_DAY - 1)
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, -1);
isDateChanged = true;
//这里是对于12小时制时,凌晨11点和12点交替时对日期的更改
int newHour = mHourSpinner.getValue() % HOURS_IN_HALF_DAY + (mIsAm ? 0 : HOURS_IN_HALF_DAY);
//通过数字选择器对newHour的赋值
mDate.set(Calendar.HOUR_OF_DAY, newHour);
//通过set函数将新的Hour值传给mDate
onDateTimeChanged();
if (isDateChanged)
setCurrentYear(cal.get(Calendar.YEAR));
setCurrentMonth(cal.get(Calendar.MONTH));
setCurrentDay(cal.get(Calendar.DAY_OF_MONTH));
;
private NumberPicker.OnValueChangeListener mOnMinuteChangedListener = new NumberPicker.OnValueChangeListener()
@Override
//这里是对 分钟(Minute)改变的监听
public void onValueChange(NumberPicker picker, int oldVal, int newVal)
int minValue = mMinuteSpinner.getMinValue();
int maxValue = mMinuteSpinner.getMaxValue();
int offset = 0;
//设置offset,作为小时改变的一个记录数据
if (oldVal == maxValue && newVal == minValue)
offset += 1;
else if (oldVal == minValue && newVal == maxValue)
offset -= 1;
//如果原值为59,新值为0,则offset加1
//如果原值为0,新值为59,则offset减1
if (offset != 0)
mDate.add(Calendar.HOUR_OF_DAY, offset);
mHourSpinner.setValue(getCurrentHour());
updateDateControl();
int newHour = getCurrentHourOfDay();
if (newHour >= HOURS_IN_HALF_DAY)
mIsAm = false;
updateAmPmControl();
else
mIsAm = true;
updateAmPmControl();
mDate.set(Calendar.MINUTE, newVal);
onDateTimeChanged();
;
private NumberPicker.OnValueChangeListener mOnAmPmChangedListener = new NumberPicker.OnValueChangeListener()
//对AM和PM的监听
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal)
mIsAm = !mIsAm;
if (mIsAm)
mDate.add(Calendar.HOUR_OF_DAY, -HOURS_IN_HALF_DAY);
else
mDate.add(Calendar.HOUR_OF_DAY, HOURS_IN_HALF_DAY);
updateAmPmControl();
onDateTimeChanged();
;
public interface OnDateTimeChangedListener
void onDateTimeChanged(DateTimePicker view, int year, int month,
int dayOfMonth, int hourOfDay, int minute);
public DateTimePicker(Context context)
this(context, System.currentTimeMillis());
//通过对数据库的访问,获取当前的系统时间
public DateTimePicker(Context context, long date)
this(context, date, DateFormat.is24HourFormat(context));
//上面函数的得到的是一个天文数字(1970至今的秒数),需要DateFormat将其变得有意义
public DateTimePicker(Context context, long date, boolean is24HourView)
super(context);
//获取系统时间
mDate = Calendar.getInstance();
mInitialising = true;
mIsAm = getCurrentHourOfDay() >= HOURS_IN_HALF_DAY;
inflate(context, R.layout.datetime_picker, this);
//如果当前Activity里用到别的layout,比如对话框layout
//还要设置这个layout上的其他组件的内容,就必须用inflate()方法先将对话框的layout找出来
//然后再用findViewById()找到它上面的其它组件
mDateSpinner = (NumberPicker) findViewById(R.id.date);
mDateSpinner.setMinValue(DATE_SPINNER_MIN_VAL);
mDateSpinner.setMaxValue(DATE_SPINNER_MAX_VAL);
mDateSpinner.setOnValueChangedListener(mOnDateChangedListener);
mHourSpinner = (NumberPicker) findViewById(R.id.hour);
mHourSpinner.setOnValueChangedListener(mOnHourChangedListener);
mMinuteSpinner = (NumberPicker) findViewById(R.id.minute);
mMinuteSpinner.setMinValue(MINUT_SPINNER_MIN_VAL);
mMinuteSpinner.setMaxValue(MINUT_SPINNER_MAX_VAL);
mMinuteSpinner.setOnLongPressUpdateInterval(100);
mMinuteSpinner.setOnValueChangedListener(mOnMinuteChangedListener);
String[] stringsForAmPm = new DateFormatSymbols().getAmPmStrings();
mAmPmSpinner = (NumberPicker) findViewById(R.id.amPm);
mAmPmSpinner.setMinValue(AMPM_SPINNER_MIN_VAL);
mAmPmSpinner.setMaxValue(AMPM_SPINNER_MAX_VAL);
mAmPmSpinner.setDisplayedValues(stringsForAmPm);
mAmPmSpinner.setOnValueChangedListener(mOnAmPmChangedListener);
// update controls to initial state
updateDateControl();
updateHourControl();
updateAmPmControl();
set24HourView(is24HourView);
// set to current time
setCurrentDate(date);
setEnabled(isEnabled());
// set the content descriptions
mInitialising = false;
@Override
public void setEnabled(boolean enabled)
if (mIsEnabled == enabled)
return;
super.setEnabled(enabled);
mDateSpinner.setEnabled(enabled);
mMinuteSpinner.setEnabled(enabled);
mHourSpinner.setEnabled(enabled);
mAmPmSpinner.setEnabled(enabled);
mIsEnabled = enabled;
//存在疑问!!!!!!!!!!!!!setEnabled的作用
//下面的代码通过原程序的注释已经比较清晰,另外可以通过函数名来判断
//下面的各函数主要是对上面代码引用到的各函数功能的实现
@Override
public boolean isEnabled()
return mIsEnabled;
/**
* Get the current date in millis
*
* @return the current date in millis
*/
public long getCurrentDateInTimeMillis()
return mDate.getTimeInMillis();
//实现函数——得到当前的秒数
/**
* Set the current date
*
* @param date The current date in millis
*/
public void setCurrentDate(long date)
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(date);
setCurrentDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH),
cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE));
//实现函数功能——设置当前的时间,参数是date
/**
* Set the current date
*
* @param year The current year
* @param month The current month
* @param dayOfMonth The current dayOfMonth
* @param hourOfDay The current hourOfDay
* @param minute The current minute
*/
public void setCurrentDate(int year, int month,
int dayOfMonth, int hourOfDay, int minute)
setCurrentYear(year);
setCurrentMonth(month);
setCurrentDay(dayOfMonth);
setCurrentHour(hourOfDay);
setCurrentMinute(minute);
//实现函数功能——设置当前的时间,参数是各详细的变量
/**
* Get current year
*
* @return The current year
*/
//下面是得到year、month、day等值
public int getCurrentYear()
return mDate.get(Calendar.YEAR);
/**
* Set current year
*
* @param year The current year
*/
public void setCurrentYear(int year)
if (!mInitialising && year == getCurrentYear())
return;
mDate.set(Calendar.YEAR, year);
updateDateControl();
onDateTimeChanged();
/**
* Get current month in the year
*
* @return The current month in the year
*/
public int getCurrentMonth()
return mDate.get(Calendar.MONTH);
/**
* Set current month in the year
*
* @param month The month in the year
*/
public void setCurrentMonth(int month)
if (!mInitialising && month == getCurrentMonth())
return;
mDate.set(Calendar.MONTH, month);
updateDateControl();
onDateTimeChanged();
/**
* Get current day of the month
*
* @return The day of the month
*/
public int getCurrentDay()
return mDate.get(Calendar.DAY_OF_MONTH);
/**
* Set current day of the month
*
* @param dayOfMonth The day of the month
*/
public void setCurrentDay(int dayOfMonth)
if (!mInitialising && dayOfMonth == getCurrentDay())
return;
mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
updateDateControl();
onDateTimeChanged();
/**
* Get current hour in 24 hour mode, in the range (0~23)
* @return The current hour in 24 hour mode
*/
public int getCurrentHourOfDay()
return mDate.get(Calendar.HOUR_OF_DAY);
private int getCurrentHour()
if (mIs24HourView)
return getCurrentHourOfDay();
else
int hour = getCurrentHourOfDay();
if (hour > HOURS_IN_HALF_DAY)
return hour - HOURS_IN_HALF_DAY;
else
return hour == 0 ? HOURS_IN_HALF_DAY : hour;
/**
* Set current hour in 24 hour mode, in the range (0~23)
*
* @param hourOfDay
*/
public void setCurrentHour(int hourOfDay)
if (!mInitialising && hourOfDay == getCurrentHourOfDay())
return;
mDate.set(Calendar.HOUR_OF_DAY, hourOfDay);
if (!mIs24HourView)
if (hourOfDay >= HOURS_IN_HALF_DAY)
mIsAm = false;
if (hourOfDay > HOURS_IN_HALF_DAY)
hourOfDay -= HOURS_IN_HALF_DAY;
else
mIsAm = true;
if (hourOfDay == 0)
hourOfDay = HOURS_IN_HALF_DAY;
updateAmPmControl();
mHourSpinner.setValue(hourOfDay);
onDateTimeChanged();
/**
* Get currentMinute
*
* @return The Current Minute
*/
public int getCurrentMinute()
return mDate.get(Calendar.MINUTE);
/**
* Set current minute
*/
public void setCurrentMinute(int minute)
if (!mInitialising && minute == getCurrentMinute())
return;
mMinuteSpinner.setValue(minute);
mDate.set(Calendar.MINUTE, minute);
onDateTimeChanged();
/**
* @return true if this is in 24 hour view else false.
*/
public boolean is24HourView ()
return mIs24HourView;
/**
* Set whether in 24 hour or AM/PM mode.
*
* @param is24HourView True for 24 hour mode. False for AM/PM mode.
*/
public void set24HourView(boolean is24HourView)
if (mIs24HourView == is24HourView)
return;
mIs24HourView = is24HourView;
mAmPmSpinner.setVisibility(is24HourView ? View.GONE : View.VISIBLE);
int hour = getCurrentHourOfDay();
updateHourControl();
setCurrentHour(hour);
updateAmPmControl();
private void updateDateControl()
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, -DAYS_IN_ALL_WEEK / 2 - 1);
mDateSpinner.setDisplayedValues(null);
for (int i = 0; i < DAYS_IN_ALL_WEEK; ++i)
cal.add(Calendar.DAY_OF_YEAR, 1);
mDateDisplayValues[i] = (String) DateFormat.format("MM.dd EEEE", cal);
mDateSpinner.setDisplayedValues(mDateDisplayValues);
mDateSpinner.setValue(DAYS_IN_ALL_WEEK / 2);
mDateSpinner.invalidate();
// 对于星期几的算法
private void updateAmPmControl()
if (mIs24HourView)
mAmPmSpinner.setVisibility(View.GONE);
else
int index = mIsAm ? Calendar.AM : Calendar.PM;
mAmPmSpinner.setValue(index);
mAmPmSpinner.setVisibility(View.VISIBLE);
// 对于上下午操作的算法
private void updateHourControl()
if (mIs24HourView)
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW);
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW);
else
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW);
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW);
// 对与小时的算法
/**
* Set the callback that indicates the 'Set' button has been pressed.
* @param callback the callback, if null will do nothing
*/
public void setOnDateTimeChangedListener(OnDateTimeChangedListener callback)
mOnDateTimeChangedListener = callback;
private void onDateTimeChanged()
if (mOnDateTimeChangedListener != null)
mOnDateTimeChangedListener.onDateTimeChanged(this, getCurrentYear(),
getCurrentMonth(), getCurrentDay(), getCurrentHourOfDay(), getCurrentMinute());
5、DateTimePickerDialog.java
package net.micode.notes.ui;
import java.util.Calendar;
import net.micode.notes.R;
import net.micode.notes.ui.DateTimePicker;
import net.micode.notes.ui.DateTimePicker.OnDateTimeChangedListener;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.text.format.DateFormat;
import android.text.format.DateUtils;
public class DateTimePickerDialog extends AlertDialog implements OnClickListener
private Calendar mDate = Calendar.getInstance();
//创建一个Calendar类型的变量 mDate,方便时间的操作
private boolean mIs24HourView;
private OnDateTimeSetListener mOnDateTimeSetListener;
//声明一个时间日期滚动选择控件 mOnDateTimeSetListener
private DateTimePicker mDateTimePicker;
//DateTimePicker控件,控件一般用于让用户可以从日期列表中选择单个值。
//运行时,单击控件边上的下拉箭头,会显示为两个部分:一个下拉列表,一个用于选择日期的
public interface OnDateTimeSetListener
void OnDateTimeSet(AlertDialog dialog, long date);
public DateTimePickerDialog(Context context, long date)
//对该界面对话框的实例化
super(context);
//对数据库的操作
mDateTimePicker = new DateTimePicker(context);
setView(mDateTimePicker);
//添加一个子视图
mDateTimePicker.setOnDateTimeChangedListener(new OnDateTimeChangedListener()
public void onDateTimeChanged(DateTimePicker view, int year, int month,
int dayOfMonth, int hourOfDay, int minute)
mDate.set(Calendar.YEAR, year);
mDate.set(Calendar.MONTH, month);
mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
mDate.set(Calendar.HOUR_OF_DAY, hourOfDay);
mDate.set(Calendar.MINUTE, minute);
//将视图中的各选项设置为系统当前时间
updateTitle(mDate.getTimeInMillis());
);
mDate.setTimeInMillis(date);
//得到系统时间
mDate.set(Calendar.SECOND, 0);
//将秒数设置为0
mDateTimePicker.setCurrentDate(mDate.getTimeInMillis());
setButton(context.getString(R.string.datetime_dialog_ok), this);
setButton2(context.getString(R.string.datetime_dialog_cancel), (OnClickListener)null);
//设置按钮
set24HourView(DateFormat.is24HourFormat(this.getContext()));
//时间标准化打印
updateTitle(mDate.getTimeInMillis());
public void set24HourView(boolean is24HourView)
mIs24HourView = is24HourView;
public void setOnDateTimeSetListener(OnDateTimeSetListener callBack)
mOnDateTimeSetListener = callBack;
//将时间日期滚动选择控件实例化
private void updateTitle(long date)
int flag =
DateUtils.FORMAT_SHOW_YEAR |
DateUtils.FORMAT_SHOW_DATE |
DateUtils.FORMAT_SHOW_TIME;
flag |= mIs24HourView ? DateUtils.FORMAT_24HOUR : DateUtils.FORMAT_24HOUR;
setTitle(DateUtils.formatDateTime(this.getContext(), date, flag));
//android开发中常见日期管理工具类(API)——DateUtils:按照上下午显示时间
public void onClick(DialogInterface arg0, int arg1)
if (mOnDateTimeSetListener != null)
mOnDateTimeSetListener.OnDateTimeSet(this, mDate.getTimeInMillis());
//第一个参数arg0是接收到点击事件的对话框
//第二个参数arg1是该对话框上的按钮
6、DropdownMenu.java
package net.micode.notes.ui;
import android.content.Context;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.PopupMenu;
import android.widget.PopupMenu.OnMenuItemClickListener;
import net.micode.notes.R;
public class DropdownMenu
private Button mButton;
private PopupMenu mPopupMenu;
//声明一个下拉菜单
private Menu mMenu;
public DropdownMenu(Context context, Button button, int menuId)
mButton = button;
mButton.setBackgroundResource(R.drawable.dropdown_icon);
//设置这个view的背景
mPopupMenu = new PopupMenu(context, mButton);
mMenu = mPopupMenu.getMenu();
mPopupMenu.getMenuInflater().inflate(menuId, mMenu);
//MenuInflater是用来实例化Menu目录下的Menu布局文件
//根据ID来确认menu的内容选项
mButton.setOnClickListener(new OnClickListener()
public void onClick(View v)
mPopupMenu.show();
);
public void setOnDropdownMenuItemClickListener(OnMenuItemClickListener listener)
if (mPopupMenu != null)
mPopupMenu.setOnMenuItemClickListener(listener);
//设置菜单的监听
public MenuItem findItem(int id)
return mMenu.findItem(id);
//对于菜单选项的初始化,根据索引搜索菜单需要的选项
public void setTitle(CharSequence title)
mButton.setText(title);
//布局文件,设置标题
7、FoldersListAdapter.java
package net.micode.notes.ui;
import android.content.Context;
import android.database.Cursor;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.LinearLayout;
import android.widget.TextView;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
public class FoldersListAdapter extends CursorAdapter
//CursorAdapter是Cursor和ListView的接口
//FoldersListAdapter继承了CursorAdapter的类
//主要作用是便签数据库和用户的交互
//这里就是用folder(文件夹)的形式展现给用户
public static final String [] PROJECTION =
NoteColumns.ID,
NoteColumns.SNIPPET
;//调用数据库中便签的ID和片段
public static final int ID_COLUMN = 0;
public static final int NAME_COLUMN = 1;
public FoldersListAdapter(Context context, Cursor c)
super(context, c);
// TODO Auto-generated constructor stub
//数据库操作
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent)
//ViewGroup是容器
return new FolderListItem(context);
//创建一个文件夹,对于各文件夹中子标签的初始化
@Override
public void bindView(View view, Context context, Cursor cursor)
if (view instanceof FolderListItem)
String folderName = (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context
.getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN);
((FolderListItem) view).bind(folderName);
//将各个布局文件绑定起来
public String getFolderName(Context context, int position)
Cursor cursor = (Cursor) getItem(position);
return (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context
.getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN);
//根据数据库中标签的ID得到标签的各项内容
private class FolderListItem extends LinearLayout
private TextView mName;
public FolderListItem(Context context)
super(context);
//操作数据库
inflate(context, R.layout.folder_list_item, this);
//根据布局文件的名字等信息将其找出来
mName = (TextView) findViewById(R.id.tv_folder_name);
public void bind(String name)
mName.setText(name);
8、NoteEditActivity.java
package net.micode.notes.ui;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.app.SearchManager;
import android.appwidget.AppWidgetManager;
import android.content.ContentUris;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Paint;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.text.style.BackgroundColorSpan;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.TextNote;
import net.micode.notes.model.WorkingNote;
import net.micode.notes.model.WorkingNote.NoteSettingChangedListener;
import net.micode.notes.tool.DataUtils;
import net.micode.notes.tool.ResourceParser;
import net.micode.notes.tool.ResourceParser.TextAppearanceResources;
import net.micode.notes.ui.DateTimePickerDialog.OnDateTimeSetListener;
import net.micode.notes.ui.NoteEditText.OnTextViewChangeListener;
import net.micode.notes.widget.NoteWidgetProvider_2x;
import net.micode.notes.widget.NoteWidgetProvider_4x;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class NoteEditActivity extends Activity implements OnClickListener,
NoteSettingChangedListener, OnTextViewChangeListener
//该类主要是针对标签的编辑
//继承了系统内部许多和监听有关的类
private class HeadViewHolder
public TextView tvModified;
public ImageView ivAlertIcon;
public TextView tvAlertDate;
public ImageView ibSetBgColor;
//使用Map实现数据存储
private static final Map<Integer, Integer> sBgSelectorBtnsMap = new HashMap<Integer, Integer>();
static
sBgSelectorBtnsMap.put(R.id.iv_bg_yellow, ResourceParser.YELLOW);
sBgSelectorBtnsMap.put(R.id.iv_bg_red, ResourceParser.RED);
sBgSelectorBtnsMap.put(R.id.iv_bg_blue, ResourceParser.BLUE);
sBgSelectorBtnsMap.put(R.id.iv_bg_green, ResourceParser.GREEN);
sBgSelectorBtnsMap.put(R.id.iv_bg_white, ResourceParser.WHITE);
//put函数是将指定值和指定键相连
private static final Map<Integer, Integer> sBgSelectorSelectionMap = new HashMap<Integer, Integer>();
static
sBgSelectorSelectionMap.put(ResourceParser.YELLOW, R.id.iv_bg_yellow_select);
sBgSelectorSelectionMap.put(ResourceParser.RED, R.id.iv_bg_red_select);
sBgSelectorSelectionMap.put(ResourceParser.BLUE, R.id.iv_bg_blue_select);
sBgSelectorSelectionMap.put(ResourceParser.GREEN, R.id.iv_bg_green_select);
sBgSelectorSelectionMap.put(ResourceParser.WHITE, R.id.iv_bg_white_select);
//put函数是将指定值和指定键相连
private static final Map<Integer, Integer> sFontSizeBtnsMap = new HashMap<Integer, Integer>();
static
sFontSizeBtnsMap.put(R.id.ll_font_large, ResourceParser.TEXT_LARGE);
sFontSizeBtnsMap.put(R.id.ll_font_small, ResourceParser.TEXT_SMALL);
sFontSizeBtnsMap.put(R.id.ll_font_normal, ResourceParser.TEXT_MEDIUM);
sFontSizeBtnsMap.put(R.id.ll_font_super, ResourceParser.TEXT_SUPER);
//put函数是将指定值和指定键相连
private static final Map<Integer, Integer> sFontSelectorSelectionMap = new HashMap<Integer, Integer>();
static
sFontSelectorSelectionMap.put(ResourceParser.TEXT_LARGE, R.id.iv_large_select);
sFontSelectorSelectionMap.put(ResourceParser.TEXT_SMALL, R.id.iv_small_select);
sFontSelectorSelectionMap.put(ResourceParser.TEXT_MEDIUM, R.id.iv_medium_select);
sFontSelectorSelectionMap.put(ResourceParser.TEXT_SUPER, R.id.iv_super_select);
//put函数是将指定值和指定键相连
private static final String TAG = "NoteEditActivity";
private HeadViewHolder mNoteHeaderHolder;
private View mHeadViewPanel;
//私有化一个界面操作mHeadViewPanel,对表头的操作
private View mNoteBgColorSelector;
//私有化一个界面操作mNoteBgColorSelector,对背景颜色的操作
private View mFontSizeSelector;
//私有化一个界面操作mFontSizeSelector,对标签字体的操作
private EditText mNoteEditor;
//声明编辑控件,对文本操作
private View mNoteEditorPanel;
//私有化一个界面操作mNoteEditorPanel,文本编辑的控制板
//private WorkingNote mWorkingNote;
public WorkingNote mWorkingNote;
//对模板WorkingNote的初始化
private SharedPreferences mSharedPrefs;
//私有化SharedPreferences的数据存储方式
//它的本质是基于XML文件存储key-value键值对数据
private int mFontSizeId;
//用于操作字体的大小
private static final String PREFERENCE_FONT_SIZE = "pref_font_size";
private static final int SHORTCUT_ICON_TITLE_MAX_LEN = 10;
public static final String TAG_CHECKED = String.valueOf('\\u221A');
public static final String TAG_UNCHECKED = String.valueOf('\\u25A1');
private LinearLayout mEditTextList;
//线性布局
private String mUserQuery;
private Pattern mPattern;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
this.setContentView(R.layout.note_edit);
//对数据库的访问操作
if (savedInstanceState == null && !initActivityState(getIntent()))
finish();
return;
initResources();
/**
* Current activity may be killed when the memory is low. Once it is killed, for another time
* user load this activity, we should restore the former state
*/
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState)
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState != null && savedInstanceState.containsKey(Intent.EXTRA_UID))
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_UID, savedInstanceState.getLong(Intent.EXTRA_UID));
if (!initActivityState(intent))
finish();
return;
Log.d(TAG, "Restoring from killed activity");
//为防止内存不足时程序的终止,在这里有一个保存现场的函数
private boolean initActivityState(Intent intent)
/**
* If the user specified the @link Intent#ACTION_VIEW but not provided with id,
* then jump to the NotesListActivity
*/
mWorkingNote = null;
if (TextUtils.equals(Intent.ACTION_VIEW, intent.getAction()))
long noteId = intent.getLongExtra(Intent.EXTRA_UID, 0);
mUserQuery = "";
//如果用户实例化标签时,系统并未给出标签ID
/**
* Starting from the searched result
*/
//根据键值查找ID
if (intent.hasExtra(SearchManager.EXTRA_DATA_KEY))
noteId = Long.parseLong(intent.getStringExtra(SearchManager.EXTRA_DATA_KEY));
mUserQuery = intent.getStringExtra(SearchManager.USER_QUERY);
//如果ID在数据库中未找到
if (!DataUtils.visibleInNoteDatabase(getContentResolver(), noteId, Notes.TYPE_NOTE))
Intent jump = new Intent(this, NotesListActivity.class);
startActivity(jump);
//程序将跳转到上面声明的intent——jump
showToast(R.string.error_note_not_exist);
finish();
return false;
//ID在数据库中找到
else
mWorkingNote = WorkingNote.load(this, noteId);
if (mWorkingNote == null)
Log.e(TAG, "load note failed with note id" + noteId);
//打印出红色的错误信息
finish();
return false;
//setSoftInputMode——软键盘输入模式
getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN
| WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
else if(TextUtils.equals(Intent.ACTION_INSERT_OR_EDIT, intent.getAction()))
// intent.getAction()
// 大多用于broadcast发送广播时给机制(intent)设置一个action,就是一个字符串
// 用户可以通过receive(接受)intent,通过 getAction得到的字符串,来决定做什么
long folderId = intent.getLongExtra(Notes.INTENT_EXTRA_FOLDER_ID, 0);
int widgetId = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
int widgetType = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_TYPE,
Notes.TYPE_WIDGET_INVALIDE);
int bgResId = intent.getIntExtra(Notes.INTENT_EXTRA_BACKGROUND_ID,
ResourceParser.getDefaultBgId(this));
// intent.getInt(Long、String)Extra是对各变量的语法分析
// Parse call-record note
String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
long callDate = intent.getLongExtra(Notes.INTENT_EXTRA_CALL_DATE, 0);
if (callDate != 0 && phoneNumber != null)
if (TextUtils.isEmpty(phoneNumber))
Log.w(TAG, "The call record number is null");
long noteId = 0;
if ((noteId = DataUtils.getNoteIdByPhoneNumberAndCallDate(getContentResolver(),
phoneNumber, callDate)) > 0)
mWorkingNote = WorkingNote.load(this, noteId);
if (mWorkingNote == null)
Log.e(TAG, "load call note failed with note id" + noteId);
finish();
return false;
//将电话号码与手机的号码簿相关
else
mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId,
widgetType, bgResId);
mWorkingNote.convertToCallNote(phoneNumber, callDate);
//
else
mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId, widgetType,
bgResId);
//创建一个新的WorkingNote
getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
| WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
else
Log.e(TAG, "Intent not specified action, should not support");
finish();
return false;
mWorkingNote.setOnSettingStatusChangedListener(this);
return true;
@Override
protected void onResume()
super.onResume();
initNoteScreen();
private void initNoteScreen()
//对界面的初始化操作
mNoteEditor.setTextAppearance(this, TextAppearanceResources
.getTexAppearanceResource(mFontSizeId));
//设置外观
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST)
switchToListMode(mWorkingNote.getContent());
else
mNoteEditor.setText(getHighlightQueryResult(mWorkingNote.getContent(), mUserQuery));
mNoteEditor.setSelection(mNoteEditor.getText().length());
for (Integer id : sBgSelectorSelectionMap.keySet())
findViewById(sBgSelectorSelectionMap.get(id)).setVisibility(View.GONE);
mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId());
mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId());
mNoteHeaderHolder.tvModified.setText(DateUtils.formatDateTime(this,
mWorkingNote.getModifiedDate(), DateUtils.FORMAT_SHOW_DATE
| DateUtils.FORMAT_NUMERIC_DATE | DateUtils.FORMAT_SHOW_TIME
| DateUtils.FORMAT_SHOW_YEAR));
/**
* TODO: Add the menu for setting alert. Currently disable it because the DateTimePicker
* is not ready
*/
showAlertHeader();
//设置闹钟的显示
private void showAlertHeader()
if (mWorkingNote.hasClockAlert())
long time = System.currentTimeMillis();
if (time > mWorkingNote.getAlertDate())
mNoteHeaderHolder.tvAlertDate.setText(R.string.note_alert_expired);
//如果系统时间大于了闹钟设置的时间,那么闹钟失效
else
mNoteHeaderHolder.tvAlertDate.setText(DateUtils.getRelativeTimeSpanString(
mWorkingNote.getAlertDate(), time, DateUtils.MINUTE_IN_MILLIS));
mNoteHeaderHolder.tvAlertDate.setVisibility(View.VISIBLE);
mNoteHeaderHolder.ivAlertIcon.setVisibility(View.VISIBLE);
//显示闹钟开启的图标
else
mNoteHeaderHolder.tvAlertDate.setVisibility(View.GONE);
mNoteHeaderHolder.ivAlertIcon.setVisibility(View.GONE);
;
@Override
protected void onNewIntent(Intent intent)
super.onNewIntent(intent);
initActivityState(intent);
@Override
protected void onSaveInstanceState(Bundle outState)
super.onSaveInstanceState(outState);
/**
* For new note without note id, we should firstly save it to
* generate a id. If the editing note is not worth saving, there
* is no id which is equivalent to create new note
*/
if (!mWorkingNote.existInDatabase())
saveNote();
//在创建一个新的标签时,先在数据库中匹配
//如果不存在,那么先在数据库中存储
outState.putLong(Intent.EXTRA_UID, mWorkingNote.getNoteId());
Log.d(TAG, "Save working note id: " + mWorkingNote.getNoteId() + " onSaveInstanceState");
@Override
//MotionEvent是对屏幕触控的传递机制
public boolean dispatchTouchEvent(MotionEvent ev)
if (mNoteBgColorSelector.getVisibility() == View.VISIBLE
&& !inRangeOfView(mNoteBgColorSelector, ev))
mNoteBgColorSelector.setVisibility(View.GONE);
return true;
//颜色选择器在屏幕上可见
if (mFontSizeSelector.getVisibility() == View.VISIBLE
&& !inRangeOfView(mFontSizeSelector, ev))
mFontSizeSelector.setVisibility(View.GONE);
return true;
//字体大小选择器在屏幕上可见
return super.dispatchTouchEvent(ev);
//对屏幕触控的坐标进行操作
private boolean inRangeOfView(View view, MotionEvent ev)
int []location = new int[2];
view.getLocationOnScreen(location);
int x = location[0];
int y = location[1];
if (ev.getX() < x
|| ev.getX() > (x + view.getWidth())
|| ev.getY() < y
|| ev.getY() > (y + view.getHeight()))
//如果触控的位置超出了给定的范围,返回false
return false;
return true;
private void initResources()
mHeadViewPanel = findViewById(R.id.note_title);
mNoteHeaderHolder = new HeadViewHolder();
mNoteHeaderHolder.tvModified = (TextView) findViewById(R.id.tv_modified_date);
mNoteHeaderHolder.ivAlertIcon = (ImageView) findViewById(R.id.iv_alert_icon);
mNoteHeaderHolder.tvAlertDate = (TextView) findViewById(R.id.tv_alert_date);
mNoteHeaderHolder.ibSetBgColor = (ImageView) findViewById(R.id.btn_set_bg_color);
mNoteHeaderHolder.ibSetBgColor.setOnClickListener(this);
mNoteEditor = (EditText) findViewById(R.id.note_edit_view);
mNoteEditorPanel = findViewById(R.id.sv_note_edit);
mNoteBgColorSelector = findViewById(R.id.note_bg_color_selector);
for (int id : sBgSelectorBtnsMap.keySet())
ImageView iv = (ImageView) findViewById(id);
iv.setOnClickListener(this);
//对标签各项属性内容的初始化
mFontSizeSelector = findViewById(R.id.font_size_selector);
for (int id : sFontSizeBtnsMap.keySet())
View view = findViewById(id);
view.setOnClickListener(this);
;//对字体大小的选择
mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
mFontSizeId = mSharedPrefs.getInt(PREFERENCE_FONT_SIZE, ResourceParser.BG_DEFAULT_FONT_SIZE);
/**
* HACKME: Fix bug of store the resource id in shared preference.
* The id may larger than the length of resources, in this case,
* return the @link ResourceParser#BG_DEFAULT_FONT_SIZE
*/
if(mFontSizeId >= TextAppearanceResources.getResourcesSize())
mFontSizeId = ResourceParser.BG_DEFAULT_FONT_SIZE;
mEditTextList = (LinearLayout) findViewById(R.id.note_edit_list);
@Override
protected void onPause()
super.onPause();
if(saveNote())
Log.d(TAG, "Note data was saved with length:" + mWorkingNote.getContent().length());
clearSettingState();
//和桌面小工具的同步
private void updateWidget()
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_2X)
intent.setClass(this, NoteWidgetProvider_2x.class);
else if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_4X)
intent.setClass(this, NoteWidgetProvider_4x.class);
else
Log.e(TAG, "Unspported widget type");
return;
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[]
mWorkingNote.getWidgetId()
);
sendBroadcast(intent);
setResult(RESULT_OK, intent);
public void onClick(View v)
int id = v.getId();
if (id == R.id.btn_set_bg_color)
mNoteBgColorSelector.setVisibility(View.VISIBLE);
findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(
- View.VISIBLE);
else if (sBgSelectorBtnsMap.containsKey(id))
findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(
View.GONE);
mWorkingNote.setBgColorId(sBgSelectorBtnsMap.get(id));
mNoteBgColorSelector.setVisibility(View.GONE);
else if (sFontSizeBtnsMap.containsKey(id))
findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.GONE);
mFontSizeId = sFontSizeBtnsMap.get(id);
mSharedPrefs.edit().putInt(PREFERENCE_FONT_SIZE, mFontSizeId).commit();
findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.VISIBLE);
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST)
getWorkingText();
switchToListMode(mWorkingNote.getContent());
else
mNoteEditor.setTextAppearance(this,
TextAppearanceResources.getTexAppearanceResource(mFontSizeId));
mFontSizeSelector.setVisibility(View.GONE);
//************************存在问题
@Override
public void onBackPressed()
if(clearSettingState())
return;
saveNote();
super.onBackPressed();
private boolean clearSettingState()
if (mNoteBgColorSelector.getVisibility() == View.VISIBLE)
mNoteBgColorSelector.setVisibility(View.GONE);
return true;
else if (mFontSizeSelector.getVisibility() == View.VISIBLE)
mFontSizeSelector.setVisibility(View.GONE);
return true;
return false;
public void onBackgroundColorChanged()
findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(
View.VISIBLE);
mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId());
mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId());
@Override
//对选择菜单的准备
public boolean onPrepareOptionsMenu(Menu menu)
if (isFinishing())
return true;
clearSettingState();
menu.clear();
if (mWorkingNote.getFolderId() == Notes.ID_CALL_RECORD_FOLDER)
getMenuInflater().inflate(R.menu.call_note_edit, menu);
// MenuInflater是用来实例化Menu目录下的Menu布局文件的
else
getMenuInflater().inflate(R.menu.note_edit, menu);
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST)
menu.findItem(R.id.menu_list_mode).setTitle(R.string.menu_normal_mode);
else
menu.findItem(R.id.menu_list_mode).setTitle(R.string.menu_list_mode);
if (mWorkingNote.hasClockAlert())
menu.findItem(R.id.menu_alert).setVisible(false);
else
menu.findItem(R.id.menu_delete_remind).setVisible(false);
return true;
@Override
/*
* 函数功能:动态改变菜单选项内容
* 函数实现:如下注释
*/
public boolean onOptionsItemSelected(MenuItem item)
switch (item.getItemId())
//根据菜单的id来编剧相关项目
case R.id.menu_new_note:
//创建一个新的便签
createNewNote();
break;
case R.id.menu_delete:
//删除便签
AlertDialog.Builder builder = new AlertDialog.Builder(this);
//创建关于删除操作的对话框
builder.setTitle(getString(R.string.alert_title_delete));
// 设置标签的标题为alert_title_delete
builder.setIcon(android.R.drawable.ic_dialog_alert);
//设置对话框图标
builder.setMessage(getString(R.string.alert_message_delete_note));
//设置对话框内容
builder.setPositiveButton(android.R便签项目需求分析与建议-NABCD模型
我们吃饭要排队的项目为便签APP,以下是项目需求分析与建议的NABCD模型:
N(Need需求):
我们的APP面向广大群体,适合所有人使用。现在越来越少的人使用纸质材料来记录。这款软件正是满足用户记录的需求,你可以在这里记录下所有你想记录的东西,不管是某个时刻的心情、灵感,还是看到喜欢的文字、图片,随手记录下来,简单又方便。
A(Approach做法):
我们整个项目一共三个开发人员,自学能力强的人员负责项目的一些高难度的突破性人物,擅长使用模板框架的人员负责界面的开发美化,还有了解数据传输的人员进行后台数据的处理工作。同时,我们三个人对完成项目有着充分的激情和信心,包括对产品的推广的我们都有能力完成。
B(Benefit好处):
我们的产品页面设计简洁,安装简单,操作便捷,能够更好的记录生活,用户通过使用我们的产品,生活会更加有条理,多姿多彩,养成记录的习惯,爱上记录和表达。过段时间用户在回头看看自己曾经的便签,会有一种别样的感觉。
C(Competitors竞争):
与我们的产品相似的的竞争者有很多。并且都很成熟,好多都还能支持语音、视频记事。有些还可以直接与其他软件关联,在其他软件看到的内容可以直接同步到便签APP中。虽然我们的竞争者有很多,但我们相信通过仔细分析用户的需求,我们能设计出最适合用户的产品,给用户最贴心的体验。
D(Delivery交付):
项目完成之后,将初代产品进行内部的使用测试,通过解决测试反馈的问题,完善产品。申请团队专利,可以先在朋友同学之间使用,相互推荐,同时也可以在网上进行相应的宣传。
如果产品的使用反响很好的话,再进行下一步的宣传。
以上是关于小米便签源码分析——UI包的主要内容,如果未能解决你的问题,请参考以下文章
Android源码深入源码分析UI的绘制流程 附:Android源码解析资料分享
Android源码深入源码分析UI的绘制流程 附:Android源码解析资料分享