安卓開發(一)時間管理應用DayPlay

本篇主要講本科時寫的一個app。從應用開發的流程到最後代碼落到實地,涉及設計模式、代碼編寫等相關知識。代碼https://github.com/goodluckcwl/DayPlan

需求分析:

日常生活中,人們常常會因爲拖拉而無法按時完成任務。“你有多少時間,就會花多少時間做一件事”,似乎是常人的通病。也許你開始時躊躇滿志,目標明確,可是時間一長往往就會偏離目標,將時間浪費在一些無謂的事情上。缺乏適時的、足夠多的提醒是主因。
當前,市場上有足夠多的優秀日程管理應用,如Wunderlist等。這些應用大多提供日程記錄、日程優先級、鬧鐘提醒、同步等功能。但這些應用的任務事項時間跨度都比較大、期限也比較長,提醒功能比較被動。這雖然有利於把握大的方向,但對於短期內效率的提高不一定有很好的效果。從另一個角度上看,養成良好的時間管理習慣,既需要着眼於長遠的目標,也需要有具體的、可行的短期實踐方案。所謂短期,我認爲一天比較合適。
本應用並不致力於像Wunderlist一樣具有十分全面的功能。而是專注於爲拖拉者做好一天(甚至更短)的規劃,通過主動的、多次的提醒及時檢查、糾偏,讓拖拉者逐漸成長爲高效的成功人士。

設計理念:

一天計劃,任務事項的主動、多次提醒,及時糾偏。

基本功能:

1、添加任務功能:可以添加任務事項,包括任務描述,任務屬性(4類:重要緊迫、重要不緊迫、緊迫不重要、不緊迫不重要),任務標籤,截止時間,圖片等
2、提醒功能:包括兩個模式:①普通模式:任務截止時間提醒②強迫模式:根據不同的任務屬性,間隔一定的時間提醒一次
3、根據任務標籤的搜索功能
4、分享:微信、QQ等

擴展功能:

提醒內容的自定義、任務完成情況總結表單等(待定)

DayPlan最終效果

DayPlan打開後,首先顯示一個任務列表,如圖1,任務列表左側以不同顏色標記任務的緊迫性,同時可以看到任務的標題、日期、截止時間、是否完成等信息。
用戶點擊某項任務後,將進入編輯頁面,在該頁面可以修改任務標題、截止時間,添加提醒等。
這裏寫圖片描述

這裏寫圖片描述
在編輯界面點擊相機按鈕,進入拍照界面
這裏寫圖片描述
在編輯界面點擊圖片,進入查看圖片界面,用戶可以放大照片查看細節在任務截止時間到了的時候,DayPlan彈出一個窗口,給出簡短的提醒信息
這裏寫圖片描述

功能詳細描述

(一)規劃功能:
用戶可以在前一天晚上做好第二天的規劃,添加必要的任務事項,包括任務描述,任務的緊迫性(重要緊迫、重要不緊迫、緊迫不重要、不緊迫不重要),截止時間,並且可以添加一張相關的圖片作爲提示。
(二)提醒功能
用戶可以爲每個事項添加提醒功能,每當截止時間到的時候,DayPlan給出一個簡短的提醒。DayPlan致力於在提醒用戶的同時不過分打擾用戶。
(三)分享功能
DayPlan可以從某個任務生成一個描述該任務的字符串,並分享到其他應用。

系統架構設計

根據經典的MVC設計模式,我們把系統劃分爲三層:模型層、視圖層、控制層。
(一)模型層
模型對象存儲着DayPlan的應用數據和業務邏輯。模型對象不關心用戶界面,它存在的唯一目的就是存儲和管理應用數據。DayPlan的模型對象主要是管理圖片的Photo類、管理任務事項的Plan類、進行數據存儲的DayPlanJSONSerializer類、應用運行過程中保存數據的數據池單例類SinglePlanLab類等。
(二)視圖層
視圖對象知道如何在屏幕上繪製自己及如何響應用戶的輸入,DayPlan中的視圖對象主要由layout文件中定義的各類組件構成。比如ZoomImageView類就是一個可以響應用戶手勢進行圖片縮放的視圖對象。
(三)控制層
控制對象包含應用的邏輯單元,是視圖與模型的聯繫紐帶。控制對象響應視圖突發的各類事件。在DayPlan中,控制對象主要由Fragment類的各個子類構成。
最後,給出DayPlan應用總體框圖
這裏寫圖片描述

各子模塊介紹

