#android #kotlin #android-fragments #android-viewpager #android-viewpager2
#Android #kotlin #android-фрагменты #android-viewpager #android-viewpager2
Вопрос:
Всякий раз, когда я использую тень уведомления для переключения между темными и светлыми темами устройства (мое приложение при запуске), по какой-то причине всегда происходит сбой. Минимальный API моего приложения — 29 (Android 10). Logcat указывает на строку кода, в которой причина ошибки не очевидна. ( import android.view.*
). Как я могу предотвратить повторение этого?
java.lang.NullPointerException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkNotNullParameter, parameter view
Класс активности
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (savedInstanceState == null) supportFragmentManager.beginTransaction()
.replace(R.id.detail_container, MainFragment())
.commitNow()
}
}
Основной класс фрагмента
package com.example.vp2
import android.content.Intent
import android.graphics.Color
import android.os.Bundle
import android.text.TextUtils
import android.view.*
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Spinner
import android.widget.TextView
import androidx.appcompat.app.ActionBar
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import androidx.core.app.NavUtils
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Lifecycle
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import java.util.*
class MainFragment : androidx.fragment.app.Fragment() {
private lateinit var mySpinnerItems: Array<String>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val v = inflater.inflate(R.layout.fragment_main, container, false)
super.onCreate(savedInstanceState)
return v
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
val mSpinner = requireView().findViewById<Spinner>(R.id.mSpinner)
val mViewPager2 = requireView().findViewById<ViewPager2>(R.id.mViewPager2)
// Spinner items array
mySpinnerItems = arrayOf(
"Item 1",
"Item 2",
"Item 3",
)
val arrayAdapter = ArrayAdapter(requireView().context, android.R.layout.simple_spinner_item, mySpinnerItems)
arrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_item)
mSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) {
when {
mySpinnerItems[position] == "Item 1" -> {
mViewPager2.setCurrentItem(0, false)
}
mySpinnerItems[position] == "Item 2" -> {
mViewPager2.setCurrentItem(1, false)
}
else -> {
mViewPager2.setCurrentItem(2, false)
}
}
}
override fun onNothingSelected(parent: AdapterView<*>) {
mViewPager2.setCurrentItem(0, false)
}
}
mSpinner.adapter = arrayAdapter
mViewPager2.adapter = MySpinnerFragmentAdapter(this)
mViewPager2.orientation = ViewPager2.ORIENTATION_HORIZONTAL
mViewPager2.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
when (position) {
0 -> {
mSpinner.setSelection(0)
}
1 -> {
mSpinner.setSelection(1)
}
else -> {
mSpinner.setSelection(2)
}
}
super.onPageSelected(position)
}
})
super.onActivityCreated(savedInstanceState)
}
private class MySpinnerFragmentAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {
private val intItems = 3
override fun createFragment(position: Int): Fragment {
return when (position) {
0 -> Fragment1()
1 -> Fragment2()
else -> Fragment3()
}
}
override fun getItemCount(): Int {
return intItems
}
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
menu.clear()
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return if (item.itemId == android.R.id.home) {
val intent = activity?.let { NavUtils.getParentActivityIntent(it) }
when {
intent != null -> {
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
NavUtils.navigateUpTo(requireActivity(), intent)
}
}
true
} else super.onOptionsItemSelected(item)
}
}
Основной макет активности
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@ id/detail_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
</LinearLayout>
Макет основного фрагмента
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@ id/detail_container">
<androidx.viewpager2.widget.ViewPager2
android:id="@ id/mViewPager2"
android:layout_height="0dp"
android:layout_width="match_parent"
android:layout_weight="1" />
<!-- divider (start)-->
<View
android:id="@ id/divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginBottom="5dp"
android:background="?android:attr/textColorSecondary" />
<!-- divider (end)-->
<Spinner
android:id="@ id/mSpinner"
style="@style/Widget.AppCompat.Spinner.Underlined"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:spinnerMode="dropdown"/>
</LinearLayout>
Обновить
предложение cactustictacs
mSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
when {
mySpinnerItems[position] == "Item 1" -> {
mViewPager2.setCurrentItem(0, false)
}
mySpinnerItems[position] == "Item 2" -> {
mViewPager2.setCurrentItem(1, false)
}
else -> {
mViewPager2.setCurrentItem(2, false)
}
}
}
override fun onNothingSelected(parent: AdapterView<*>) {
// Code to perform some action when nothing is selected
mViewPager2.setCurrentItem(0, false)
}
}
Ответ №1:
Трассировка стека (пожалуйста, в будущем публикуйте фактический текст, а не скриншот!) сообщает, что какой-то вызываемый параметр view
имеет значение null, если его тип указан как ненулевой ( View
вместо View?
). И это вызвано AdapterView.onItemSelected
запуском (так что это view
параметр в этом методе), который объявлен в onActivityCreated
По сути, этот onItemSelected
метод должен иметь типы с нулевым значением для первых двух параметров, AdapterView<*>?
и View?
.
Это то, что вы получаете, если позволяете IDE автоматически реализовывать методы (с помощью ctrl I) — в документах есть этот View!
тип, что означает, что, поскольку он исходит из Java, он может быть нулевым, может и нет, не знаю — так что безопасное значение по умолчанию View?
. Когда вы указываете ненулевой тип (например View
), Kotlin выполняет нулевую проверку, чтобы убедиться, что это именно тот Intrinsics.checkNotNullParameter
вызов. Вы получили значение null, поэтому оно выдало исключение!
Так что да, сделайте их обнуляемыми, затем проверьте их на нуль, прежде чем обращаться к ним. Также убедитесь appcompat
, что он обновлен (по крайней мере 1.2.0
), потому что у них была проблема, из-за которой Activity
они не воссоздавались при использовании setDefaultNightMode
Комментарии:
1. Для части проверки нуля этот код будет предшествовать
when
предложению? Должен ли я проверять, являются ли они нулевыми одновременно?2. Есть несколько способов проверить наличие нулей и безопасно обработать их ( kotlinlang.org/docs/reference/null-safety.html ) но в данном случае, поскольку это параметр, он является a
val
и не изменится — так что вы могли бы просто сделатьif (view == null) return
строку вверху. Поскольку компилятор знает, что вы продолжите работу с методом, только если значение не равно нулю, и значение не может измениться (и стать нулевым), он «умно преобразует» его в ненулевой тип для остальной части функции в любом случае. Но вы на самом деле не используетеview
его в своем коде, поэтому вам вообще не нужно его проверять! Просто сделайте тип nullable3. Проблема решена. Я не включил все это, основываясь на вашем предложении. Нет смысла иметь бесполезный код. Большое спасибо.