【问题标题】:Android - View instance gets null on screen rotationAndroid - 视图实例在屏幕旋转时为空
【发布时间】:2018-03-01 13:40:19
【问题描述】:

我正在使用 Kotlin Android Extension 通过他们的 ID 直接访问视图。 我有一个进度条,我使用 id 直接在片段中访问它,即 progress_bar

<ProgressBar
    android:id="@+id/progress_bar"
    style="@style/Widget.AppCompat.ProgressBar.Horizontal"
    android:layout_width="match_parent"
    android:layout_height="15dp"
    android:indeterminate="true"/>

在片段中,我用这段代码显示和隐藏它

progress_bar.visibility = if (visible) View.VISIBLE else View.GONE

在我旋转屏幕之前它运行良好。之后,它会抛出异常

java.lang.IllegalStateException:progress_bar 不能为空。

该变量在屏幕旋转时为空。如何解决这个问题?

片段代码

class SingleAppFragment : Fragment() {

private lateinit var appName: String

companion object {
    fun newInstance(appName: String = ""): SingleAppFragment {
        val fragment = SingleAppFragment()
        val args = Bundle()
        args.putString(Constants.EXTRA_APP_NAME, appName)
        fragment.arguments = args
        return fragment
    }
}

private var mListener: OnFragmentInteractionListener? = null

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    appName = if (arguments != null && !arguments.getString(Constants.EXTRA_APP_NAME).isEmpty()) {
        arguments.getString(Constants.EXTRA_APP_NAME)
    } else {
        Constants.APP_NAME_FACEBOOK
    }
}

override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
                          savedInstanceState: Bundle?): View? {
    return inflater!!.inflate(R.layout.fragment_single_app, container, false)
}

override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    initView()
    setEventListeners()
}

private fun initView() {
    var canShowSnackBar = true

    web_single_app.webViewClient = object : WebViewClient() {

        override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
            super.onPageStarted(view, url, favicon)
            showHideProgressBar(true)
            canShowSnackBar = true
        }

        override fun onPageFinished(view: WebView?, url: String?) {
            super.onPageFinished(view, url)
            showHideProgressBar(false)
        }

        override fun onReceivedError(view: WebView?, request: WebResourceRequest?, error: WebResourceError?) {
            web_single_app.stopLoading()
            if (canShowSnackBar) {
                mListener?.onErrorWebView()
                canShowSnackBar = false
            }
        }
    }
    web_single_app.settings.javaScriptEnabled = true
    web_single_app.loadUrl(Constants.APP_NAME_URL_MAP[appName])
}

private fun setEventListeners() {
    back_web_control.setOnClickListener({
        web_single_app.goBack()
    })
}

fun showHideProgressBar(visible: Boolean) {
    progress_bar_web_control.visibility = if (visible) View.VISIBLE else View.GONE
}

fun loadUrl(appName: String) {
    web_single_app.loadUrl(Constants.APP_NAME_URL_MAP[appName])
}

override fun onAttach(context: Context?) {
    super.onAttach(context)
    if (context is OnFragmentInteractionListener) {
        mListener = context
    }
}

override fun onDetach() {
    super.onDetach()
    mListener = null
}

interface OnFragmentInteractionListener {
    fun onErrorWebView()
}
}

重现步骤:

  1. 开始活动
  2. 片段被加载
  3. 在片段加载时,我加载一个 URL 并显示一个进度条
  4. 在加载 URL 时,我旋转手机,进度条变量变为空

【问题讨论】:

  • 通过Id获取progress_bar的方法是什么?请考虑片段状态生命周期。也许您在视图尚未准备好时尝试加载它。参考这里developer.android.com/guide/components/fragments.html
  • @BrunoBieri 我在 onCreateView() 但在“返回视图”语句之前调用。因此,变量没有被分配。现在,我正在访问“view.progress_bar”之类的“progress_bar”,问题就解决了。谢谢。
  • 你应该在onViewCreated这样做
  • 谢谢@MichałBaran,我会关注的
  • @BrunoBieri 该变量是由 kotlin android 扩展分配的,我们不需要在其中初始化视图变量。我的问题由 setRetainInstance(true) 解决

标签: android kotlin kotlin-android-extensions


【解决方案1】:

在我的情况下,这个错误不时发生。当然,onViewCreated() 是放置代码的好方法。但有时奇怪的是还不够。 setRetainInstance(true) 可能有帮助,也可能没有。所以有时这会有所帮助:使用view 变量访问您的Views。您甚至可以在onCreateView() 中访问它们。您可以使用?. 来保证应用程序不会崩溃(当然,在这种情况下某些视图不会更新)。如果您想获得context,请使用view.context

在我的例子中,这个错误只在 Kotlin 协程中重现。

private fun showProgress(view: View) {
    view.progressBar?.visibility = View.VISIBLE
}

private fun hideProgress(view: View) {
    view.progressBar?.visibility = View.GONE
}

然后在代码中:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    showData(view)
}

private fun showData(view: View) {
    showProgress(view)
    adapter = SomeAdapter()
    adapter.setItems(items)
    val divider = SomeItemDecoration(view.context)
    view.recycler_view?.run {
        addItemDecoration(divider)
        adapter = this@SomeFragment.adapter
        layoutManager = LinearLayoutManager(view.context)
        setHasFixedSize(true)
    }
    hideProgress(view)
}

【讨论】:

    【解决方案2】:

    你是通过哪种方法得到progress_bar by Id?

    请考虑片段状态lifecycle。也许您在视图尚未准备好时尝试加载它。

    确保您的progress_bar 变量仅在视图准备好后才被分配。例如在 onViewCreated 方法。

    请看这里official Android lifecycle

    更新

    正如@CoolMind 指出的那样,该图没有显示方法onViewCreated

    完整的Android Activity/Fragment生命周期可以在here找到:

    【讨论】:

    【解决方案3】:

    给片段添加retain intance true,这样当方向发生变化时它不会被破坏

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        retainInstance=true
    }
    

    在访问视图之前,还要使用安全调用运算符进行空值检查

    fun showHideProgressBar(visible: Boolean) {
        progress_bar_web_control?.visibility = if (visible) View.VISIBLE else View.GONE
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-07-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多