遊戲頁面排完版了
接下來就建立下方藍色的分頁吧
分頁的功能在swift內叫 Tab Bar Controller
我們先把他拉進畫面
此時我們會看到預設的Tab Bar 有預設了兩個畫面
不需要留情~ 刪掉那兩個畫面
接下來按住control後從 Tab Bar Controller
拉到 遊戲畫面
同時將遊戲畫面
左邊的小箭頭
拉到 Tab Bar Controller
上
這個小箭頭代表程式進入的第一個View
然後我們點選 Tab Bar Controller
設定一下屬性
屬性 | 對齊 | 設定 |
---|---|---|
Image Tint | 無 | System Orange Collor |
Bar Tint | 無 | System Teal Color |
Background | 無 | System Teal Color |
回到 遊戲畫面 點選下方的 Bar Item
設定Title為遊戲, image為 gamecontroller
此時畫面多了Bar Item 因此遊戲頁面
下方白色區塊需要推高一點
點選gameFootrt的Autolayout設定
將Bottom -> BottomOf Parent
的設定多推40
接下來在拉一個 Table View Controller頁面
來顯示遊戲紀錄
設定Bar Item的Title為 遊戲紀錄
然後兩大頁面完成
接下來打算點選遊戲紀錄時
會開啟一個View來顯示遊戲紀錄詳情
於是我們在拉一個View Controller
被設定背景為 System Yellow Color
還後新增三個Label
如圖片顯示文字
AutoLayout都是距離邊框20
這邊就不再詳細說明了
下一步我們按著 control
從遊戲紀錄
畫面拉到 紀錄詳情
畫面
然後點選show
此時會建立一條 Segue
來達到換頁的效果
此時點選兩個頁面中的 Segue
並給他一個ID "showDetail"
經過一系列設定
關於畫面Tab分頁的設定就都完成摟!
至於 紀錄詳情
頁面如何進入
下個章節再來說明
kotlin 也提供了一種導航框架
來處理分頁問題
只是稍微複雜一點點
我們往下看吧
以下是使用Safe Args前
需要先知道的知識點
我們現在想要的畫面是
下方有兩顆分頁按鈕
分別可到 遊戲畫面
與 遊戲紀錄
畫面
同時遊戲紀錄又
可以點擊
跳到遊戲詳情
畫面
整理一下吧
元件名稱 | 數量 | 說明 |
---|---|---|
Active | 1 | 最外層包一切 |
NavHostFragment | 1 | 掛載在Active內, 用來顯示Fragment |
BottomNavigationView | 1 | 掛載在Active內, 用來切換Fragment |
Navigation Graph | 1 | 設定頁面導航 |
Menu | 1 | 設定按鈕選單 |
Fragment | 3 | 遊戲頁, 遊戲紀錄, 紀錄詳情 |
準備開始動手
首先安裝依賴
先到頂級build.gradle(Project:chick_bb)
dependencies {
...
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.3.5"
...
}
在到應用級build.gradle(Module:chick_bb.app)
plugins {
...
id 'androidx.navigation.safeargs.kotlin'
...
}
dependencies {
...
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
...
}
然後SYNCK 同步完成後
就可以往下摟~
Android所需步驟多又繁雜
看清楚摟
首先在res內新增一個資料夾叫menu
再menu內新增一個menu File叫 menu_bottom_nav
進入 menu_bottom_nav 從上方拉入兩個Menu item
設定如下
遊戲item
屬性 | 值 |
---|
icon|@android:drawable/ic_menu_myplaces|
id|gameFragment|
title|遊戲|
紀錄item
屬性 | 值 |
---|
icon|@android:drawable/ic_menu_day|
id|historyFragment|
title|紀錄|
第一個重點來了!!
導航要順利關聯的重點在id
接下來menu
, Navigation Graph
, Fragment
的id
全都要一樣, 才能完成導航喔
接下來回到 active_main.xml
新增NavHostFragment
與BottomNavigationView
在添加 NavHostFragment
會跳出畫面
要你選擇 Navigation Graph
理所當然的~我們不會有
那就來新增吧
點選左上的+ 並新增
命名為 nav_graph
此時回產生一個nav_graph 點選OK
此時進入 res/navigation/nav_graph
此畫面可建立導航地圖
點選上方的+圖示
選擇create new destination
接下來就產生 Fragment 了
我們先分別產生 GameFragment
與 HistoryFragment
此時會產生四個檔案
GameFragment.kt
HistoryFragment.kt
fragment_game.xml
fragment_history.xml
nav_graph 內會出現兩個畫面
有房子的是預設第一個顯示的 Fragment
請依序將他們兩個的id設定為 gameFragment
與 historyFragment
並進入fragment_game.xml
與fragment_history.xml
將最外層的 FrameLayout轉成 ConstraintLayout
接下來將
fragment_game.xml
最外層 id 設定成 gameFragment
fragment_history.xml
最外層 id 設定成 historyFragment
此時確認一下
menu
, Navigation Graph
, Fragment
的id
都要一樣
此時回到active_main.xml 檢查一下
並設定 NavHostFragment
與BottomNavigationView
元件
nav_host_fragment(NavHostFragment)
屬性 | 對齊 | 設定 |
---|
id| 無 |nav_host_fragment|
name| 無 |androidx.navigation.fragment.NavHostFragment|
layout_width| 無 |0dp|
layout_height| 無 |0dp|
defaultNavHost| 無 |true|
navGraph| 無 |@navigation/nav_graph|
Start -> StartOf| parent |0dp|
End -> EndOf| parent |0dp|
Top -> TopOf| parent |0dp|
Bottom -> BottomOf | bottom_nav |0dp|
bottom_nav(BottomNavigationView)
屬性 | 對齊 | 設定 |
---|
id| 無 |bottom_nav|
layout_width| 無 |0dp|
layout_height| 無 |50dp|
menu| 無 |@menu/menu_bottom_nav|
Start -> StartOf| parent |0dp|
End -> EndOf| parent |0dp|
Bottom -> BottomOf | parent |0dp|
畫面設定好了
接下來必須把 bottom_nav 導航的功能
綁定到 nav_host_fragment 上面
進入 MainActivity
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 綁定 bottom_nav 與 nav_host_fragment
val navHostFragment =
supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navController = navHostFragment.navController
findViewById<BottomNavigationView>(R.id.bottom_nav)
.setupWithNavController(navController)
}
這樣就完成綁定摟
接下來搬移畫面與資料
進入 active_main.xml
切換到 code模式
將除了 nav_host_fragment
與 bottom_nav
之外的元件剪下
通通貼到 fragment_game.xml 內
進入 MainActive 將動畫的方法複製到 GameFragment
這邊帶入一個 Fragment 知識點
Fragment 實際上是 active的一個片段
所以動畫程式中的 findViewById
是不能使用的
甚至在Fragment 也是沒有 view 可以直接使用的
竟然這樣 我就來順便來開啟
Android 一個很方便的功能 view binding
首先進入 build.gradle 有 .app的那個
加入
buildFeatures {
viewBinding true
}
接著進入另一個 build.gradle
於dependencies加入
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.3.5"
填入後畫面右上方會出現 Sync Now 點下去讓他同步
回到 GameFragment 預設 Fragment模板上
有一些我們暫時不需要的程式
我們來將程式修改一下
package com.test.chickbb
import android.animation.Keyframe
import android.animation.ObjectAnimator
import android.animation.PropertyValuesHolder
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.test.chickbb.databinding.FragmentGameBinding
class GameFragment : Fragment() {
// FragmentGameBinding 是自動產生的類別 用來綁定視圖
private var _binding: FragmentGameBinding? = null
// FragmentGameBinding 預設下有可能是null ,
// 透過這個get 方便在正確取資料時 不用加上問號
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
_binding = FragmentGameBinding.inflate(inflater, container, false)
// 執行動畫
setChickAnimation()
return binding.root
}
override fun onDestroy() {
super.onDestroy()
// 離開畫面要移除參照
_binding = null
}
fun setChickAnimation() {
// translationX
val pvhtranslationX = PropertyValuesHolder.ofKeyframe("translationX",
Keyframe.ofFloat(0f, 0f),
Keyframe.ofFloat(.1f, -33f),
Keyframe.ofFloat(.2f, -66f),
Keyframe.ofFloat(.3f, -99f),
Keyframe.ofFloat(.4f, -66f),
Keyframe.ofFloat(.5f, -33f),
Keyframe.ofFloat(.6f, 0f),
Keyframe.ofFloat(.7f, 40f),
Keyframe.ofFloat(.8f, 100f),
Keyframe.ofFloat(.9f, 40f),
Keyframe.ofFloat(1f, 0f)
)
// translationY
val pvhtranslationY = PropertyValuesHolder.ofKeyframe("translationY",
Keyframe.ofFloat(0f, 0f),
Keyframe.ofFloat(.1f, -20f),
Keyframe.ofFloat(.2f, 0f),
Keyframe.ofFloat(.3f, -20f),
Keyframe.ofFloat(.4f, 0f),
Keyframe.ofFloat(.5f, -20f),
Keyframe.ofFloat(.6f, 0f),
Keyframe.ofFloat(.7f, -20f),
Keyframe.ofFloat(.8f, 0f),
Keyframe.ofFloat(.9f, -20f),
Keyframe.ofFloat(1f, 0f)
)
// rotation
val pvhRotation = PropertyValuesHolder.ofKeyframe("rotation",
Keyframe.ofFloat(0f, 10f),
Keyframe.ofFloat(.1f, -10f),
Keyframe.ofFloat(.2f, 10f),
Keyframe.ofFloat(.3f, -10f),
Keyframe.ofFloat(.4f, 10f),
Keyframe.ofFloat(.5f, -10f),
Keyframe.ofFloat(.6f, 10f),
Keyframe.ofFloat(.7f, -10f),
Keyframe.ofFloat(.8f, 10f),
Keyframe.ofFloat(.9f, -10f),
Keyframe.ofFloat(1f, 10f)
)
// scaleX
val pvhScaledBy = PropertyValuesHolder.ofKeyframe("scaleX",
Keyframe.ofFloat(0f, 1f),
Keyframe.ofFloat(.1f, 1f),
Keyframe.ofFloat(.2f, 1f),
Keyframe.ofFloat(.3f, 1f),
Keyframe.ofFloat(.4f, -1f),
Keyframe.ofFloat(.5f, -1f),
Keyframe.ofFloat(.6f, -1f),
Keyframe.ofFloat(.7f, -1f),
Keyframe.ofFloat(.8f, -1f),
Keyframe.ofFloat(.9f, 1f),
Keyframe.ofFloat(1f, 1f)
)
// 透過 binding 取得 ggView
val ggView = binding.ggView
// 設定 ggView 關鍵影格
ObjectAnimator.ofPropertyValuesHolder(ggView,
pvhtranslationY,
pvhtranslationX,
pvhRotation,
pvhScaledBy).apply {
duration = 4000 // 動畫持續四秒
repeatCount = ObjectAnimator.INFINITE // 無限重播
start() // 開始播放
}
}
}
Fragment與Active 生命徵期略有不同
要特別注意一下
取元件的方法也修改成 binding.ggView
到這邊 原本動畫應該可以執行
而且下方換頁也應該要正常摟
目前Fragment 只有兩個
差最後一個
我們進入 Navigation Graph
新增一個 showDetailFragment
然後在 historyFragment 右邊有個藍點點
從 historyFragment 拉到 showDetailFragment
整個導航地圖將會變成這樣
完成~ 接下來開始寫遊戲摟
這樣代表可以在 historyFragment 裡面
透過某個方法跳頁到 showDetailFragment
這樣整個tab暫時先設定完成了
到tab分頁這邊開始
開始感受到Swift與Kotlin之間比較明顯的差異了
Swift整個功能是準備好的
你只要拉到畫面上 稍微設定一下就完成了
Kotlin要設定很多東西
主要是很多小細節 只要漏了
功能就會無法運作
但最終達成的效果幾乎是一樣的
只是差在開發其順不順手而已
今天篇幅大爆炸!
累死我了/︿\
鐵人賽進入的23天
在一週後就不用天天發文了
怎麼突然感覺....有點空虛 ( ・◇・)?
哇賽!我找虐啊!!
自虐啊我~
算了加油加油~剩下最後一週
衝刺!