21

There is a fairly simple scenario that is giving me quite a bit of trouble. I'm making a very simple Activity with an embedded fragment. This fragment is simply a Gridview that displays some images. The issue comes when referring to the Gridview using Kotlin extensions to refer directly to an XML id. What is the issue here? Does kotlinx not work on static fragments?

Error:

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.android.android_me/com.example.android.android_me.ui.MainActivity}: java.lang.IllegalStateException: gridview_all_parts must not be null
 Caused by: java.lang.IllegalStateException: gridview_all_parts must not be null                                                                                  at com.example.android.android_me.ui.MasterListFragment.onActivityCreated(MasterListFragment.kt:22)

Fragment with offensive line of code

import kotlinx.android.synthetic.main.fragment_master_list.*

    class MasterListFragment: Fragment() {

        override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
            val layoutView = inflater?.inflate(R.layout.fragment_master_list, container, false)
            return layoutView
        }

        override fun onActivityCreated(savedInstanceState: Bundle?) {
            //If this is removed, code runs
            gridview_all_parts.adapter = MasterListAdapter(activity, AndroidImageAssets.getAll())
            super.onActivityCreated(savedInstanceState)
        }
    }

Fragment Layout:

<?xml version="1.0" encoding="utf-8"?>
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/gridview_all_parts"
    android:layout_width="match_parent" android:layout_height="match_parent"/>

Parent Activity Layout

<?xml version="1.0" encoding="utf-8"?>
<!--have tried both class:= and android:name:=-->
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
    class="com.example.android.android_me.ui.MasterListFragment"
    android:id="@+id/fragment_masterlist"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    />

Parent Activity

class MainActivity: AppCompatActivity(){

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}
2
  • Can you show us more of the Fragment class, not just the "offensive code"? Commented Feb 16, 2018 at 15:57
  • Hey Tom, that is the entire code. This is part of a simple tutorial I am trying to "Kotlinize", hence the rudimentary structure Commented Feb 16, 2018 at 17:41

2 Answers 2

29

To use the extensions in Fragment, you need to use with your layoutView. This should work: layoutView.gridview_all_parts.adapter = MasterListAdapter(activity, AndroidImageAssets.getAll())

You can make your layoutView global in this case.

UPDATED EXPLANATION Its something to do with view inflating. Like in butterknife, we need to bind the inflated view in case of fragment/recyclerView, similarly in case of kotlin, we need that inflate view for accessing the views in the xml.

Quoting from official documentation,

Importing synthetic properties It is convenient to import all widget properties for a specific layout in one go:

import kotlinx.android.synthetic.main.<layout>.*

Thus if the layout filename is activity_main.xml, we'd import

kotlinx.android.synthetic.main.activity_main.*.

If we want to call the synthetic properties on View, we should also import

kotlinx.android.synthetic.main.activity_main.view.*.

Once we do that, we can then invoke the corresponding extensions, which are properties named after the views in the XML file.

Sign up to request clarification or add additional context in comments.

2 Comments

That worked! Would you mind explaining why in this example layoutInflater is required to refer to the View object as opposed to other scenarios? Once you do, I'll mark this as the answer. Thanks again!
@JoshRibeiro this problems also occurs for RecyclerView, when we write the adapter. Everyone who found this problem, might want to read this about LayoutContainer
0

For everyone who found this problem in the other case.

The NPE from Android Extensions also occurs when we write the adapter for RecyclerView (especially: writing CustomViewHolder).

See LayoutContainer for more info and how to fix this issue.

  1. Add this into your module level gradle
apply plugin: 'kotlin-android-extensions'

android {
    androidExtensions {
        experimental = true
    }
    // your config
    defaultConfig {}
}
  1. Your adapter
class MainViewHolder(override val containerView: View) :
    RecyclerView.ViewHolder(containerView), 
    LayoutContainer { // Extends this
        fun bind(item: News) = containerView.apply {
            tv_item_title.text = item.title
        }
}

2 Comments

This is an incorrect usage of LayoutContainer. If you are using it, you shouldn't use a View reference to get the child View (in this case containerView.apply).
I see, thanks for the input. Would you mind giving me a hint to edit the answer? or you might want to edit my answer directly @Eliezer If I recall correctly. Without containerView.* access, KTX will automatically import the synthetic properties of my_layout.xml and thus giving me the NPE. And, I try to use LayoutContainer & using containerView to access the child view. This is why I prefer to use Data Binding from google: youtu.be/Qxj2eBmXLHg?t=505

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.