使用合成

时间:2018-12-24 15:58:58

标签: android kotlin-android-extensions

我有时使用合成在片段内的视图上获取空指针(有时)。 我不知道出什么问题了,因为我正在声明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>

3 个答案:

答案 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)
        }
    }
}