動態配置
延續上一節靜態配置
在上一節提到靜態配置雖然較簡潔, 但若想靈活更換Fragment就得依靠 動態添加.替換Fragment。
再建一個新的Fragment
要替換當然要有兩個才看得出效果。
那就建立一個Fragment,取名叫做EditFragment。
然後將fragment_edit.xml修改成一個文字匡和一個送出按鈕
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.savesomthingdemo.EditFragment">
<!-- TODO: Update blank fragment layout -->
<Button
android:id="@+id/btnSubmitMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="送出"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/textInputMessage"
app:layout_constraintTop_toTopOf="@+id/textInputMessage" />
<EditText
android:id="@+id/textInputMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:ems="10"
android:hint="請輸入訊息"
android:inputType="textPersonName"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
效果:
修改activity_main.xml
要將靜態配置的xml轉成動態,改起來其實很方便。
只要將fragment 替換成 FrameLayout, 並將原本的android:name那行刪除就Ok了。
另外我添加一個SeekBar用來切換我們顯示的Fragment。
完整activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.savesomthingdemo.MainActivity">
<FrameLayout
android:id="@+id/demoFragment"
android:name="com.example.savesomthingdemo.TimeFragment"
android:layout_width="388dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/btnShowHide"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btnShowHide"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="隱藏"
android:textSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent" />
<SeekBar
android:id="@+id/seekbarChangeFragment"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginRight="32dp"
android:max="2"
android:progress="0"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toEndOf="@+id/btnShowHide"
app:layout_constraintTop_toTopOf="@+id/btnShowHide" />
</android.support.constraint.ConstraintLayout>
效果:
更改MainActivy
這時MainActivity絕對是出現一堆紅字XD 因為原本的Fragment變成了FrameLayout,所以導致一些屬性找不到。
可以用底下這行取代原本的Fragemnt,雖然長了一點。
val demoFragment = supportFragmentManager.findFragmentById(R.id.demoFragment)
完整MainActivity.kt
package com.example.savesomthingdemo
import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.app.AppCompatActivity
import android.util.Log
import android.widget.SeekBar
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
private val pageFragemnts: Array<Fragment> = arrayOf(EditFragment(), TimeFragment())
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
seekbarChangeFragment.setOnSeekBarChangeListener(object: SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(p0: SeekBar?, position: Int, p2: Boolean) {
Log.d("onProgressChanged", "位置:$position")
changeFragment(position)
}
override fun onStartTrackingTouch(p0: SeekBar?) {}
override fun onStopTrackingTouch(p0: SeekBar?) {}
})
btnShowHide.setOnClickListener {
val demoFragment = supportFragmentManager.findFragmentById(R.id.demoFragment) ?: return@setOnClickListener
val fragmentTransaction = supportFragmentManager.beginTransaction()
if (demoFragment.isHidden){
fragmentTransaction.show(demoFragment)
btnShowHide.text = "顯示"
}else{
fragmentTransaction.hide(demoFragment)
btnShowHide.text = "隱藏"
}
fragmentTransaction.commit()
}
}
fun changeFragment(position: Int) {
val fragmentTransaction = supportFragmentManager.beginTransaction()
val demoFragment = supportFragmentManager.findFragmentById(R.id.demoFragment)
when(position) {
2 -> {
fragmentTransaction.remove(demoFragment)
}
else -> {
fragmentTransaction.replace(R.id.demoFragment, pageFragemnts[position])
}
}
fragmentTransaction.commit()
}
}
MainActivty.kt 碎碎念補充說明
由上而下講,首先pageFragments是我們存放自訂Fragment的陣列, 方便後續替換Fragment。
seekbarChangeFragment.setOnSeekBarChangeListener 就是設定我們seekbar的監聽器。 裡面必須Override三個方法, onProgressChanged() 只要seekbar的拉條一拉動就會觸發。 onStartTrackingTouch() 從手指沒按突然按下seekbar的拉條時觸發。 onStopTrackingTouch() 從手指一直按著到突然放開seekbar的拉條時觸發。
此範例只使用到onProgressChanged(), 並利用此方法的第二個參數:position(位置)來決定我要更動哪個Fragment, 而切換哪個Fragment則定義在changeFragment()這個函數中。
changeFragment(position: Int)
新出現的就是replace和remove, replace就是把第一個參數id對映的的fragmentLayout的內容物替換成第二參數的fragment。 remove則是移除你填入的fragment。
有趣的是依據我參考的文件說明,remove會破壞該Fragment的實例,而replace則是remove() + add()的綜合體。
有趣的點在於說 我的程式明明就一直重複使用那些Fragment,但在替換(replace)時應該會摧毀才對, 但我的App依舊正常運作.沒有崩潰。 =W= Why?