(一)模型層子模塊
模型層主要的模塊是數據處理模塊、數據存儲模塊。
1、數據處理模塊
主要由類Photo、類Plan、類SinglePlanLab處理。分別介紹如下:
①Photo類:保存圖片路徑的類。
②Plan類:保存任務事項的類。包含任務標題、任務截止時間等成員變量。
以上兩個類均提供了從JSON對象獲取數據的構造器及轉化爲JSON對象的方法,以方便數據存儲。
③SinglePlanLab類:保存應用中所有Plan對象的單例類。爲了保證數據的同步,應該把整個應用中的所有Plan對象集中保存在一個單例類裏,這樣數據就只有一份。其他對象從SinglePlanLab對象獲取需要的Plan對象的引用。因爲其他對象獲取的只是SinglePlanLab對象中Plan池中某個Plan的引用,所以當其他對象修改Plan時,實際上是對SinglePlanLab中的Plan對象進行操作,所以可以保證SinglePlanLab中的Plan將會得到同步更新。
這裏貼一下SinglePlanLab.java的代碼:

package cn.edu.zju.isee.www.dayplan;

import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.UUID;

import android.content.Context;
import android.util.Log;

public class SinglePlanLab {
    private static final String         TAG         ="SinglePlanLab";
    private static final String         FILENAME    ="plans.json";

    private static SinglePlanLab        sPlanLab;
    private ArrayList<Plan>             mPlans;
    private DayPlanJSONSerializer       mSerializer;
    private Context                     mContext;



    private SinglePlanLab(Context context){
        mContext=context;
//      mPlans=new ArrayList<Plan>();
        mSerializer=new DayPlanJSONSerializer(mContext, FILENAME);
//      mPlans.add(new Plan("","今天要去跑步"));
//      mPlans.add(new Plan("","信息論與編碼還有複習"));
//      mPlans.add(new Plan("","嵌入式系統設計課程作業"));
//      mPlans.add(new Plan("","網絡安全課程大作業期末..........."));
        try{
            mPlans=mSerializer.loadPlans();
//創建1000條信息用於測試
            Log.d(TAG,"load plans");
        }catch(Exception e){
            Log.d(TAG,"plans.json doesn't exist,create empty planLab");
            mPlans=new ArrayList<Plan>();
        }
    }


    public static SinglePlanLab getInstance(Context context){
        if(sPlanLab==null){
            sPlanLab=new SinglePlanLab(context);
        }

        return sPlanLab;
    }

    public void addPlan(Plan plan){
        mPlans.add(plan);
    }

    public void deletePlan(Plan plan){
        mPlans.remove(plan);
    }

    public Plan getPlan(int index){
        Plan p;
        try{
            p=mPlans.get(index);
        }catch(Exception e){
            Log.d(TAG,"getPlan out of range,return null");
            return null;
        }
        return p;
    }

    public Plan getPlan(UUID planId){
        for(Plan p:mPlans){
            if(p.getId().equals(planId)){
                return p;
            }
        }
        return null;
    }

    public ArrayList<Plan>getPlans(){
        return mPlans;
    }
    public int getLength(){
        int result=0;
        for(Plan plan:mPlans){
            result++;
        }
        return result;
    }

    public ArrayList<Plan> getPlanByLabel(String label){
        ArrayList<Plan>result=new ArrayList<Plan>();
        for(Plan plan:mPlans){
            if(plan.getLabel().equals(label)){
                result.add(plan);
            }
        }
        return result;
    }

    public ArrayList <Plan> getPlanByType(String type){
        ArrayList <Plan>result=new ArrayList<Plan>();
        for(Plan plan:mPlans){
            if(plan.getType().equals(type)){
                result.add(plan);
            }
        }
        return result;
    }

    public ArrayList <Plan>getPlanByDate(Date date){
        ArrayList<Plan>result=new ArrayList<Plan>();
        for(Plan plan:mPlans){
            if(plan.getDate().equals(date)){
                result.add(plan);
            }
        }
        return result;
    }

    public Boolean savePlans(){
        try{
            mSerializer.savePlans(mPlans);
            Log.d(TAG,"plans saved to file");
            return true;
        }catch(Exception e){
            Log.d(TAG,"error save plans");
            return false;
        }
    }

    public void clearPlans(){
        File f=new File(mContext.getFilesDir()+"/"+FILENAME);
        if(f.exists()){
            mContext.deleteFile(FILENAME);          
        }
        mPlans.clear();
    }
}

2、數據存儲模塊
DayPlan中處理數據保存的類是DayPlanJSONSerializer。在DayPlan中需要保存的數據是SinglePlanLab中的所有Plan類實例。DayPlan應用採用JSON保存數據,過程如下:
首先,Plan類包含了一個轉化爲JSON對象的方法、一個從JSON對象獲取數據並構造Plan實例的構造函數。保存數據時首先將Plan實例轉化爲JSON實例。
然後,DayPlanJSONSerializer類負責將JSON對象保存到應用的內部空間。
讀取數據時,DayPlanJSONSerializer從文件讀取數據並構造JSON對象,然後調用Plan的構造函數構造Plan對象。
由於SinglePlabLab維護着整個應用的數據,所以數據的保存與讀取由SinglePlanLab負責。SinglePlanLab類知道如何使用DayPlanJSONSerializer類讀取與保存數據。

(二)控制層子模塊
根據應用的功能,可將控制層劃分爲5個子模塊:添加任務模塊、刪除任務模塊、提醒模塊、拍照模塊,分享模塊。
1、添加任務模塊
主要由類PlanListFragment與PlanEditFragment類處理。當用戶在任務列表界面點擊某個任務的時候,PlanListFragment啓動PlanEditFragment並把用戶所點擊任務的對應的Plan實例的UUID傳遞給PlanEditFragment。PlanEditFragment利用UUID從SinglePlanLab獲取Plan對象並初始化視圖。
用戶修改數據後,PlanEditFragment從視圖層獲取數據,對Plan實例作相應的修改,完成更新數據層的功能。
2、刪除任務模塊
主要由PlanListFragment類處理。當用戶在任務列表界面長按操作時,進入刪除模式,可以選擇多個任務並刪除。PlanListFragment負責告知模型層哪些任務被刪除了。
3、提醒模塊
主要由類PlanEditFragment和AlarmActivity類處理。當用戶點擊“提醒我”按鈕時,PlanEditFragment啓動一個TimePicker,用戶輸入時間後,PlanEditFragment調用系統鬧鐘服務,在設定的時間啓動AlarmActivity,AlarmActivity則根據Plan的內容彈出提醒窗口。
4、拍照模塊
主要由類PlanEditFragment和PlanCameraFragment處理。當用戶點擊相機按鈕後,PlanEditFragment啓動PlanCameraActivity(管理PlanCameraFragment的Activity)。PlanCameraFragment使用系統相機API完成拍照、處理圖片、保存圖片的功能,並把圖片路徑傳遞給PlanEditFragment,PlanEditFragment根據圖片路徑生成一個Photo對象並添加到Plan實例裏。最後,PlanEditFragment刷新視圖層,用戶將看到剛剛拍攝的照片的縮略圖。
5、分享模塊
主要由類PlanEditFragment處理。當用戶點擊“分享”按鈕時,PlanEditFragment根據當前的Plan實例,生成一個包含任務標題、任務截止時間、任務緊迫情況、任務完成情況的字符串,然後將該字符串附加到一個隱式Intent裏,啓動其他應用,實現分享功能。

(三)視圖層子模塊
DayPlan的界面主要有:任務列表界面(圖1),編輯界面(圖2),拍照界面(圖3),查看照片界面(圖4)等。
1、任務列表界面
該界面需要顯示多個任務,並且根據用戶的上下滑動加載不同的任務並顯示。使用ListView可以方便地解決這個問題。
2、編輯界面、拍照界面
編輯界面由佈局文件fragment_edit_plan.xml完成。此外,爲了實現左右滑動切換到不同任務的編輯界面,我們使用PlanPagerActivity(包含ViewPager成員)來管理PlanEditFragment。
拍照界面由佈局文件fragment_plan_camera.xml完成。
3、查看照片界面
DayPlan自定義了一個ZoomImageView類,負責響應用戶手勢進行圖片的縮放。ZoomImageView類繼承自View類,包含一個Bitmap成員。ZoomImageView類檢測用戶的手勢操作,根據手指的座標計算出圖片縮放的比例,通過重寫View的OnDraw函數,將Bitmap按照所計算的縮放比例繪製到屏幕上。在用戶看來,就像進行了圖片的縮放一樣。

總結

DayPlan實現了一個時間管理應用的基本功能,但尚有以下不足:
DayPlan根據一段時間的任務完成情況繪製統計圖的統計功能尚未完成;DayPlan在查看照片模式中進行圖片縮放的時候,流暢度與系統自帶照片查看軟件相比還有差距;DayPlan在拍照過程中,由於直接調用了硬件相機,實現起來相對複雜,且拍照過程中亮度無法自動調節。但直接調用相機應用的方法則無法獲得全尺寸的照片,不能滿足DayPlan的需求;DayPlan在拍照後處理圖像的過程中由於沒有使用多線程,阻塞了UI線程;DayPlan在加載圖片的過程中阻塞了UI線程;DayPlan的界面有待美化。
DayPlan這些不足有待後續完善。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章