我的 Fragment 中有一个 RecyclerView,应用中有两个主题:Day、Night 和 System Default。
有一个奇怪的问题会导致 NullPointerException。如果我将主题切换到夜间并退出应用程序,然后再次进入,则 NullPointerException 崩溃并且应用程序将无法再次打开,直到我将其从手机或模拟器中删除。但是,如果我一直停留在浅色主题并关闭并再次打开应用程序,那么一切都会好起来的。
片段代码:
private var _binding: FragmentListBinding? = null
private val binding get() = _binding!!
private lateinit var rvAdapter: RvStatesAdapter
private var statesList = ArrayList<State>()
private var databaseReferenceStates: DatabaseReference? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentListBinding.inflate(inflater, container, false)
checkTheme()
initDatabase()
getStates()
binding.rvStates.layoutManager = LinearLayoutManager(requireContext())
binding.ibMenu.setOnClickListener {
openMenu()
}
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
private fun getStates() {
databaseReferenceStates?.addValueEventListener(object: ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
if (snapshot.exists()) {
for (stateSnapshot in snapshot.children) {
val state = stateSnapshot.getValue(State::class.java)
statesList.add(state!!)
}
rvAdapter = RvStatesAdapter(statesList)
binding.rvStates.adapter = rvAdapter
}
}
override fun onCancelled(error: DatabaseError) {
}
})
}
private fun initDatabase() {
FirebaseApp.initializeApp(requireContext());
databaseReferenceStates = FirebaseDatabase.getInstance().getReference("States")
}
private fun openMenu() {
binding.drawerLayout.openDrawer(GravityCompat.START)
binding.navigationView.setNavigationItemSelectedListener {
when (it.itemId) {
R.id.about_app -> Toast.makeText(context, "item clicked", Toast.LENGTH_SHORT).show()
R.id.change_theme -> {
chooseThemeDialog()
}
}
binding.drawerLayout.closeDrawer(GravityCompat.START)
true
}
}
private fun checkTheme() {
when (ThemePreferences(requireContext()).darkMode) {
0 -> {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
(activity as AppCompatActivity).delegate.applyDayNight()
}
1 -> {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
(activity as AppCompatActivity).delegate.applyDayNight()
}
2 -> {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
(activity as AppCompatActivity).delegate.applyDayNight()
}
}
}
private fun chooseThemeDialog() {
val builder = AlertDialog.Builder(requireContext())
builder.setTitle("Choose Theme")
val themes = arrayOf("Light", "Dark", "System default")
val checkedItem = ThemePreferences(requireContext()).darkMode
builder.setSingleChoiceItems(themes, checkedItem) {dialog, which ->
when (which) {
0 -> {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
(activity as AppCompatActivity).delegate.applyDayNight()
ThemePreferences(requireContext()).darkMode = 0
dialog.dismiss()
}
1 -> {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
(activity as AppCompatActivity).delegate.applyDayNight()
ThemePreferences(requireContext()).darkMode = 1
dialog.dismiss()
}
2 -> {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
(activity as AppCompatActivity).delegate.applyDayNight()
ThemePreferences(requireContext()).darkMode = 2
dialog.dismiss()
}
}
}
val dialog = builder.create()
dialog.show()
}
主题偏好类:
companion object {
private const val DARK_STATUS = ""
}
private val preferences = PreferenceManager.getDefaultSharedPreferences(context)
var darkMode = preferences.getInt(DARK_STATUS, 0)
set(value) = preferences.edit().putInt(DARK_STATUS, value).apply()
.xml 代码中的 RecyclerView:
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvStates"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="20dp"
android:background="@color/background"
app:layoutManager="LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tvLabelDescription"
tools:listitem="@layout/rv_state_list" />
以及来自 RecyclerView Adapter 的代码:
inner class MyViewHolder(val binding: RvStateListBinding): RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
return MyViewHolder(RvStateListBinding.inflate(LayoutInflater.from(parent.context), parent, false))
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val currentItem = stateList[position]
with(holder) {
with(stateList[position]) {
binding.tvState.text = this.name
Picasso.with(itemView.context)
.load(this.image)
.into(binding.ibState, object: Callback {
override fun onSuccess() {
binding.progressBar.visibility = View.GONE
}
override fun onError() {
}
})
itemView.ibState.setOnClickListener {
val action = StatesFragmentDirections.actionListFragmentToAttractionsFragment(currentItem)
itemView.findNavController().navigate(action)
}
}
}
}
override fun getItemCount(): Int {
return stateList.size
}