當我第一次通過Kotlin和Compose來實現一個Canvas時, 我收穫了什麼?

2023-04-27 15:01:52

當我第一次通過Kotlin和Compose來實現一個Canvas時, 我收穫了什麼?

自從2019年Google推薦Kotlin為Android開發的首選語言以來已經經歷了將近四年的時間, Compose的1.0版本也釋出了將近2年的時間, Kotlin+Compose在現階段的Android開發過程中還遠遠達不到主流的程度. 我們是否應該開始嘗試這個組合? 這個組合有會給我們帶來什麼?

對於我來說, 我是個守舊又喜新的人, 自2018初我就嘗試用Kotlin來完成一些Android的工作了(Android For Bezier), 但是一直沒有將kotlin作為我個人的Android首要開發語言. 不過隨著Kotlin+Compose這個組合社群的完善, 越來越多的人開始嘗試這個組合, 併為這個組合的社群構建添了加自己的一份力. 得益於Kotlin和Java之間可以無感的互相呼叫, 越來越多的人(包括我)開始嘗試將Kotlin或Kotlin+Compose加入到現有的開發專案中, 本文將展示一些我在這個組合中遇到的收穫和思考.

從一個自定義Canvas開始

先看下效果, 動畫原始效果非原創, 在結合實際功能效果的情況下進行了二次設計.

最終的實現結果還是比較滿意的, 額外的新增了許多的動畫效果. 或者說, 顯示的每個部分都是有自己的動畫的.

先說說收穫和思考

