切换暗黑主题后返回MainFragment,TextInputEditText监听无意义触发如何解决?
问题:切换暗黑主题后返回MainFragment,TextInputEditText的TextWatcher无意义触发
我在MainActivity下的SettingsFragment中按如下方式切换暗黑主题,MainActivity可正常重建,但返回MainFragment后,TextInputEditText(代码中的searchBarEditText)的beforeTextChanged、onTextChanged、afterTextChanged方法会被无意义触发,请问如何避免该问题?
SettingsFragment代码
var sharedPreferences = requireActivity().getSharedPreferences("Mode", Context.MODE_PRIVATE) var isNightMode = sharedPreferences.getBoolean("nightmode",false) with(binding){ if (isNightMode) darkThemeSwitch.isChecked = true darkThemeSwitch.setOnCheckedChangeListener { compoundButton, b -> if (b){ AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) sharedPreferences.edit().putBoolean("nightmode",true).apply() } else { AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) sharedPreferences.edit().putBoolean("nightmode",false).apply() } } }
MainFragment代码
class MainFragment : Fragment(){ private var _binding : FragmentMainBinding? = null private val binding get() = _binding!! private val placeList = arrayListOf<Place>() private var placesAdapter : PlacesAdapter? = null private val TAG = "TRVL_MainFragment" private lateinit var db : FirebaseFirestore private var kullaniciAdi = "" private var email = "" private var userUid = "" override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { _binding = FragmentMainBinding.inflate(inflater,container,false) val view = binding.root return view } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) Log.i(TAG,"onViewCreated") initializeFirebaseTools() createRecyclerView() settings() getPlaces() } private fun settings() { val settingsButton = headerView.findViewById<ImageView>(R.id.settingsButton) settingsButton.setOnClickListener { binding.drawerLayout.closeDrawer(GravityCompat.START) val action = MainFragmentDirections.actionMainFragmentToSettingsFragment() Navigation.findNavController(it).navigate(action) } } private fun initializeFirebaseTools() { db = FirebaseFirestore.getInstance() userUid = mAuth.currentUser?.uid.toString() } private fun createRecyclerView() { Log.i(TAG,"createRecyclerView and Adapter") placesAdapter = PlacesAdapter(placeList) binding.recyclerView.layoutManager = LinearLayoutManager(requireContext()) binding.recyclerView.adapter = placesAdapter } private fun getPlaces() { Log.i(TAG,"getPlaces") var collectionTAG = "" when(localeCode){ "tr" -> collectionTAG = "Places" "en" -> collectionTAG = "PlacesEng" else -> collectionTAG = "Places" } db.collection(collectionTAG).addSnapshotListener { value, error -> if (error != null){ Toast.makeText(requireContext(),"Error: " + error.localizedMessage,Toast.LENGTH_LONG).show() } else { if (value != null){ Log.i(TAG,"value is not null") if (!value.isEmpty){ Log.i(TAG,"value is not Empty") placeList.clear() val documents = value.documents for (document in documents){ val name = document.getString("name") val desc = document.getString("desc") val photos = document.get("photos") as ArrayList<String>? val placeId = document.getString("placeId") val productId = document.getString("productId") if (name != null && desc != null && !photos.isNullOrEmpty() && placeId != null && productId != null){ val place = Place(name,desc,photos,placeId,productId) placeList.add(place) Log.i(TAG,"placeList -> " + placeList.size) } else { Log.e(TAG,"one of the parameters is null") } } placesAdapter?.notifyDataSetChanged() } } } } binding.searchBarEditText.addTextChangedListener(object : TextWatcher { override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) { Log.i(TAG,"beforeTextChanged: " + charSequence.toString()) } override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) { Log.i(TAG,"onTextChanged: " + charSequence.toString()) } override fun afterTextChanged(editable: Editable) { val text = editable.toString() Log.i(TAG,"afterTextChanged: " + text) placesAdapter?.filter(text) } }) } override fun onDestroyView() { super.onDestroyView() _binding = null } }
解决方案
问题原因
切换主题时Activity会重建,MainFragment的视图会重新初始化,系统会自动从savedInstanceState中恢复TextInputEditText的文本内容,这个恢复过程会触发TextWatcher的回调,但这并非用户主动输入导致的,所以属于无意义触发。
解决方法
方法1:标记文本变化来源,过滤系统恢复的触发
在MainFragment中添加标记变量,区分是用户输入还是系统恢复文本的触发:
class MainFragment : Fragment(){ // 新增标记变量 private var isTextRestored = false override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // 若存在savedInstanceState,说明是重建场景,标记为文本待恢复 isTextRestored = savedInstanceState != null // 原有代码... } private fun getPlaces() { // 原有代码... binding.searchBarEditText.addTextChangedListener(object : TextWatcher { override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) { if (isTextRestored) return Log.i(TAG,"beforeTextChanged: " + charSequence.toString()) } override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) { if (isTextRestored) { // 系统恢复完成后重置标记 isTextRestored = false return } Log.i(TAG,"onTextChanged: " + charSequence.toString()) } override fun afterTextChanged(editable: Editable) { if (isTextRestored) return val text = editable.toString() Log.i(TAG,"afterTextChanged: " + text) placesAdapter?.filter(text) } }) } }
方法2:延迟添加TextWatcher
利用view.post()延迟添加监听,等系统完成文本恢复后再绑定回调:
private fun getPlaces() { // 原有代码... view.post { binding.searchBarEditText.addTextChangedListener(object : TextWatcher { // 原有回调实现... }) } }
方法3:禁止保存输入框状态(适合不需要保留输入内容的场景)
在布局文件中给searchBarEditText添加属性,禁止系统保存和恢复其文本:
<com.google.android.material.textfield.TextInputEditText android:id="@+id/searchBarEditText" android:saveEnabled="false" <!-- 其他属性 --> />
内容的提问来源于stack exchange,提问作者Rüçhan Okal
相关产品推荐
相关产品推荐

