本文原创地址,
我的博客
:https://jsbintask.cn/2019/02/23/android/android-memo/(食用效果最佳),转载请注明出处!
介绍
今天给大家分享一个以前学习android
时做的小项目,一个带有定时提醒功能的备忘录
,主要用到RecycleView
, sqlite
, butterknife
,效果如下:
apk地址
详细功能实现
建立db,编写db helper类
- 新建一个常量类,包含所有操作db的语句,
ColumnContacts
:
public class ColumnContacts {
public static final String EVENT_TABLE_NAME = "event";
public static final String EVENT_TITLE_COLUMN = "title";
public static final String EVENT_CONTENT_COLUMN = "content";
public static final String EVENT_CREATED_TIME_COLUMN = "created_time";
public static final String EVENT_UPDATED_TIME_COLUMN = "updated_time";
public static final String EVENT_REMIND_TIME_COLUMN = "remind_time";
public static final String EVENT_IS_IMPORTANT_COLUMN = "is_important";
public static final String EVENT_IS_CLOCKED = "is_clocked";
}
- 新建一个
DBTemplate
,此处用到设计模式模板方法,所以还包含一个回调接口DBCallbackk
public class DBTemplate<T> {
private DBOpenHelper dbHelper;
public DBTemplate() {
dbHelper = new DBOpenHelper();
}
public T queryOne(String sql, DBCallback<T> callback, String...args) {
T t = null;
SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor cursor = db.rawQuery(sql, args);
if (cursor != null && cursor.moveToNext()) {
t = callback.cursorToInstance(cursor);
cursor.close();
}
return t;
}
public List<T> query(String sql, DBCallback<T> callback, String... args) {
List<T> list = new ArrayList<>();
SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor cursor = db.rawQuery(sql, args);
if (cursor != null) {
while (cursor.moveToNext()) {
T t = callback.cursorToInstance(cursor);
list.add(t);
}
cursor.close();
}
return list;
}
public long create(String table, ContentValues values) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
return db.insert(table, null, values);
}
public int remove(String table, String whereConditions, String... args) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
return db.delete(table, whereConditions, args);
}
public int getLatestId(String table) {
SQLiteDatabase db = dbHelper.getReadableDatabase();
String sql = "SELECT MAX(" + BaseColumns._ID + ") FROM " + table;
Cursor cursor = db.rawQuery(sql, new String[]{});
int result = -1;
if (cursor != null && cursor.moveToNext()) {
result = cursor.getInt(0);
cursor.close();
}
return result;
}
public int update(String table, ContentValues contentValues, String whereConditions, String... args) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
return db.update(table, contentValues, whereConditions, args);
}
}
public interface DBCallback <T> {
/**
* Gets a instance of T by cursor
*/
T cursorToInstance(Cursor cursor);
}
- 新建一个类继承
SQLiteOpenHelper
,DBOpenHelper
,用于启动app时创建数据库,并且初始化数据,加入一条记录:
public class DBOpenHelper extends SQLiteOpenHelper {
private static final String TAG = DBOpenHelper.class.getSimpleName();
private static final int VERSION = 1;
private static final String DB_NAME = "memo.db";
public DBOpenHelper() {
super(MemoApplication.getContext(), DB_NAME, null, VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
/*第一次初始化app,创建表结构 */
db.execSQL("CREATE TABLE IF NOT EXISTS " + ColumnContacts.EVENT_TABLE_NAME + "( "
+ BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ ColumnContacts.EVENT_TITLE_COLUMN + " text, "
+ ColumnContacts.EVENT_CONTENT_COLUMN + " text, "
+ ColumnContacts.EVENT_CREATED_TIME_COLUMN + " datetime, "
+ ColumnContacts.EVENT_UPDATED_TIME_COLUMN + " datetime, "
+ ColumnContacts.EVENT_REMIND_TIME_COLUMN + " datetime, "
+ ColumnContacts.EVENT_IS_IMPORTANT_COLUMN + " INTEGER, "
+ ColumnContacts.EVENT_IS_CLOCKED + " INTEGER"
+ ")");
String sql = "INSERT INTO " + ColumnContacts.EVENT_TABLE_NAME + " VALUES(NULL, ?, ?, ?, ?, ?, ?, ?)";
db.beginTransaction();
db.execSQL(sql, new Object[]{"jsbintask->memo",
"Memo是一个小巧方便带有闹铃功能的记事本app,主要使用butterknife和recycleview,clockmanager构建\n" +
"git地址:https://github.com/jsbintask22/memo.git",
"2018-04-25 17:28:23",
"2018-04-25 17:28",
"2018-04-25 17:28",
0, 0});
db.setTransactionSuccessful();
db.endTransaction();
}
/*版本更新时会执行该方法,如版本变更 => 2 */
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//Nothing to do
}
}
编写dao层
记事本中是包含一个实体类,Event
,编写实体类和dao层代码对该表进行增删改查Event
:
public class Event implements BaseColumns, Parcelable{
//id
private Integer mId;
//事件
private String mTitle;
//事件内容
private String mContent;
//创建时间
private String mCreatedTime;
public Integer getmIsClocked() {
return mIsClocked;
}
public void setmIsClocked(Integer mIsClocked) {
this.mIsClocked = mIsClocked;
}
//更新时间
private String mUpdatedTime;
//闹钟表示位:该事件是否已经响过铃了,默认没有
private Integer mIsClocked = 0;
public Event(){}
protected Event(Parcel in) {
if (in.readByte() == 0) {
mId = null;
} else {
mId = in.readInt();
}
mTitle = in.readString();
mContent = in.readString();
mCreatedTime = in.readString();
mUpdatedTime = in.readString();
if (in.readByte() == 0) {
mIsClocked = null;
} else {
mIsClocked = in.readInt();
}
if (in.readByte() == 0) {
mIsImportant = null;
} else {
mIsImportant = in.readInt();
}
mRemindTime = in.readString();
}
public static final Creator<Event> CREATOR = new Creator<Event>() {
@Override
public Event createFromParcel(Parcel in) {
return new Event(in);
}
@Override
public Event[] newArray(int size) {
return new Event[size];
}
};
public Integer getmIsImportant() {
return mIsImportant;
}
public void setmIsImportant(Integer mIsImportant) {
this.mIsImportant = mIsImportant;
}
private Integer mIsImportant;
public Integer getmId() {
return mId;
}
public void setmId(Integer mId) {
this.mId = mId;
}
public String getmTitle() {
return mTitle;
}
public void setmTitle(String mTitle) {
this.mTitle = mTitle;
}
public String getmContent() {
return mContent;
}
public void setmContent(String mContent) {
this.mContent = mContent;
}
public String getmCreatedTime() {
return mCreatedTime;
}
public void setmCreatedTime(String mCreatedTime) {
this.mCreatedTime = mCreatedTime;
}
public String getmUpdatedTime() {
return mUpdatedTime;
}
public void setmUpdatedTime(String mUpdatedTime) {
this.mUpdatedTime = mUpdatedTime;
}
public String getmRemindTime() {
return mRemindTime;
}
public void setmRemindTime(String mRemindTime) {
this.mRemindTime = mRemindTime;
}
private String mRemindTime;
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
if (mId == null) {
dest.writeByte((byte) 0);
} else {
dest.writeByte((byte) 1);
dest.writeInt(mId);
}
dest.writeString(mTitle);
dest.writeString(mContent);
dest.writeString(mCreatedTime);
dest.writeString(mUpdatedTime);
if (mIsClocked == null) {
dest.writeByte((byte) 0);
} else {
dest.writeByte((byte) 1);
dest.writeInt(mIsClocked);
}
if (mIsImportant == null) {
dest.writeByte((byte) 0);
} else {
dest.writeByte((byte) 1);
dest.writeInt(mIsImportant);
}
dest.writeString(mRemindTime);
}
}
值得注意的是,为了让其在Activity之间传递数据,需要继承Parcelable
接口,接下编写EventDao
,因为用到了模板方法,所以加入回调类EventCallback
:
public class EventDao {
//使用模板模式进行DB操作
private DBTemplate<Event> mTemplate = new DBTemplate<>();
private EventCallback mCallback = new EventCallback();
//设置为单例模式,其他类均是此方法
private static EventDao mEventDao = new EventDao();
private EventDao() {
}
public static EventDao getInstance() {
return mEventDao;
}
public List<Event> findAll() {
String sql = "SELECT * FROM " + ColumnContacts.EVENT_TABLE_NAME + " ORDER BY " + ColumnContacts.EVENT_IS_IMPORTANT_COLUMN + " DESC, " + ColumnContacts.EVENT_CREATED_TIME_COLUMN + " DESC";
return mTemplate.query(sql, mCallback);
}
public List<Event> findAllWithNOClocked() {
String sql = "SELECT * FROM " + ColumnContacts.EVENT_TABLE_NAME + " WHERE " + ColumnContacts.EVENT_IS_CLOCKED + " = " + Constants.EventClockFlag.NONE;
return mTemplate.query(sql, mCallback);
}
public int updateEventClocked(Integer id) {
ContentValues contentValues = new ContentValues();
contentValues.put(ColumnContacts.EVENT_IS_CLOCKED, Constants.EventClockFlag.CLOCKED);
return mTemplate.update(ColumnContacts.EVENT_TABLE_NAME, contentValues, BaseColumns._ID + " = ?", Integer.toString(id));
}
public Event findById(Integer id) {
String sql = "SELECT * FROM " + ColumnContacts.EVENT_TABLE_NAME + " WHERE " + BaseColumns._ID + " = ?";
return mTemplate.queryOne(sql, mCallback, Integer.toString(id));
}
public int remove(List<Integer> ids) {
StringBuilder whereConditions = new StringBuilder(BaseColumns._ID + " IN(");
for (Integer id : ids) {
whereConditions.append(id).append(",");
}
whereConditions.deleteCharAt(whereConditions.length() - 1).append(")");
return mTemplate.remove(ColumnContacts.EVENT_TABLE_NAME, whereConditions.toString());
}
public int create(Event event) {
return (int) mTemplate.create(ColumnContacts.EVENT_TABLE_NAME, generateContentValues(event, false));
}
public int update(Event event) {
return mTemplate.update(ColumnContacts.EVENT_TABLE_NAME, generateContentValues(event, true), BaseColumns._ID + " = ?", Integer.toString(event.getmId()));
}
public int getLatestEventId() {
return mTemplate.getLatestId(ColumnContacts.EVENT_TABLE_NAME);
}
private ContentValues generateContentValues(Event event, boolean isUpdate) {
ContentValues contentValues = new ContentValues();
contentValues.put(ColumnContacts.EVENT_TITLE_COLUMN, event.getmTitle());
contentValues.put(ColumnContacts.EVENT_CONTENT_COLUMN, event.getmContent());
if (!isUpdate) {
contentValues.put(ColumnContacts.EVENT_CREATED_TIME_COLUMN, DateTimeUtil.dateToStr(new Date()));
} else {
contentValues.put(ColumnContacts.EVENT_CREATED_TIME_COLUMN, event.getmCreatedTime());
}
contentValues.put(ColumnContacts.EVENT_IS_CLOCKED, event.getmIsClocked());
contentValues.put(ColumnContacts.EVENT_UPDATED_TIME_COLUMN, DateTimeUtil.dateToStr(new Date()));
contentValues.put(ColumnContacts.EVENT_REMIND_TIME_COLUMN, event.getmRemindTime());
contentValues.put(ColumnContacts.EVENT_IS_IMPORTANT_COLUMN, event.getmIsImportant());
return contentValues;
}
}
public class EventCallback implements DBCallback<Event> {
@Override
public Event cursorToInstance(Cursor cursor) {
Event event = new Event();
event.setmId(cursor.getInt(cursor.getColumnIndexOrThrow(BaseColumns._ID)));
event.setmTitle(cursor.getString(cursor.getColumnIndexOrThrow(ColumnContacts.EVENT_TITLE_COLUMN)));
event.setmContent(cursor.getString(cursor.getColumnIndexOrThrow(ColumnContacts.EVENT_CONTENT_COLUMN)));
event.setmCreatedTime(cursor.getString(cursor.getColumnIndexOrThrow(ColumnContacts.EVENT_CREATED_TIME_COLUMN)));
event.setmUpdatedTime(cursor.getString(cursor.getColumnIndexOrThrow(ColumnContacts.EVENT_UPDATED_TIME_COLUMN)));
event.setmRemindTime(cursor.getString(cursor.getColumnIndexOrThrow(ColumnContacts.EVENT_REMIND_TIME_COLUMN)));
event.setmIsImportant(cursor.getInt(cursor.getColumnIndexOrThrow(ColumnContacts.EVENT_IS_IMPORTANT_COLUMN)));
event.setmIsClocked(cursor.getInt(cursor.getColumnIndexOrThrow(ColumnContacts.EVENT_IS_CLOCKED)));
return event;
}
}
编写manager层
- EventManager用于管理所有的event如下:
public class EventManager {
public static final String TAG = "EventManager";
private static EventManager mEventManager = new EventManager();
private EventDao mEventDao = EventDao.getInstance();
//保存一份数据
public List<Integer> getDeletedIds() {
return deletedIds;
}
public void setDeletedIds(List<Integer> deletedIds) {
this.deletedIds = deletedIds;
}
private List<Event> events = new ArrayList<>();
private List<Integer> deletedIds = new ArrayList<>();
private EventManager(){
}
public static EventManager getInstance() {
return mEventManager;
}
public List<Event> findAll() {
events = mEventDao.findAll();
return events;
}
public void flushData() {
events = mEventDao.findAll();
}
public List<Event> getEvents() {
return events;
}
public void removeEvents(final Handler handler, final List<Integer> ids) {
new Thread(new Runnable() {
@Override
public void run() {
try {
int result = mEventDao.remove(ids);
Message message = new Message();
message.what = Constants.HANDLER_SUCCESS;
message.obj = result;
message.setTarget(handler);
message.sendToTarget();
} catch (Exception e) {
Log.e(TAG, "run: ", e);
handler.obtainMessage(Constants.HANDLER_FAILED, new MemoException(e)).sendToTarget();
}
}
}).start();
}
public boolean removeEvent(Integer id) {
return mEventDao.remove(Collections.singletonList(id)) != 0;
}
public boolean saveOrUpdate(Event event) {
try {
if (event.getmId() != null) {
mEventDao.update(event);
} else {
mEventDao.create(event);
}
return true;
} catch (Exception e) {
Log.e(TAG, "saveOrUpdate: ", e);
return false;
}
}
public int getLatestEventId() {
return mEventDao.getLatestEventId();
}
public Event getOne(Integer id) {
return mEventDao.findById(id);
}
public boolean checkEventField(Event event) {
if (event == null) {
return false;
}
if (StringUtil.isBlank(event.getmTitle())) {
ToastUtil.showToastShort(R.string.event_can_not_empty);
return false;
}
if (StringUtil.isBlank(event.getmContent())) {
ToastUtil.showToastShort(R.string.content_can_not_empty);
return false;
}
if (StringUtil.isBlank(event.getmRemindTime())) {
ToastUtil.showToastShort(R.string.remind_time_can_not_empty);
return false;
}
if (DateTimeUtil.str2Date(event.getmRemindTime()) == null) {
ToastUtil.showToastShort(R.string.invalid_remind_time_format);
return false;
}
if (new Date().getTime() > DateTimeUtil.str2Date(event.getmRemindTime()).getTime()) {
ToastUtil.showToastShort(R.string.remind_time_deprecated);
return false;
}
return true;
}
}
接着编写一个ClockManager
用于管理系统闹钟服务,用于app定时提醒:
public class ClockManager {
private static ClockManager instance = new ClockManager();
private ClockManager() {
}
public static ClockManager getInstance() {
return instance;
}
/**
* 获取系统闹钟服务
* @return
*/
private static AlarmManager getAlarmManager() {
return (AlarmManager) MemoApplication.getContext().getSystemService(Context.ALARM_SERVICE);
}
/**
* 取消闹钟
* @param pendingIntent
*/
public void cancelAlarm(PendingIntent pendingIntent) {
getAlarmManager().cancel(pendingIntent);
}
/**
* 添加闹钟
* @param pendingIntent 执行动作
* @param performTime 执行时间
*/
public void addAlarm(PendingIntent pendingIntent, Date performTime) {
cancelAlarm(pendingIntent);
getAlarmManager().set(AlarmManager.RTC_WAKEUP, performTime.getTime(), pendingIntent);
}
}
编写系统Service和Receiver
为了让我们的提醒服务在后台保活,我们需要编写一个ClockService
或者ClockReceiver
在后台运行(任意一种都行,此处用的service
:
/**
* Service和Broadcast都行,此处选一个,service存活率更高
*/
public class ClockService extends Service {
private static final String TAG = "ClockService";
public static final String EXTRA_EVENT_ID = "extra.event.id";
public static final String EXTRA_EVENT_REMIND_TIME = "extra.event.remind.time";
public static final String EXTRA_EVENT = "extra.event";
private EventDao mEventDao = EventDao.getInstance();
public ClockService() {
Log.d(TAG, "ClockService: Constructor");
}
@Override
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand: onStartCommand");
WakeLockUtil.wakeUpAndUnlock();
postToClockActivity(getApplicationContext(), intent);
return super.onStartCommand(intent, flags, startId);
}
private void postToClockActivity(Context context, Intent intent) {
Intent i = new Intent();
i.setClass(context, ClockActivity.class);
i.putExtra(EXTRA_EVENT_ID, intent.getIntExtra(EXTRA_EVENT_ID, -1));
Event event = mEventDao.findById(intent.getIntExtra(EXTRA_EVENT_ID, -1));
if (event == null) {
return;
}
i.putExtra(EXTRA_EVENT_REMIND_TIME, intent.getStringExtra(EXTRA_EVENT_REMIND_TIME));
i.putExtra(EXTRA_EVENT, event);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy: ");
super.onDestroy();
}
}
ClockReceiver
如下:
public class ClockReceiver extends BroadcastReceiver {
private static final String TAG = "ClockReceiver";
public static final String EXTRA_EVENT_ID = "extra.event.id";
public static final String EXTRA_EVENT_REMIND_TIME = "extra.event.remind.time";
public static final String EXTRA_EVENT = "extra.event";
private EventDao mEventDao = EventDao.getInstance();
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "onReceive: " + intent.getAction());
WakeLockUtil.wakeUpAndUnlock();
postToClockActivity(context, intent);
}
private void postToClockActivity(Context context, Intent intent) {
Intent i = new Intent();
i.setClass(context, ClockActivity.class);
i.putExtra(EXTRA_EVENT_ID, intent.getIntExtra(EXTRA_EVENT_ID, -1));
Event event = mEventDao.findById(intent.getIntExtra(EXTRA_EVENT_ID, -1));
if (event == null) {
return;
}
i.putExtra(EXTRA_EVENT_REMIND_TIME, intent.getStringExtra(EXTRA_EVENT_REMIND_TIME));
i.putExtra(EXTRA_EVENT, event);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
public ClockReceiver() {
super();
Log.d(TAG, "ClockReceiver: Constructor");
}
}
Activity
接着编写Activity,首先,写一个主界面,用于展示事件清单,因为有多个Activity
,所以我们加入BaseActivity
:
public abstract class BaseActivity extends AppCompatActivity {
/*Butterknife绑定器,该activity销毁时要取消绑定,避免内存泄漏 */
private Unbinder mUnbinder;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getContentView());
mUnbinder = ButterKnife.bind(this);
/*Base activity中的公共的方法,因为每个类都需要初始化,所以在基类中定义 */
initView();
initData();
setListener();
}
protected abstract void setListener();
protected abstract void initView();
protected abstract void initData();
public abstract int getContentView();
@Override
protected void onDestroy() {
super.onDestroy();
mUnbinder.unbind();
}
}
MainActivity
如下:
public class MainActivity extends BaseActivity implements BaseHandler.HandlerResultCallBack {
@BindView(R.id.recycler_view)
RecyclerView mRecyclerView;
@BindView(R.id.search_view)
SearchView mSearchView;
private EventRecyclerViewAdapter mAdapter;
private EventManager mEventManger = EventManager.getInstance();
private ClockManager mClockManager = ClockManager.getInstance();
private BaseHandler mBaseHandler = new BaseHandler(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
protected void initView() {
mAdapter = new EventRecyclerViewAdapter(this);
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) mSearchView.getLayoutParams();
//将文字内容略微下移,SearchView bug
params.bottomMargin = -3;
mSearchView.setLayoutParams(params);
mSearchView.onActionViewExpanded();
initSearchView();
}
private void initSearchView() {
//一处searchView进入屏幕时候的焦点
mSearchView.clearFocus();
Class<? extends SearchView> aClass = mSearchView.getClass();
try {
//去掉SearchView自带的下划线
Field mSearchPlate = aClass.getDeclaredField("mSearchPlate");
mSearchPlate.setAccessible(true);
View o = (View) mSearchPlate.get(mSearchView);
o.setBackgroundColor(getColor(R.color.transparent));
} catch (Exception e) {
e.printStackTrace();
}
//隐藏键盘
AppUtil.hideSoftInput(this, mSearchView);
}
@Override
protected void onStart() {
super.onStart();
}
@Override
protected void onPause() {
super.onPause();
}
@Override
protected void initData() {
//设置数据源,适配器等等
mAdapter.setDatabases(mEventManger.findAll());
mRecyclerView.setAdapter(mAdapter);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
}
@Override
protected void setListener() {
mAdapter.setOnItemClickListener(mOnItemClickListener);
mSearchView.setOnQueryTextListener(mQueryListener);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main_activity, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
//判断是点击了那个按钮,删除,添加
if (item.getItemId() == R.id.menu_add) {
Intent intent = new Intent();
intent.setClass(this, EventDetailActivity.class);
intent.putExtra(EventDetailActivity.EXTRA_IS_ADD_EVENT, true);
startActivity(intent);
} else if (item.getItemId() == R.id.menu_delete) {
if (mAdapter.getIsDeleteMode()) {
//删除数据
if (mAdapter.getSelectedEventIds().size() == 0) {
ToastUtil.showToastShort(R.string.no_event_selected_msg);
} else {
int msg = mAdapter.getSelectedEventIds().size() == 1 ? R.string.delete_event_msg : R.string.delete_events_msg;
AlertDialogUtil.showDialog(this, msg, mConfirmListener);
}
} else {
mAdapter.setDeleteMode(true);
}
}
return super.onOptionsItemSelected(item);
}
@Override
public int getContentView() {
return R.layout.activity_main;
}
private EventRecyclerViewAdapter.OnItemClickListener mOnItemClickListener = new EventRecyclerViewAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
if (!mAdapter.getIsDeleteMode()) {
//跳屏,此时为查看详情,不是编辑状态
Intent intent = new Intent();
intent.setClass(MainActivity.this, EventDetailActivity.class);
intent.putExtra(EventDetailActivity.EXTRA_IS_EDIT_EVENT, false);
intent.putExtra(EventDetailActivity.EXTRA_EVENT_DATA, mAdapter.getDatabases().get(position));
startActivity(intent);
}
}
@Override
public void onItemLongClick(View view, int position) {
ToastUtil.showToastShort("Long clicked");
mAdapter.setDeleteMode(true);
}
};
private DialogInterface.OnClickListener mConfirmListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
mEventManger.setDeletedIds(mAdapter.getSelectedEventIds());
mEventManger.removeEvents(mBaseHandler, mEventManger.getDeletedIds());
}
};
/**
* 从编辑屏幕回来时调用该方法,做数据更新
*/
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
mAdapter.setDatabases(mEventManger.getEvents());
}
private SearchView.OnQueryTextListener mQueryListener = new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
//做本地查询
if (!StringUtil.isBlank(newText)) {
List<Event> events = new ArrayList<>();
for (Event event : mAdapter.getDatabases()) {
if (event.getmTitle().contains(newText)) {
events.add(event);
}
}
mAdapter.setDatabases(events);
} else {
mAdapter.setDatabases(mEventManger.getEvents());
}
return true;
}
};
/**
* handler处理成功的回调函数
* @link com.jsbintask@gmail.com.memo.base.BaseHandler
*/
@Override
public void handlerSuccess(Message msg) {
ToastUtil.showToastShort(R.string.delete_successful);
for (PendingIntent pendingIntent : buildIntent(mEventManger.getDeletedIds())) {
mClockManager.cancelAlarm(pendingIntent);
}
mAdapter.setDatabases(mEventManger.findAll());
}
/**
* 处理失败的回调函数
*/
@Override
public void handlerFailed(MemoException e) {
ToastUtil.showToastShort(R.string.delete_failed);
mAdapter.setDeleteMode(false);
}
private List<PendingIntent> buildIntent(List<Integer> ids) {
List<PendingIntent> pendingIntents = new ArrayList<>();
for (Integer id : ids) {
Intent intent = new Intent();
intent.putExtra(ClockService.EXTRA_EVENT_ID, id);
intent.setClass(this, ClockService.class);
pendingIntents.add(PendingIntent.getService(this, 0x001, intent, PendingIntent.FLAG_UPDATE_CURRENT));
}
return pendingIntents;
}
}
接着,编写个用于展示事件详情的EventDetailActivity
:
public class EventDetailActivity extends BaseActivity {
public static final String EXTRA_IS_EDIT_EVENT = "extra.is.edit.event";
public static final String EXTRA_EVENT_DATA = "extra.event.data";
public static final String EXTRA_IS_ADD_EVENT = "extra.is.create.event";
//从主屏进来的操作是不是编辑操作
private boolean isEditEvent;
//从主屏进来的操作是不是添加操作
private boolean isAddEvent;
private EventManager mEventManager = EventManager.getInstance();
private ClockManager mClockManager = ClockManager.getInstance();
@BindView(R.id.ll_update_time)
LinearLayout llUpdateTime;
@BindView(R.id.ed_title)
EditText edTitle;
@BindView(R.id.tv_remind_time_picker)
EditText tvRemindTime;
@BindView(R.id.ed_content)
EditText edContent;
@BindView(R.id.tv_last_edit_time)
TextView tvUpdateTime;
@BindView(R.id.iv_back)
ImageView ivBack;
@BindView(R.id.tv_confirm)
TextView tvConfirm;
@BindView(R.id.iv_delete)
ImageView ivDelete;
@BindView(R.id.iv_edit)
ImageView ivEdit;
@BindView(R.id.chb_is_important)
CheckBox chbIsImportant;
@BindView(R.id.scroll_view)
ScrollView scrollView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
protected void setListener() {
}
@Override
protected void initView() {
isEditEvent = getIntent().getBooleanExtra(EXTRA_IS_EDIT_EVENT, false);
isAddEvent = getIntent().getBooleanExtra(EXTRA_IS_ADD_EVENT, false);
judgeOperate();
}
private void judgeOperate() {
//是否显示上方上次编辑时间
llUpdateTime.setVisibility(isAddEvent ? View.GONE : View.VISIBLE);
//是否能够编辑标题
setEditTextReadOnly(edTitle, !isEditEvent && !isAddEvent);
//是否能够编辑内容
setEditTextReadOnly(edContent, !isEditEvent && !isAddEvent);
//设置提醒时间不能手动输入
setEditTextReadOnly(tvRemindTime, true);
//设置提醒时间是否能够点击:弹出时间选择器
tvRemindTime.setClickable(isEditEvent || isAddEvent);
//设置右上角确定按钮是否可见
tvConfirm.setVisibility(isEditEvent || isAddEvent ? View.VISIBLE : View.GONE);
//设置右下角编辑按钮是否可见
ivEdit.setVisibility(!isEditEvent && !isAddEvent ? View.VISIBLE : View.GONE);
//设置左下角删除按钮是否可见
ivDelete.setVisibility(!isAddEvent ? View.VISIBLE : View.GONE);
//设置checkbox能不能点击
chbIsImportant.setClickable(isEditEvent || isAddEvent);
}
@Override
protected void initData() {
if (!isAddEvent) {
Event event = getIntent().getParcelableExtra(EXTRA_EVENT_DATA);
//填充值
tvUpdateTime.setText(event.getmUpdatedTime());
edTitle.setText(event.getmTitle());
edContent.setText(event.getmContent());
tvRemindTime.setText(event.getmRemindTime());
chbIsImportant.setChecked(event.getmIsImportant() == Constants.EventFlag.IMPORTANT);
}
}
@OnClick(R.id.iv_back)
public void backImageClick(View view) {
finish();
}
@OnClick(R.id.iv_delete)
public void deleteImageClick(View view) {
if (!isAddEvent) {
AlertDialogUtil.showDialog(this, R.string.delete_event_msg, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Event event = getIntent().getParcelableExtra(EventDetailActivity.EXTRA_EVENT_DATA);
if (mEventManager.removeEvent(event.getmId())) {
ToastUtil.showToastShort(R.string.delete_successful);
mClockManager.cancelAlarm(buildIntent(event.getmId()));
mEventManager.flushData();
postToMainActivity();
} else {
ToastUtil.showToastShort(R.string.delete_failed);
}
}
});
}
}
/**
* 回到主屏幕
*/
private void postToMainActivity() {
Intent intent = new Intent();
intent.setClass(EventDetailActivity.this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
finish();
}
@Override
public int getContentView() {
return R.layout.activity_event_detail;
}
/**
* 弹出时间选择器,选择闹钟执行时间
* @param view
*/
@OnClick(R.id.tv_remind_time_picker)
public void datePickClick(View view) {
if (isEditEvent || isAddEvent) {
final Calendar calendar = Calendar.getInstance();
DatePickerDialog dialog = new DatePickerDialog(this, new DatePickerDialog.OnDateSetListener() {
@Override
public void onDateSet(DatePicker view, final int year, final int month, final int dayOfMonth) {
TimePickerDialog timePickerDialog = new TimePickerDialog(EventDetailActivity.this, new TimePickerDialog.OnTimeSetListener() {
@Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
String time = year + "-" + StringUtil.getLocalMonth(month) + "-" + StringUtil.getMultiNumber(dayOfMonth) + " " + StringUtil.getMultiNumber(hourOfDay) + ":" + StringUtil.getMultiNumber(minute);
tvRemindTime.setText(time);
}
}, calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE), false);
timePickerDialog.show();
}
}, calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH));
dialog.getDatePicker().setMinDate(calendar.getTimeInMillis());
dialog.show();
}
}
@OnClick(R.id.iv_edit)
public void editImageClick(View view) {
if (!isEditEvent) {
ToastUtil.showToastShort(R.string.enter_edit_mode);
ivEdit.setVisibility(View.GONE);
isEditEvent = true;
judgeOperate();
}
}
@OnClick(R.id.tv_confirm)
public void confirmClick(View view) {
//更新
if (isEditEvent || isAddEvent) {
Event event = buildEvent();
//检查属性并且提醒
if (!mEventManager.checkEventField(event)) {
return;
}
if (mEventManager.saveOrUpdate(event)) {
if (isEditEvent) {
ToastUtil.showToastShort(R.string.update_successful);
} else if (isAddEvent) {
ToastUtil.showToastShort(R.string.create_successful);
event.setmId(mEventManager.getLatestEventId());
}
//添加闹钟
mClockManager.addAlarm(buildIntent(event.getmId()), DateTimeUtil.str2Date(event.getmRemindTime()));
mEventManager.flushData();
postToMainActivity();
} else {
if (isEditEvent) {
ToastUtil.showToastShort(R.string.update_failed);
} else if (isAddEvent) {
ToastUtil.showToastShort(R.string.create_failed);
}
}
}
}
private PendingIntent buildIntent(int id) {
Intent intent = new Intent();
intent.putExtra(ClockReceiver.EXTRA_EVENT_ID, id);
intent.setClass(this, ClockService.class);
return PendingIntent.getService(this, 0x001, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
@OnClick(R.id.scroll_view)
public void scrollViewClick(View view) {
if (isAddEvent || isEditEvent) {
//打开软键盘
setEditTextReadOnly(edContent, false);
}
}
@NonNull
private Event buildEvent() {
Event event = new Event();
if (isEditEvent) {
event.setmId(((Event) getIntent().getParcelableExtra(EXTRA_EVENT_DATA)).getmId());
event.setmCreatedTime(((Event) getIntent().getParcelableExtra(EXTRA_EVENT_DATA)).getmCreatedTime());
}
event.setmRemindTime(tvRemindTime.getText().toString());
event.setmTitle(edTitle.getText().toString());
event.setmIsImportant(chbIsImportant.isChecked() ? Constants.EventFlag.IMPORTANT : Constants.EventFlag.NORMAL);
event.setmContent(edContent.getText().toString());
event.setmUpdatedTime(DateTimeUtil.dateToStr(new Date()));
return event;
}
private void setEditTextReadOnly(EditText editText, boolean readOnly) {
editText.setFocusable(!readOnly);
editText.setFocusableInTouchMode(!readOnly);
editText.setCursorVisible(!readOnly);
editText.setTextColor(getColor(readOnly ? R.color.gray3 : R.color.black));
}
}
最后,再写一个Activity用于展示闹铃提醒,ClockActivity
:
public class ClockActivity extends BaseActivity {
private static final String TAG = "ClockActivity";
public static final String EXTRA_CLOCK_EVENT = "clock.event";
//闹铃
private MediaPlayer mediaPlayer;
//震动
private Vibrator mVibrator;
private EventManager mEventManger = EventManager.getInstance();
private Event event;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
protected void setListener() {
}
@Override
protected void initView() {
}
private void clock() {
mediaPlayer.start();
long[] pattern = new long[]{1500, 1000};
mVibrator.vibrate(pattern, 0);
//获取自定义布局
View inflate = LayoutInflater.from(this).inflate(R.layout.dialog_alarm_layout, null);
TextView textView = inflate.findViewById(R.id.tv_event);
textView.setText(String.format(getString(R.string.clock_event_msg_template), event.getmTitle()));
Button btnConfirm = inflate.findViewById(R.id.btn_confirm);
final AlertDialog alertDialog = AlertDialogUtil.showDialog(this, inflate);
btnConfirm.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mediaPlayer.stop();
mVibrator.cancel();
alertDialog.dismiss();
finish();
}
});
alertDialog.show();
}
@Override
protected void onStart() {
super.onStart();
clock();
}
@Override
protected void initData() {
mediaPlayer = MediaPlayer.create(getApplicationContext(), R.raw.clock);
mVibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
Intent intent = getIntent();
event = getIntent().getParcelableExtra(ClockService.EXTRA_EVENT);
if (event == null) {
finish();
}
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
clock();
}
@Override
public int getContentView() {
return R.layout.activity_clock;
}
}
系统界面,服务注册清单
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="cn.jsbintask.memo">
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<application
android:name="cn.jsbintask.memo.MemoApplication"
android:allowBackup="true"
android:icon="@drawable/ic_logo"
android:label="@string/app_name"
android:roundIcon="@drawable/ic_logo"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
<activity
android:name="cn.jsbintask.memo.ui.activity.MainActivity"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="cn.jsbintask.memo.ui.activity.EventDetailActivity"
android:theme="@style/NoActionBarTheme" />
<!-- <service android:name=".service.ClockService" /> -->
<activity
android:name="cn.jsbintask.memo.ui.activity.ClockActivity"
android:launchMode="singleTask"
android:theme="@style/FullScreen" />
<receiver android:name="cn.jsbintask.memo.receiver.ClockReceiver">
<intent-filter android:priority="100">
<action android:name="com.liuzhengwei.memo.action.CLOCK_RECEIVER" />
</intent-filter>
</receiver>
<service
android:name="cn.jsbintask.memo.service.ClockService"
android:enabled="true"
android:exported="true" />
</application>
</manifest>
成果展示
闹铃提醒:
批量删除:
新增和编辑:
搜索:
项目源码地址:https://github.com/jsbintask22/memo,欢迎fork,star。
关注我!这里只有干货!
点击查看更多内容
3人点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