我有时使用合成在片段内的视图上获取空指针(有时)。
我不知道出什么问题了,因为我正在声明XML中的片段,并且我认为当我从Activity调用populate
方法时,已经创建了根视图
无论如何,我不认为每次致电居民时都检查正确吗...
我写了一个示例代码,说明发生了什么(空指针将位于tv
上):
片段:
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import kotlinx.android.synthetic.main.fragment_foo.*
class FooFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_foo, container, false)
}
fun populate(fooName: String) {
tv.text = fooName
}
}
与Activity相关的XML中的XML相关
<fragment
android:id="@+id/fFoo"
android:name="com.foo.FooFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:layout="@layout/fragment_foo"/>
这与活动有关:
class FooActivity : AppCompatActivity() {
private lateinit var fooFragment: FooFragment
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_foo)
fooFragment = fFoo as FooFragment
}
private fun loadDataProductionInfo(productionId: Long) {
FooAPIParser().getFoo {
initFoo(it.fooName)
}
}
private fun initFoo(fooName: String) {
fooFragment.populate(fooName)
}
}
最后是特定片段的XML:
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tvFoo"
style="@style/Tv"/>
</androidx.constraintlayout.widget.ConstraintLayout>
答案 0 :(得分:1)
科特林的合成特性不是魔术,而是以非常简单的方式起作用的。当您访问btn_K时,它会调用getView().findViewById(R.id.tv)
问题是您访问它太早了,getView()
在null
中返回了onCreateView
。尝试使用onViewCreated
方法进行操作:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
//Here
}
}
在onViewCreated之外使用视图。
tv?.text = "kotlin safe call"
如果要使用Fragments
,则需要扩展FragmentActivity
答案 1 :(得分:0)
考虑到您的答案,我已实施了一个补丁。我不太喜欢,但是我认为它应该比现在更好。你觉得呢?
我将从类中扩展要检查的每个片段:
fun populate(fooName: String) {
checkViewCreated {
tv.text = fooName
}
}
“片段”中的调用或多或少会很干净:
{{1}}
答案 2 :(得分:0)
最后,我得到了帮助以找到更好的答案。
open class BaseFooFragment : Fragment() {
private var listener: VoidListener? = null
private var viewCreated = false
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewCreated = true
listener?.let {
it.invoke()
listener = null
}
}
override fun onDestroyView() {
viewCreated = false
super.onDestroyView()
}
fun doWhenViewCreated(success: VoidListener) {
if (viewCreated) {
success()
} else {
listener = success
}
}
}
VoidListener
就是这样:
typealias VoidListener = () -> Unit
一种实现此功能的方法更通用(例如,当您想使用多个监听器时)可能是这样的:
open class BaseFooFragment : Fragment() {
private val listeners: MutableList<VoidListener> = mutableListOf()
private var viewCreated = false
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewCreated = true
listeners.forEach { it() }
listeners.clear()
}
override fun onDestroyView() {
viewCreated = false
super.onDestroyView()
}
fun doWhenViewCreated(success: VoidListener) {
if (viewCreated) {
success()
} else {
listeners.add(success)
}
}
}