Skip to content

Commit

Permalink
なあPreferenceFragmentCompat.onPreferenceTreeClickくん、getCallbackFragmen…
Browse files Browse the repository at this point in the history
…t()が常にnull返すの辞めてくれ~?

Fragmentの階層に対応した
  • Loading branch information
takusan23 committed Nov 3, 2020
1 parent 5197ca1 commit b935ce8
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@ import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.core.view.postDelayed
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
import androidx.preference.*
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceGroup
import androidx.preference.PreferenceScreen
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView

Expand Down Expand Up @@ -51,20 +55,29 @@ class SearchPreferenceChildFragment : PreferenceFragmentCompat() {
}
}

/**
* このFragment([SearchPreferenceChildFragment])を置いてるFragment。
* */
private val searchPreferenceFragment by lazy { (requireParentFragment() as SearchPreferenceFragment) }

/**
* Fragment切り替えに失敗するので手直し
* その他にも検索結果押したときもandroid:fragment指定時はこれが使われる
* */
override fun onPreferenceTreeClick(preference: Preference?): Boolean {
// 高階関数を呼ぶ
searchPreferenceFragment.onPreferenceClickFunc?.invoke(preference)
if (preference?.fragment != null) {

// なんか文字列からFragment作ってる
val fragment = parentFragmentManager.fragmentFactory.instantiate(requireActivity().classLoader, preference.fragment)
val fragment = createFragment(preference.fragment)
// スクロール先のPreferenceの名前を入れるなど
fragment.arguments = Bundle().apply {
putString("scroll_key", preference.key)
putString("scroll_title", preference.title?.toString())
}
(requireParentFragment() as SearchPreferenceFragment).setFragment(fragment)
// Fragment設置
searchPreferenceFragment.setFragment(fragment)

/**
* スクロールを実行する。Fragmentのライフサイクルに合わせて書かないとエラーが出ちゃうから無理やり遅延させたりする必要があったりしたけど、
Expand All @@ -75,6 +88,7 @@ class SearchPreferenceChildFragment : PreferenceFragmentCompat() {
// ライフサイクルがonStart()のときに関数を自動で呼ぶ
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun preferenceScroll() {

/**
* もし遷移先Fragmentが[PreferenceFragmentCompat]なら該当Preferenceまでスクロールを実行させる
* */
Expand All @@ -83,6 +97,23 @@ class SearchPreferenceChildFragment : PreferenceFragmentCompat() {
val scrollKey = arguments?.getString("scroll_key")
// スクロール
scroll(getAllPreference(preferenceScreen), listView, scrollKey, scrollTitle)

// クリックイベントを上書きするか
val clickFunc = (requireParentFragment() as? SearchPreferenceFragment)?.onChildPreferenceFragmentCompatClickFunc
if (clickFunc != null) {
getAllPreference(preferenceScreen).forEach { preference ->
preference.setOnPreferenceClickListener {
// なお、第3階層目からエラーが出るので直す
val fragmentPath = preference.fragment
if (fragmentPath == null) {
// Fragment未設定時のみ
clickFunc(preference)
}
false
}
}
}
preferenceFragmentFix(fragment)
}
}
})
Expand All @@ -91,6 +122,50 @@ class SearchPreferenceChildFragment : PreferenceFragmentCompat() {
return false
}

