ME-App:我的履歷 App
終於能來寫這篇了~
最近在工作轉換期的空擋。之前自己一直很想寫一些東西,一來當作個人作品,二來也可以練習一些之前一直想玩,但是工作上沒用到的一些東西。
所以最近剛好有時間了,就來安排一下~
Play Store 已上架完成
要做一個 App,首先就要有個方向。 主要有兩個思考方向:
- 想要呈現什麼內容?
- 想用什麼技術元件?
內容部分,其實之前有過很多想法,工具類、遊戲類、社群類。 但是由於沒有太多資源可以利用,最後決定就做一個個人履歷的內容。
計劃階段
簡單明瞭,一來從技術面可以練習一些技術框架跟工具,二來從內容面也可以比較有彈性的呈現一般履歷無法呈現的效果。 而且內容都是自己的經歷,所以內容產出與整理都可以自己完成。
確定方向之後,再來就是 Brainstorming 來確認一下大概這個 app 的 scope 要多大
大概就是分成四大區塊:
- 工作經歷
- 做過的專案
- 擁有的技能
- 其他個人作品(Blog 文章)
然後工作經歷,專案與技能之間其實都互有關連,加上之前就想玩玩看 Room,於是這樣剛好就可以練習一下設計 SQLite 的關聯資料表。
設計階段
大概有個方向與具體內容之後,接下來就是進行初步的設計階段,這部分也分成幾大塊
App 介面設計
首先要先知道,User 用起來大概會是什麼樣子,要怎麼呈現 Mind map 上面的內容。 最後決定使用常見的底下有個 TabBar 然後用 List-Detail view 的架構
App 架構設計
大致知道 UI 要如何呈現後,就可以開始規劃 App 的架構了。 參考之前 MVVM 的設計模板 ,把頁面,ViewModel 跟資料的部分進行規劃與安排
這部分的設計思維是:
資料的部分,一來為了方便更新,二來為了練習,所以決定是從 Server 端即時更新。
首先利用一個 Config file 確認資料與結構的版本,如果確認本地的數據落後之後才需要更新。資料從 Server 下載後整理好並更新本地的 Database,之後所有的頁面就可以直接透過本地的 Database 存取資料。
Data Model 的部分,一樣跟之前提到的一樣,我會開一個 App 內部使用獨立的 App Entity,用來與 JSON parser POJO 與 Room Entity 區隔,然後寫一個 Adapter 工具來做資料格式的轉換。 這樣就能確保 UI / Server data / Database data 格式之間不會有太高的耦合度。
這樣就同時包含了 Retrofit/JSON parser 與 Room 的練習。
Server 數據這一塊,由於沒時間去研究 API 與 Backend 的撰寫,所以就直接把產好的 JSON file 直接放在 Firebase storage or AWS S3,然後透過 public link 直接當成 HTTP GET 的 path,這樣就可以模擬出 API 的效果。 數據要更新直接更新雲端檔案就好(不過要注意版本與 local cache 的問題)
後來因為 Firebase 還有提供其他的分析與測試功能,所以後來選擇使用 Firebase Storage
要用到 Database 之前,首先要來進行資料庫的設計。 這部分首先我是先在 Google Spreadsheet/Excel 上面整理好工作、專案、技能的資料
大概像這樣:
綜合考量資料屬性以及之後 UI 的呈現方式後,就可以大概知道所有的資料欄位,以及有哪些欄位是在 Table 之間需要關聯的。 哪些欄位是要放 reference ID,哪些欄位直接放 value 就可以。
像我原本設計 Company 跟 Position 是獨立的 Table,然後透過 Reference 的方式關聯公司,職務與專案。 後來發現公司與職務其實是 1-1 的關係,而且其他地方其實也用不到 Company ID,所以之後就改成直接把 Display name/image 放在職務(Job)的 table 裡面,然後專案的部分透過 job_id 關聯就可以。
之後就可以產出一份簡單的 DB Schema
實作階段
既然介面/技術架構設計都有了,接下來就是動手實作了~ 這部分其實也沒什麼好寫,就是按照規劃的架構與設計一步步完成。
具體的程式碼與 Git history 可以參考這個 app 的專案
目前做出來的效果大概如下
大致的 UI 架構與 Mockup 類似,不過做了一些微調。 並且加了一個 Splash screen,一來練習一下 Animation,二來也把 remote 的資料檢查,下載,處理在前期完成,這樣進入 app 後就只要存取 local database 就好(也可以當作離線版使用)
測試與微調階段
初步完成之後,其實就是花點時間測試跟介面上的微調,目前還在研究 Espresso 與 Firebase 的 Test Lab 服務。
目前是有計畫會把這個 App 上到 Google PlayStore,並正式當成自己的作品,不過現在 PlayStore 上架流程變得更複雜了... 跟之前 D3 App 的時候比起來更麻煩,而且有些步驟還要等 Google 審查,所以等上架完成後,再把連結放過來。
技術/技巧/工具整理
這邊就梳理一下這個 App 裡面有使用到的技術/工具
App 架構
- MVVM
- AndroidViewModel
- LiveData
UI 流程控制
- JetPack Navigation (with SafeArgs)
- ConstraintLayout
- CollapsingToolbarLayout
遠端資料與 App 數據分析
- Firebase Storage
- Firebase Crashlytics
- Firebase Analytics
Restful API:
- Retrofit
- Moshi
本地端 Database
- JetPack Room
圖片下載/Cache
- Glide
測試 (研究中)
- JUnit
- Espresso
- Firebase Test Lab
後記
其實這個計畫,一開始真的只是想當作練習與留個還能看的作品(之前的 D3 App 架構根本不能看...)
不過在整個實作過程,其實還是蠻有趣的。 尤其在資料收集時,首先要努力回想過去做過的專案,而且還要盡可能想辦法在網路上找到相關的圖片照片(有些早期的真的很難找.....),還要努力的回想當時專案的內容細節,以及盡可能地表達「我」實際在專案內負責的內容。
在技術上,的確這個 app 某方面也是要展示一些自己的技術能力,所以除了原本就熟悉的東西外,也想要練習一些新的工具與技巧,這階段也發現了很多以前不知道的一些 Android 上面的進化。 這邊就簡單提兩點:
String plurals
過去我們在畫面上要顯示一些文字的時候,可能有些情況會需要把本地文字與 Server 的數據結合後才呈現。
比方說在我這個專案內,我有個欄位要顯示某個技能在多少個專案內使用到。 所以我在 string.xml
裡面就需要加上一個
... <string name="projects_for_skill"> in %d projects </string> ...
然後在確認數字之後,在 code 裡面使用
val numOfProjects = data.numOfProjects textview.text = context.getString(R.string.projects_for_skill, numOfProjects)
但是這時候通常會有個問題,就是在英語或是某些歐洲語言中,單數與多數在呈現上會有一些差異(像是英文中複數就需要在結尾加上 s)
過去需要處理這中狀況,通常都會在 code 裡面在 getString()
先去做一些資料檢查,然後根據數字形態的不同而去取不同的 R.string.<id>
但是這次發現了其實 Android 現在有提供了這種功能:String plurals
用法上類似,就在 string.xml
裡面更新一下
... <plurals name="projects_for_skill"> <item quantity="one"> in a project </item> <item quantity="other"> in %d projects </item> </plurals> ...
quantity
在官網上有多種選項可以使用,不過以英語來說大概就是單數與複數,然後 Android 系統會根據不同 quantity
的定義取用不同的字串版本(有點像 <selector>
的用法)
然後在 Code 裡面更新一下使用方式
val numOfProjects = data.numOfProjects val res = context.resources textview.text = res.getQuantityString(R.plurals.projects_for_skill, numOfProjects, numOfProjects)
這邊有看到一個特別的點是,numOfProjects
需要丟兩次,按照官方文件的說明,第一個數字丟進去是為了讓 plurals 系統選擇目標字串,第二個數字才是用來做 string format (%d) 的用途。
所以在某些情況,比方說你的 1 想要顯示 one,那就第一個數字丟 1,第二個丟 'one' 字串,或是你的顯示文字裡面不用顯示到數字,那就丟第一個數字就好
另外要注意一點! 這個功能只在對應的語系中才會有作用!像是我放了這個 plurals 的功能,如果手機的設置是在中文語系的話,就不會有作用(就都顯示 other 的 case)
Animations and Transitions
第二個就是 Animation 系統,雖然我在 HTC 的時候也簡單玩過一陣子最早期的 View animation,但是那時候的版本還很難用,而且效率很差。 並且會跟 UI 複雜的操作互相干擾與影響,所以後來實務上的專案也很少使用(也要看 UI/UX 有沒有設計,或是 Dev team 有沒有時間做),頂多是在 Activity / Fragment 畫面切換的時候做一些 Window transition 動畫。
不過這次在研究中,發現 Android 現在的 Animation 系統變得非常強大而已易用,不管是畫面(Activity / Fragment)之間轉換的動畫, View 的元件互動與連動的動畫也相當強大,而且新增的 Animator/AnimatorSet 與衍伸出來的 ValueAnimator / ObjectAnimator 讓整個動畫變得更好操作,以及整體效能是有完整的系統支援(而不是單純像以前只是開一個 Thread 然後不管其他人的播放而已)
這部分我在這個專案裡面還沒有用到很深,只有在 LauncherActivity
裡面玩了一下 ViewPropertyAnimator,原本想要在 Job/Skill 的 List/Detail page 玩一下 shared element transition,但是試了幾次效果不好,所以後來先拿掉了。
關於目前 Animation 的系統與內容,可以參考Android Dev Summit '18 的這個影片,蠻推薦 UI/UX designer 也可以看一下~ 除了 Material design 之外,還可以看一下 Android 裡面的元件可以利用原生的機制,做到怎麼樣的互動與效果!
Reference
最後也提供一些相關連結
- Project Source code:https://gitlab.com/mouseface99/me-app
- UI/架構設計工具:http://whimsical.com/
- 免費 Icon(不過要註明出處與作者):http://flaticon.com/
- 教學影片(大部分是 Android developer 官方的)
- Animation 相關:
- Shared Element Transition:https://www.youtube.com/watch?v=DkSlk03-opA
- Keyframe Animations:https://www.youtube.com/watch?v=OHcfs6rStRo
- Motional Intelligence: Build Smarter Animations:https://www.youtube.com/watch?v=f3Lm8iOr4mE
- 其他:
- Modular Architecture:https://www.youtube.com/watch?v=PZBg5DIzNww
- Jetpack Navigation:https://www.youtube.com/watch?v=JFGq0asqSuA
- Kotlin Coroutines:https://www.youtube.com/watch?v=ZTDXo0-SKuU
- Animation 相關: