You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

切换暗黑主题后返回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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 23:49:59