有鲫雪狐
有鲫雪狐

Reputation: 1

Screen rotation with configChanges causes Fragment ViewBinding to NPE

I have encountered a puzzling problem. When I create a Fragment in an Android View and create a ViewBinding inside the Fragment, and then declare android:configChanges="orientation|screenSize|screenLayout" in the manifest file, why does calling the ViewBinding created inside the Fragment by calling the Activity's onMultiWindowModeChanged() cause NPE of the ViewBinding created inside the Fragment when the screen is rotated?

Following is my source code:

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    private lateinit var viewPagerAdapter: ViewPagerAdapter
    private lateinit var fragmentAdapter: FragmentAdapter

    override fun onCreate(savedInstanceState: Bundle?) {
        enableEdgeToEdge()
        super.onCreate(savedInstanceState)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        viewPagerAdapter = ViewPagerAdapter(this)
        fragmentAdapter = FragmentAdapter()
        binding.viewPager.adapter = viewPagerAdapter
    }

    override fun onMultiWindowModeChanged(isInMultiWindowMode: Boolean, newConfig: Configuration) {
        super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig)
        fragmentAdapter.changeListStaggeredGridLayout()
    }

    override fun onConfigurationChanged(newConfig: Configuration) {
        super.onConfigurationChanged(newConfig)
        fragmentAdapter.changeListStaggeredGridLayout()
    }

}
class FragmentAdapter : Fragment() {

    private var _binding: RecycleViewMainBinding? = null
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
    ): View {
        _binding = RecycleViewMainBinding.inflate(inflater, container, false)
        binding.recycleView.adapter = RecycleViewAdapter((1..30).toList())
        changeListStaggeredGridLayout()
        return binding.root
    }

    // This method is called when the external activity triggers onMultiWindowModeChanged() and onConfigurationChanged()
    // Then it reports an error NPE: binding not initialized
    fun changeListStaggeredGridLayout() {
        val screenWidthDp = (Resources.getSystem().displayMetrics.widthPixels).pxToDp
        val screenHeightDp = (Resources.getSystem().displayMetrics.heightPixels).pxToDp
        binding.recycleView.apply {
            layoutManager = if (screenHeightDp >= 600) {
                when {
                    screenWidthDp in 600..840 -> StaggeredGridLayoutManager(
                        2, StaggeredGridLayoutManager.VERTICAL
                    )

                    screenWidthDp > 840 -> StaggeredGridLayoutManager(
                        3, StaggeredGridLayoutManager.VERTICAL
                    )

                    else -> LinearLayoutManager(this.context)
                }
            } else LinearLayoutManager(this.context)
        }
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}
class ViewPagerAdapter(context: Context) :
    FragmentStateAdapter(context as MainActivity) {

    override fun getItemCount(): Int = 1

    override fun createFragment(position: Int): Fragment {
        return FragmentAdapter()
    }
}
class RecycleViewAdapter(private val list: List<Int>) :
    RecyclerView.Adapter<RecycleViewAdapter.ViewHolder>() {

    inner class ViewHolder(binding: CardViewMainBinding) :
        RecyclerView.ViewHolder(binding.root)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val binding =
            CardViewMainBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return ViewHolder(binding)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    }

    override fun getItemCount(): Int {
        return list.size
    }
}

Upvotes: 0

Views: 18

Answers (0)

Related Questions