一般來說我都會從設計到程式碼的實現過程依次講解, 但是這次我先講收穫和思考的原因, 主要是因為整個設計到程式碼實現的過程在整體上和原來的開發(Java)沒有什麼本質的區別. 當然, 後面仍然會提供對這個動畫的詳細說明.

  1. Kotlin和Java
    Kotlin和Java之間的比較和關係我就不多贅述了, 不過通過在這個Canvas的Coding過程中, 對於Kotlin的使用和理解有著更深入的理解. 比較時兩種語言, 雖然兩者的關係十分的密切, 但是在真正使用的使用, 還是要避免將一種語言的習慣帶入到另一種.

  2. Compose
    最初的我項做一個通過Compose來實現的gist desktop工具, 不過當時的種種問題和資料的缺少讓我最終沒有完成這個專案. 當我真正在專案中使用Compose後, 我們才能理解Compose應用如何使用, 僅靠他人的介紹是完完全全達不到的體驗效果. 這個對大多數一直深耕於Android的開發者們來說是十分明顯的.

    其中首當其衝的就是響應式佈局的思路, 和我們最初使用的xml佈局所帶來的習慣其中的差異是十分巨大的.

    當我們通過xml來實現我們的佈局時(乃至我們直接通過 View.add() 構建的時候), 我們都是先建立一個View, 我們持有這個View的"把柄", 我們可以通過這個"把柄"來對這個View在任何時間任何地點做任意的修改.

    而當我們使用Compose進行佈局的時候, 我們需要現將Compose(Compose 就是Compose, 它和View是同級的)定義好, 告訴它應該在什麼情況下需要做什麼. 就像玉兔號一樣, 在地面的時候你可以為他新增各種的工具(履帶, 攝像頭...), 但是當你把它發射都月球上後, 哪怕是改一下表面的花紋都無法做到了.

    最初的Coding過程中, 由於固有思路的原因, 想著先實現一部分的功能, 然後看下實現的效果在逐步新增相關的功能. 但是當我實現了某個效果再回來的時候, 在一小部分的情況下, 我不得不對現有的程式碼進行很大的修改. 同時我也確認過, 如果不使用Compose的話, 是不需要改動如此大的. 這裡就是我在這個專案中對我觀念轉變最大的地方. "如果沒有設計完成, 就不要去實現它", 平時的這個問題被我們拿住"把柄"的View所掩飾了.

    換句話說, 使用Compose就像使用各種Builder一樣, 當我們沒有build()的時候, 我們可以做任何事情, 一旦我們完成build()了, 我們就不能這樣隨心所欲的控制它, 想想我們的AlertDialog.Builder().create().

  3. Kotlin Compose和Java XML
    在整個程式碼中, 我都儘量使用Kotlin+Compose來進行實現各種功能, 就像Kotlin和Java直接可以很方便的互相使用, Compose和View直接也可以很簡單的互相融合, 在Coding過程中, 經驗的不足和對Kotlin Compose的不熟悉使得很多看似簡單的功能遲遲無法實現, 甚至一些效果對我來說, 不使用老方法我無法做到. (即便如此, 仍使用了一部分ValueAnimator而不是 rememberInfiniteTransition)

  4. 會讓我更多的使用Kotlin和Compose麼?
    經過一段時間的使用後(不僅僅是這個demo, 還有在實際工作中的使用), 我認為我會嘗試更多的Kotlin程式碼和用Compose來構建頁面. 通常來說的Kotlin相對於Java來說程式碼量會更少, 不過現在的各種輔助開發工具(Copilot, ChatGpt)使其"寫更少的程式碼"看起來並不是十分能吸引人, 俗話說的好"紙上得來終覺淺,絕知此事要躬行", 當你真正的使用一段時間之後, 你會發現寫的少, 不僅僅是寫的少, 也代表了看的少, 理解的少, 改的少, 維護的少.當然, 這些都是建立在一定基礎上的, 在瞭解一定的特性和約定後才能達到, 不然最多的感受可能只有"這裡的功能是什麼? 這裡為什麼要這麼設計?", (是吧 協程).

    如果說Java的學習難度是線性的, 那麼Kotlin的學習難度我認為是拋物線的形狀, 入門比Java更簡單, 但是稍一深入, 由於有大量約定的特性, 使其中期難度比Java更難. 當然, 後期深入精通的部分都是需要不斷的學習和使用的.

    這導致了很多人初期使用的時候感覺很省心, 但是當嘗試使用或者深入學習的時候, 會發現很多的地方都無從下手, 或者預期的功能無法實現. 一是固有思維的作祟, 二是你認為學習完成的基礎知識還不足以讓你進行下一步. 這可能就是"基礎知識陷阱"吧. 最初的我, 認為Kotlin是Java的另一種實現形式, 認為直接的嘗試是沒有問題的. 但是就像是View和Compose直接的關係一樣, 雖然兩種之間都可以很輕易的互相使用, 但是, View就是View, Compose就是Compose, Kotlin就是Kotlin, Java就是Java. 兩者之間的關係並不是替代和補充. 而是實現同一目的的不同思路. "條條大路通羅馬"不是麼? 所以當你使用Kotlin和Compose的時候, 放棄一下固有的思維和觀念, Kotlin不是替代品, 它是另一條路.

對於我來說, 最大的問題是不可避免的使用了原有的思路來解決新的問題, 當然, 這也是在整個過程當中思考最多的, 多嘗試一下新的思路, 走出舒適圈. 才能有更大的提升.(記得設計完成了再實現, 如果你不想重新設計好幾次你的程式碼的話.)

看看我們的Canvas

思考和收穫都寫完, 下面我們來看看這個看起來還不錯的Canvas是如何實現的吧.

整體看來分為了四層, 分別是背景, 白天的雲彩, 夜晚的星星, 以及太陽和月亮.

為了可以很明確的看到canvas的設計, 背景這裡沒有限制顯示範圍,可以看到這裡使用了四個不同半徑但是圓心在同一點的圓, 用來模擬不同層級光的效果.

白天的雲層為了方便檢視更改為了藍色來檢視. 雲層的是由7個2層大小位置都不近相同的圓形繪製而成.

夜晚的星星也比較簡單, 在隨機的位置顯示菱形即可.

太陽是最簡單一個設計了, 只新增了一個顏色變化的效果.

月亮的設計也相對簡單, 新增了一個轉動的效果.

最後來看整體的設計還是比較簡單的. 但是陰影的部分耽誤了很多的時間(甚至部分效果未達到預期就沒有使用).

相關的程式碼在[我的GitHub]中(https://github.com/clwater/AndroidComposeCanvas)