androidandroid-recyclerviewkotlinandroid-framelayout

is it possible to get `RecyclerView` from Fragment?


trying to get RecyclerView from this layout :

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".listFragment">

<android.support.v7.widget.RecyclerView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/recyclee">
</android.support.v7.widget.RecyclerView>

into main activity class :

    private var mBlogList = findViewById<RecyclerView>(R.id.recyclee)

getting error : java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.Window$Callback android.view.Window.getCallback()' on a null object reference any help please :)

edit 1

i use kotlin extension now

import kotlinx.android.synthetic.main.fragment_list.*
class MainActivity : AppCompatActivity() {
private lateinit var mBlogList : RecyclerView

in onCreate method :

        mBlogList = recyclee

but the same error still exist

edit 2 listFragment code :

class listFragment : Fragment() {

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

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    recyclee != null
}
companion object {
    fun newInstance(): listFragment = listFragment()
}
}

edit 3 whole MainActivity code:

//this app supposed to read from FirebaseDatabase
//into Recycler view
//the RecyclerView is into Fragment layout
//i use Fragments into FrameLayout in the activity_main.xml
// the RecyclerView should be shown when navigatoinBar is clicked
//or on start of MainActivity
class MainActivity : AppCompatActivity() {
    private var mDatabase:DatabaseReference? = null
    private lateinit var mBlogList : RecyclerView
    private var query:Query?=null
    private var options:FirebaseRecyclerOptions<Blog>?=null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        //start listFragment , RecyclerView is there
        val mFragment = listFragment.newInstance()
        //openFragment method is below
        openFragment(mFragment)
        //navigation bottom onclicklistener


navBar.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)
        //get data from database
        mDatabase=FirebaseDatabase.getInstance().getReference().child("mall")
        mDatabase?.keepSynced(true)
        //here i should have recyclee but it is null i don't know why
        mBlogList = recyclee
        mBlogList.setHasFixedSize(true)
        mBlogList.layoutManager = LinearLayoutManager(this)
        //query of database
        query = mDatabase?.orderByKey()

    }
private val mOnNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item ->
   //there are 4 icons in the navigation_bottom_bar 
    //now we are talking about listNav icon only because it is realted 
    // with listFragment
    when (item.itemId) {
        R.id.listNav -> {
            val mFragment = listFragment.newInstance()
            openFragment(mFragment)
            return@OnNavigationItemSelectedListener true
        }
        R.id.cartNav -> {
            val mFragment = cartFragment.newInstance()
            openFragment(mFragment)
            return@OnNavigationItemSelectedListener true
        }
        R.id.supportNav -> {
            val mFragment = supportFragment.newInstance()
            openFragment(mFragment)
            return@OnNavigationItemSelectedListener true
        }
        R.id.accountNav -> {
            val mFragment = accountFragment.newInstance()
            openFragment(mFragment)
            return@OnNavigationItemSelectedListener true
        }
    }
    false
}
private fun openFragment(fragment: Fragment) {
    //open Fragment into FrameLayout in the main_activity.xml
    val transaction = supportFragmentManager.beginTransaction()
    transaction.replace(R.id.mainFrame, fragment)
    transaction.addToBackStack(null)
    transaction.commit()
}

override fun onStart() {
    super.onStart()
    //set options for FirebaseRecyclerAdapter
    options = FirebaseRecyclerOptions.Builder<Blog>()
            .setQuery(query!!, Blog::class.java)
            .build()
    //set custom adapter
    val mAdapter = object : FirebaseRecyclerAdapter<Blog, BlogViewHolder>(
            options!!) {
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BlogViewHolder {
            val view = LayoutInflater.from(parent.context)
                    .inflate(R.layout.cardview, parent, false)
            return BlogViewHolder(view)}

        override fun onBindViewHolder(holder: BlogViewHolder, position: Int, model: Blog) {
            holder.setTitle(model.title)
            holder.setDes(model.des)
            holder.setImage(applicationContext, model.image)
        }
    }
    mBlogList.adapter = mAdapter
}
inner class BlogViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    var mView:View= itemView
    //set title, des amd image with data we got from database
    fun setTitle(title:String){
        var postTitle = mView.findViewById<TextView>(R.id.post_title)
        postTitle?.text = title
    }
    fun setDes(des:String){
        var postDes = mView.findViewById<TextView>(R.id.post_des)
        postDes?.text = des
    }
    fun setImage(image:String){
        var postImage = mView.findViewById<ImageView>(R.id.post_title)
        Picasso.get().load(image).into(postImage)
    }
}

}


Solution

  • If you apply kotlin-android-extensions plugin then you don't need to use findViewById anymore because you can access views in the layout as if they're properties (by name). So, your onCreate could look as follows:

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    
        assert recyclee != null
    }
    

    Edit

    Since you're trying to do this in Fragment and your layout file is called fragment_List.xml then in your Fragment you must inflate the layout first and then you can access your RecyclerView as in the example above for Activity:

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_List, container, false)
    } 
    
    override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
    
        assert recyclee != null
    }
    

    If you try to access your RecyclerView in Activity before fragment_List is inflated in the Fragment then it will obviously be null (it hasn't been created yet). That's actually what you're trying to do in onCreate - it's too early as the Fragment's layout hasn't been built yet.

    Edit 2

    As it's seen from your code, mBlogList = recyclee is set in onCreate. Even though it's done after the ListFragment is created, it's still too early as its onCreateView hasn't been called yet and so no layout is in place.

    A quick fix would be to do it in onStart as at that point ListFragment is definitely there. However, a better approach is to do the logic inside ListFragment itself and communicate with MainActivity via callbacks as described here