Android第四次作業
一、團隊成員
姓名:李凱 學號:1600509018 班級:計算機164班 部落格連結地址:https://www.cnblogs.com/JusperLee/
姓名:季軒石 學號:1600802115 班級:計算機164班 部落格連結地址:http://www.cnblogs.com/midnightclad/
二、團隊專案APK連結
Time APK地址:https://github.com/JusperLee/Time/blob/master/APK/Time.apk
三、團隊專案程式碼
GitHub連結網址:https://github.com/JusperLee/Time
四、團隊專案介紹
4.1 團隊專案總體效果截圖
App啟動頁
App的三個頁面按鈕對應的頁面
新增定量計劃頁面
打卡計劃頁面展示
提醒事項頁面展示
主題目前僅僅有白色和黑色
時間新增控制元件展示
4.2 實現的功能及其效果描述
4.2.1 新增計劃功能展示
首先我們點選新增計劃按鈕,然後根據自己的需求我們有兩個選項可以選擇,一個是定量計劃,一個是打卡計劃,下面我們展示一下定量計劃。我們先填寫好自己計劃的資訊點選確定就可以在主頁面上看到自己填寫的內容了。由於截圖較多,換了一種方式展示使用操作gif來展示。
4.2.2 打卡計劃展示
打卡計劃也是通過自定義填寫內容可以直接生成一個打卡計劃的卡片,然後每天可以通過該卡片生成一個打卡內容。下面時演示的gif動圖。
4.2.3 新增提醒事項功能展示
首先我們選中下面的選單欄的第二個內容,然後點選新增TODO,輸入你要提醒的事項,然後選擇提醒時間,就可以新增提醒了。下面是我們的gif動態演示。
4.2.4 主題的更改功能展示
我們通過更改按鈕進行主題更改,可以更改為白天和黑夜,下面通過gif來展示我們的主題更改功能。
五、專案核心程式碼展示
5.1 新增計劃程式碼
package com.lj.Time.page.plan; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v7.widget.Toolbar; import com.lj.Time.R; import com.lj.Time.common.CustomFragmentPagerAdapter; import com.lj.Time.event.PlanSelectedEvent; import com.lj.Time.page.base.BaseActivity; import com.lj.Time.widget.NoScrollViewPager; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; import java.util.ArrayList; import java.util.List; import butterknife.BindView; import butterknife.ButterKnife; /** * 新增計劃介面 */ public class AddPlanActivity extends BaseActivity { @BindView(R.id.toolbar) Toolbar toolbar; @BindView(R.id.view_pager) NoScrollViewPager viewPager; @Override protected int getLayoutResId() { return R.layout.activity_add_plan; } @Override protected void initView(@Nullable Bundle savedInstanceState) { ButterKnife.bind(this); EventBus.getDefault().register(this); initToolbar(toolbar, "新增計劃", true); List<Fragment> fragmentList = new ArrayList<>(); fragmentList.add(new ChoosePlanTypeFragment()); fragmentList.add(new AddPlanFragment()); CustomFragmentPagerAdapter fragmentPagerAdapter = new CustomFragmentPagerAdapter(getSupportFragmentManager(), fragmentList); viewPager.setAdapter(fragmentPagerAdapter); } @Subscribe(threadMode = ThreadMode.MAIN) public void onEventMainThread(PlanSelectedEvent event) { if(event.getPlanType() == -1) { viewPager.setCurrentItem(0); }else{ viewPager.setCurrentItem(1); } } @Override public void onBackPressed() { if(viewPager.getCurrentItem() == 1){ EventBus.getDefault().post(new PlanSelectedEvent(-1)); }else{ super.onBackPressed(); } } @Override protected void onDestroy() { EventBus.getDefault().unregister(this); super.onDestroy(); } }
5.2 修改計劃功能
package com.lj.Time.presenter.plan; import android.app.Activity; import android.content.Context; import android.os.Handler; import android.os.Looper; import android.text.TextUtils; import com.lj.Time.contract.plan.IEditPlanContract; import com.lj.Time.db.ClockPlan; import com.lj.Time.db.ClockPlanDao; import com.lj.Time.db.DBManager; import com.lj.Time.db.RationPlan; import com.lj.Time.db.RationPlanDao; import com.lj.Time.event.PlanChangedEvent; import com.lj.Time.model.plan.EditPlanDataEntity; import com.lj.Time.model.plan.ShowPlanEntity; import com.lj.Time.util.DateUtils; import org.greenrobot.eventbus.EventBus; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.Timer; import java.util.TimerTask; import io.reactivex.Observable; import io.reactivex.ObservableEmitter; import io.reactivex.ObservableOnSubscribe; import io.reactivex.Observer; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.disposables.Disposable; import io.reactivex.functions.Consumer; import io.reactivex.schedulers.Schedulers; /** * 修改計劃 */ public class EditPlanPresenterImpl implements IEditPlanContract.Presenter { private Context context; private IEditPlanContract.View view; private long planId; /** * 0-定量計劃 * 1-打卡計劃 */ private int planType; private Handler mHandler = new Handler(Looper.getMainLooper()); private RationPlanDao mRationPlanDao; private ClockPlanDao mClockPlanDao; private RationPlan rationPlan; private ClockPlan clockPlan; public EditPlanPresenterImpl(Context context, IEditPlanContract.View view) { this.context = context; this.view = view; mRationPlanDao = DBManager.getInstance().getRationPlanDao(); mClockPlanDao = DBManager.getInstance().getClockPlanDao(); } @Override public void initDate(long planId, int planType) { this.planId = planId; this.planType = planType; view.showRoundProgressDialog(); if (planType == 0) { view.showRationPlan(); getRationPlanWithId(); } else { view.showClockPlan(); getClockPlanWithId(); } } private void getRationPlanWithId() { Observable.create((ObservableEmitter<Integer> e) -> { List<RationPlan> rationPlanList = mRationPlanDao.queryBuilder() .where(RationPlanDao.Properties.Id.eq(planId)) .list(); if (rationPlanList.isEmpty()) { e.onNext(0); } else { rationPlan = rationPlanList.get(0); e.onNext(1); } e.onComplete(); }).subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe((Integer value) -> { if (value == 0) { planDoesNotExits(); } else { mHandler.postDelayed(() -> view.fillRationPlanData(rationPlan), 500); } }); } private void getClockPlanWithId() { Observable.create((ObservableEmitter<Integer> e) -> { List<ClockPlan> clockPlanList = mClockPlanDao.queryBuilder() .where(ClockPlanDao.Properties.Id.eq(planId)) .list(); if (clockPlanList.isEmpty()) { e.onNext(0); } else { clockPlan = clockPlanList.get(0); e.onNext(1); } e.onComplete(); }).subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe((Integer value) -> { if (value == 0) { planDoesNotExits(); } else { mHandler.postDelayed(() -> view.fillClockPlanData(clockPlan), 500); } }); } private void planDoesNotExits() { view.showNoActionSnackbar("計劃不存在啊?怎麼回事"); mHandler.postDelayed(() -> ((Activity) view).finish(), 700); } @Override public void updatePlan(final EditPlanDataEntity editData) { if (planType == 0) { if (DateUtils.compareDate("yyyy-MM-dd", rationPlan.getStartDate(), editData.getFinishDate()) != -1) { view.showNoActionSnackbar("結束時間不能小於開始時間!!!"); return; } Observable.create((ObservableEmitter<Integer> e) -> { rationPlan.setName(editData.getName()); rationPlan.setTarget(editData.getTarget()); rationPlan.setCurrent(editData.getCurrent()); rationPlan.setFinishDate(editData.getFinishDate()); rationPlan.setUnit(editData.getUnit()); rationPlan.setPeriodIsOpen(editData.isPeriodIsOpen()); if(editData.isPeriodIsOpen()){ rationPlan.setPeriodPlanTarget(editData.getPeriodTarget()); rationPlan.setPeriodPlanType(editData.getPeriodPlanType()); } mRationPlanDao.insertOrReplace(rationPlan); DBManager.getInstance().clear(); e.onNext(0); e.onComplete(); }).subscribeOn(Schedulers.computation()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(integer -> editSuccess()); } else { Observable.create((ObservableEmitter<Integer> e) -> { clockPlan.setName(editData.getName()); if (!TextUtils.isEmpty(editData.getDescription())) { clockPlan.setDescription(editData.getDescription()); } mClockPlanDao.insertOrReplace(clockPlan); DBManager.getInstance().clear(); e.onNext(0); e.onComplete(); }).subscribeOn(Schedulers.computation()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(integer -> editSuccess()); } } @Override public void deletePlan() { if (planType == 0) { mRationPlanDao.deleteByKey(planId); } else { mClockPlanDao.deleteByKey(planId); } view.showNoActionSnackbar("搞定"); DBManager.getInstance().clear(); EventBus.getDefault().post(new PlanChangedEvent()); mHandler.postDelayed(() -> ((Activity) context).finish(), 700); } @Override public void deletePeriod() { if (rationPlan != null) { rationPlan.setPeriodIsOpen(false); mRationPlanDao.insertOrReplace(rationPlan); DBManager.getInstance().clear(); initDate(planId, planType); } } private void editSuccess() { view.showNoActionSnackbar("修改好了"); EventBus.getDefault().post(new PlanChangedEvent()); mHandler.postDelayed(() -> ((Activity) context).finish(), 700); } @Override public void onDestroy() { mRationPlanDao = null; mClockPlanDao = null; } }
5.3 提醒計劃已完成功能程式碼
package com.lj.Time.presenter.todo; import com.lj.Time.contract.todo.ICompletedTodoContract; import com.lj.Time.db.DBManager; import com.lj.Time.db.Todo; import com.lj.Time.db.TodoDao; import com.lj.Time.model.todo.ShowTodoEntity; import java.util.ArrayList; import java.util.List; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; /** * 已完成的待辦事項 */ public class CompletedTodoPresenterImpl implements ICompletedTodoContract.Presenter { private ICompletedTodoContract.View view; private TodoDao mTodoDao; private Observable<Integer> updateTodoObservable; private List<ShowTodoEntity> listData = new ArrayList<>(); public CompletedTodoPresenterImpl(ICompletedTodoContract.View view) { this.view = view; mTodoDao = DBManager.getInstance().getTodoDao(); updateTodoObservable = Observable.create(e -> { listData.clear(); List<Todo> todoList = mTodoDao.queryBuilder() .where(TodoDao.Properties.Completed.eq(true)) .orderAsc(TodoDao.Properties.Date) .list(); if (!todoList.isEmpty()) { listData.addAll(Observable.fromIterable(todoList) .map(todo -> { ShowTodoEntity showTodo = new ShowTodoEntity(); showTodo.setType(0); showTodo.setTodo(todo); return showTodo; }) .toList() .blockingGet()); } e.onNext(0); e.onComplete(); }); } @Override public void update() { updateTodoObservable.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(integer -> view.notifyTodoChanged(listData)); } }
5.4 提醒計劃展示實現程式碼
package com.lj.Time.presenter.todo; import android.content.Context; import com.lj.Time.contract.todo.IShowTodoContract; import com.lj.Time.db.DBManager; import com.lj.Time.db.Todo; import com.lj.Time.db.TodoDao; import com.lj.Time.model.todo.ShowTodoEntity; import com.lj.Time.util.DateUtils; import java.util.ArrayList; import java.util.List; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; /** * 待辦事項展示 */ public class ShowTodoPresenterImpl implements IShowTodoContract.Presenter { private Context context; private IShowTodoContract.View view; private TodoDao mTodoDao; private Observable<Integer> updateTodoObservable; private String dateFormat = "yyyy-MM-dd HH:mm"; private String currentDate = DateUtils.getCurrentDate(dateFormat); private List<ShowTodoEntity> listData = new ArrayList<>(); public ShowTodoPresenterImpl(Context context, IShowTodoContract.View view) { this.context = context; this.view = view; mTodoDao = DBManager.getInstance().getTodoDao(); updateTodoObservable = Observable.create(e -> { listData.clear(); List<Todo> todoList = mTodoDao.queryBuilder() .where(TodoDao.Properties.Completed.eq(false)) .orderAsc(TodoDao.Properties.Date) .list(); if (!todoList.isEmpty()) { listData.addAll(Observable.fromIterable(todoList) .map(todo -> { ShowTodoEntity showTodo = new ShowTodoEntity(); showTodo.setType(0); showTodo.setTodo(todo); showTodo.setLevel(getLevel(todo)); return showTodo; }) .toList() .blockingGet()); } ShowTodoEntity addTodoEntity = new ShowTodoEntity(); addTodoEntity.setType(1); listData.add(addTodoEntity); e.onNext(0); e.onComplete(); }); } /** * 獲取待辦事項緊急等級 */ private int getLevel(Todo todo) { int level = 1; int hourSpace = DateUtils.getHourSpace(dateFormat, currentDate, todo.getDate()); if (hourSpace <= 1) { level = 6; } else if (hourSpace <= 5) { level = 5; } else if (hourSpace <= 12) { level = 4; } else if (hourSpace <= 24) { level = 3; } else if (hourSpace <= 48) { level = 2; } return level; } @Override public void update() { updateTodoObservable.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(integer -> view.notifyTodoChanged(listData)); } }
六、執行其他團隊專案的apk,並進行評論
由於同學們還沒寫完,我們還不能進行排名並評論。因此,等都提交完後進行排名並評論。
七、專案遇到的問題
李凱,1600509018,在做專案的過程中遇到了很多的問題:
(1)由於我想到對我們的這個專案使用greenrobot的一個開源專案EventBus,來對專案提供高效的釋出和訂閱事件,來對不同執行緒之間傳遞資料。我在引入GrennDao時出現了,專案混淆日誌報錯,Could not init DAOConfig。 通過在國外論壇發現了給問題的解決方法:在proguard-rules.pro中加入
-keepclassmembers class * extends de.greenrobot.dao.AbstractDao {
public static java.lang.String TABLENAME;
}
-keep class **$Properties
(2)同時在將專案進行執行的時候,在虛擬機器安裝app的時候報了一個錯誤:Android Studio配置androidannotations 出現 Error:Execution failed for task ':app:compileDebugJavaWithJavac'。我的問題的解決方式是通過更新 BuildTools,然後clean專案後重新進行Build。問題就解決了。
(3)同時在安裝apk的時候也出現了一個這個錯誤。error Unknown failure (at android.os.Binder.execTransact(Binder.java:565)) Error while Installing APKs。通過Stackoverflow查詢網友們的評論,我也找到了解決方案。這是由於 我在移動專案的時候沒有重新生成專案,當我移動專案資料夾時,就我而言,Build-> Clean Project解決了這個問題。
還有其他的一些問題,是由於我的程式碼出現的問題我認為那種程式碼出現的問題只屬於個人問題,也容易解決,通過檢視log就可以解決這些問題,而我在文章中提出的這些問題都是比較棘手的,給大家提供個解決方案用來參考。
八、專案分工
姓名 | 分工 | 工作比例 | 分數(10分) |
李凱 | UI,功能實現,資料庫 | 60% | 10 |
季軒石 | UI,部分功能實現 | 40% | 10 |