本文原創地址: jsbintask的博客(食用效果最佳),轉載請註明出處!php
項目源碼地址:github.com/jsbintask22…,歡迎fork,starjava
乾貨推薦:【乾貨】SSM,Jquery, Bootstrap從零打造一個論壇系統android
分享一個之前學習android
時作的小項目,一個帶有定時提醒功能的備忘錄
,主要用到RecycleView
, sqlite
, butterknife
,效果以下: git
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
}
}
複製代碼
記事本中是包含一個實體類,Event
,編寫實體類和dao層代碼對該表進行增刪改查 Event
:github
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
:sql
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;
}
}
複製代碼
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);
}
}
複製代碼
爲了讓咱們的提醒服務在後臺保活,咱們須要編寫一個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
以下:app
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
,因此咱們加入BaseActivity
:ide
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>
複製代碼
鬧鈴提醒:
關注我!這裏只有乾貨!