/**
* [PreferenceFragmentCompat.onPreferenceTreeClick]のFragment置き換えがうまく動かないためそれを直す
*
* - なんで動かないの?
* - Preferenceを押すと、[PreferenceFragmentCompat.onPreferenceTreeClick]が呼ばれる
* - 親のFragment もしくは 親のActivity に [PreferenceFragmentCompat.OnPreferenceStartFragmentCallback] が実装されていれば、onPreferenceTreeClick()が呼ばれる
* - Activityの方なら動くが、Fragmentに関しては常にnullを返してるためFragmentに実装しても無駄
* - わざわざActivityに書かせるのもあれなので書いた
* */
private fun preferenceFragmentFix(preferenceFragmentCompat: PreferenceFragmentCompat) {
preferenceFragmentCompat.lifecycle.addObserver(object : LifecycleObserver {
// Fragmentがstartのときに呼ばれる
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onStart() {
getAllPreference(preferenceFragmentCompat.preferenceScreen).forEach { preference ->
// Fragmentが設定されている場合は、処理を変える
val fragmentPath = preference.fragment
if (fragmentPath != null) {
// そのままの実装でFragmentを置き換えるともれなくエラーが出るので書き換える
val fragment = createFragment(fragmentPath)
// 再帰的に呼ぶ
if (fragment is PreferenceFragmentCompat) {
preferenceFragmentFix(fragment)
}
// onPreferenceTreeClickを呼ばないため
preference.fragment = null
// fragmentが設定されているときのみクリックイベントを書き換え
preference.setOnPreferenceClickListener {
searchPreferenceFragment.setFragment(fragment)
false
}
}
}
}

})
}

/** 文字列からFragmentを生成する */
private fun createFragment(path: String): Fragment {
// なんか文字列からFragment作ってる
return parentFragmentManager.fragmentFactory.instantiate(requireActivity().classLoader, path)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

Expand Down Expand Up @@ -138,6 +213,9 @@ class SearchPreferenceChildFragment : PreferenceFragmentCompat() {
preference.title == preferenceTitle
}
}
if (pos == RecyclerView.NO_POSITION) {
return
}
// なんか遅延させると動く
listView.postDelayed(500) {
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import androidx.activity.addCallback
import androidx.appcompat.app.AppCompatActivity
import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import kotlinx.android.synthetic.main.fragment_search_preference_fragment.*

/**
Expand Down Expand Up @@ -72,6 +75,28 @@ open class SearchPreferenceFragment : Fragment() {

private var fragmentHostLayout: View? = null

/**
* 表示しているPreferenceを押したときに呼ばれる高階関数。
*
* [Preference.setOnPreferenceClickListener]の代わり
*
* */
var onPreferenceClickFunc: ((preference: Preference?) -> Unit)? = null

/**
* 切り替え先Fragmentが[androidx.preference.PreferenceFragmentCompat]だったときに、Preferenceを押したときに呼ばれる関数
*
* (遷移先Fragmentとは→Preference要素に「android.fragment」属性として指定したFragmentのこと。以下例)
*
* ```xml
* <Preference
* android:title="Fragment切り替え"
* android:fragment="ChildPreferenceFragment" // ここで指定したFragmentのこと
* />
* ```
* */
var onChildPreferenceFragmentCompatClickFunc: ((preference: Preference?) -> Unit)? = null

/** レイアウト指定 */
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_search_preference_fragment, container, false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ package io.github.takusan23.searchpreferencefragmentexample

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import com.google.android.material.snackbar.Snackbar
import io.github.takusan23.searchpreferencefragment.SearchPreferenceChildFragment
import io.github.takusan23.searchpreferencefragment.SearchPreferenceFragment
import kotlinx.android.synthetic.main.activity_main.*
Expand Down Expand Up @@ -31,6 +35,16 @@ class MainActivity : AppCompatActivity() {
putInt(SearchPreferenceChildFragment.PREFERENCE_XML_RESOURCE_ID, R.xml.preference)
}
fragment.arguments = bundle

// FragmentのPreferenceを押したときに呼ばれる高階関数
fragment.onPreferenceClickFunc = { preference ->
Toast.makeText(this, preference?.title, Toast.LENGTH_SHORT).show()
}

fragment.onChildPreferenceFragmentCompatClickFunc = { preference ->
Toast.makeText(this, preference?.title, Toast.LENGTH_SHORT).show()
}

supportFragmentManager.beginTransaction().replace(R.id.activity_main_fragment_host_frame_layout, fragment).commit()
true
}
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/xml/sub_preference.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<Preference
android:fragment="io.github.takusan23.searchpreferencefragmentexample.SubSettingFragment"
android:summary="Cupcake"
android:title="Android 1.5" />
<Preference
Expand Down

0 comments on commit b935ce8

Please sign in to comment.