はじめに
@androhiさんの記事「AndroidXでのバックキー制御」を読んで、これは使わない手はない!と思いプロダクトに導入し、その時にハマった点や補足を皆様に共有したいと思い記事にしました。
(@androhiさんに許可を頂いてます。ありがとうございます!)
ブログの内容を掻い摘んで説明すると、
Activity#onBackPressed()
でやっていたバックキーの制御をAndroidXからは onBackPressedDispatcher
が使えるようになったので、これを使ってFragmentでの実装を簡単にしよう!というやつです。
※ 2019/04/28 追記 activity:1.0.0-alpha07からコールバックの呼び出し方法が変更になりました。変更点はこちら
※ 2019/04/05 追記 appcompat:1.1.0-alpha04からコールバックの設定方法が変更になりました。変更点はこちら
インストール
- 利用にはandroidxのappcompat:1.1.0以上、activity:1.0.0以上が必要です。まずはこれらをインストールします。
dependencies {
implementation 'androidx.appcompat:appcompat:1.1.0-alpha04'
implementation 'androidx.activity:activity:1.0.0-alpha06'
}
- 2019/4/5時点の最新はappcompat:1.1.0-alpha04、activity:1.0.0-alpha06です。appcompat:1.1.0-alpha02についてはバグがありエラーが出るため使わないようにしてください。
`NoSuchMethodException: addFontWeightStyle [class java.lang.String, int, boolean]`っていうエラー出ててなんだろーなーと調べたら 'androidx.appcompat:appcompat:1.1.0-alpha02' 使うとだめっぽい。https://t.co/omgGlZfF5R
— たる (@tarumzu) 2019年3月1日
使い方
- 今まではFragmentで制御する場合、このように書いていましたが…
class OldActivity : AppCompatActivity() {
override fun onBackPressed() {
val fragment = supportFragmentManager.findFragmentById(R.id.fragment_container)
if (fragment != null && fragment is BackPressedListener) {
(fragment as BackPressedListener).onBackPressed()
} else {
super.onBackPressed()
finish()
}
}
}
interface BackPressedListener {
fun onBackPressed()
}
class OldFragment: Fragment(), BackPressedListener {
// 略
override fun onBackPressed() {
activity?.let {
val fm = it.supportFragmentManager
val backStackCnt = fm.backStackEntryCount
// フラグメントの戻り先ある場合は戻る。
if (backStackCnt > 1) {
fm.popBackStack()
}else {
// 戻り先無い場は合終了する
it.finish()
}
}
}
}
👇 次のように制御を簡単にできます!
// ActivityはandroidxのAppCompatActivityを継承
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
// 略
class EndFragment : Fragment() {
val mainActivity: MainActivity
get() = (activity as MainActivity)
val isOverrideBack = false
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
mainActivity.onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback {
override fun handleOnBackPressed(): Boolean {
if (isOverrideBack) {
// NOTE: tureを返すと何もしない。なのでバックの制御を上書いたり出来る。
xxx()
return true
}else {
// NOTE: falseの場合はfragmentがスタックに積まれていれば1つ戻る。
return false
}
}
})
}
以上です!とてもシンプルで最高ですね。
handleOnBackPressedでfalseを返す場合、 スタックに積まれたFlagment分すべて戻ってからActivityを終了させるといった動作になリます。
ただ、デフォルトの動作ではFragmentが載っていない、Activityのみのブランクページも表示されてしまうため、ブランクページを表示させたくない場合は別途下記のように書いてあげる必要があります。
mainActivity.onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback {
override fun handleOnBackPressed(): Boolean {
val fm = mainActivity.supportFragmentManager
val backStackCnt = fm.backStackEntryCount
// フラグメントの戻り先がない場合は終了する。
if (backStackCnt > 1) {
return false
}
mainActivity.finish()
return true
}
})
最後に
現時点(2019/04/28)ではまだalphaではありますが、前述のバージョンであれば動作も安定しており、コードも非常にシンプルになるのでプロダクトで採用しても問題ないかなと思ってます。気になった方はぜひ使ってみてください!