From b935ce884beeb2c6e226178ff72d06646a362e6d Mon Sep 17 00:00:00 2001 From: takusan23 Date: Tue, 3 Nov 2020 21:13:27 +0900 Subject: [PATCH] =?UTF-8?q?=E3=81=AA=E3=81=82PreferenceFragmentCompat.onPr?= =?UTF-8?q?eferenceTreeClick=E3=81=8F=E3=82=93=E3=80=81getCallbackFragment?= =?UTF-8?q?()=E3=81=8C=E5=B8=B8=E3=81=ABnull=E8=BF=94=E3=81=99=E3=81=AE?= =?UTF-8?q?=E8=BE=9E=E3=82=81=E3=81=A6=E3=81=8F=E3=82=8C=EF=BD=9E=EF=BC=9F?= =?UTF-8?q?=20Fragment=E3=81=AE=E9=9A=8E=E5=B1=A4=E3=81=AB=E5=AF=BE?= =?UTF-8?q?=E5=BF=9C=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SearchPreferenceChildFragment.kt | 84 ++++++++++++++++++- .../SearchPreferenceFragment.kt | 25 ++++++ .../MainActivity.kt | 14 ++++ app/src/main/res/xml/sub_preference.xml | 1 + 4 files changed, 121 insertions(+), 3 deletions(-) diff --git a/SearchPreferenceFragment/src/main/java/io/github/takusan23/searchpreferencefragment/SearchPreferenceChildFragment.kt b/SearchPreferenceFragment/src/main/java/io/github/takusan23/searchpreferencefragment/SearchPreferenceChildFragment.kt index 4dc07b4..1a89907 100644 --- a/SearchPreferenceFragment/src/main/java/io/github/takusan23/searchpreferencefragment/SearchPreferenceChildFragment.kt +++ b/SearchPreferenceFragment/src/main/java/io/github/takusan23/searchpreferencefragment/SearchPreferenceChildFragment.kt @@ -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 @@ -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のライフサイクルに合わせて書かないとエラーが出ちゃうから無理やり遅延させたりする必要があったりしたけど、 @@ -75,6 +88,7 @@ class SearchPreferenceChildFragment : PreferenceFragmentCompat() { // ライフサイクルがonStart()のときに関数を自動で呼ぶ @OnLifecycleEvent(Lifecycle.Event.ON_START) fun preferenceScroll() { + /** * もし遷移先Fragmentが[PreferenceFragmentCompat]なら該当Preferenceまでスクロールを実行させる * */ @@ -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) } } }) @@ -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) @@ -138,6 +213,9 @@ class SearchPreferenceChildFragment : PreferenceFragmentCompat() { preference.title == preferenceTitle } } + if (pos == RecyclerView.NO_POSITION) { + return + } // なんか遅延させると動く listView.postDelayed(500) { /** diff --git a/SearchPreferenceFragment/src/main/java/io/github/takusan23/searchpreferencefragment/SearchPreferenceFragment.kt b/SearchPreferenceFragment/src/main/java/io/github/takusan23/searchpreferencefragment/SearchPreferenceFragment.kt index dc54810..e48a7b7 100644 --- a/SearchPreferenceFragment/src/main/java/io/github/takusan23/searchpreferencefragment/SearchPreferenceFragment.kt +++ b/SearchPreferenceFragment/src/main/java/io/github/takusan23/searchpreferencefragment/SearchPreferenceFragment.kt @@ -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.* /** @@ -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 + * + * ``` + * */ + 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) diff --git a/app/src/main/java/io/github/takusan23/searchpreferencefragmentexample/MainActivity.kt b/app/src/main/java/io/github/takusan23/searchpreferencefragmentexample/MainActivity.kt index d1a2689..74ed550 100644 --- a/app/src/main/java/io/github/takusan23/searchpreferencefragmentexample/MainActivity.kt +++ b/app/src/main/java/io/github/takusan23/searchpreferencefragmentexample/MainActivity.kt @@ -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.* @@ -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 } diff --git a/app/src/main/res/xml/sub_preference.xml b/app/src/main/res/xml/sub_preference.xml index 74b37a4..e477743 100644 --- a/app/src/main/res/xml/sub_preference.xml +++ b/app/src/main/res/xml/sub_preference.xml @@ -1,6 +1,7 @@