Search code examples
androidkotlinandroid-recyclerviewandroid-viewpagerfragment

Fragment is not attached to a context


I intend to show a list of farms (Fazendas) previously registered in the database (SQLite) in a Fragment called "FazendaListFragment" that is part of the FazendaActivity. Here I have two screens using ViewPage for screen change via sliding, but I have the problem reported in the Title of this questions. I don't know how I solve this problem with attach contexts. Bellow the code:

FazendaActivity

    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_fazenda)

    setupTabs()
}

private fun setupTabs(){
    val adapter = ViewPagerAdapter(supportFragmentManager)
    adapter.addFragment(FazendaListFragment(), "")
    adapter.addFragment(ListaPastoFragment(), "")

    view_pager_fazenda.adapter = adapter
    tabs.setupWithViewPager(view_pager_fazenda)

    tabs.getTabAt(0)!!.setIcon(R.drawable.ic_baseline_foundation_24)
    tabs.getTabAt(1)!!.setIcon(R.drawable.ic_outline_fence_24)
}

FazendaListFragment (UPDATED)

ar nameList = ArrayList<String>()

//Iniciando RecyclerView
var fazendaAdapter: FazendaListAdapter? = null
var linearLayoutManager: LinearLayoutManager? = null

//Iniciando o SQLite
var fazendaList = ArrayList<Fazenda>()
val fazendaDatabaseHandler by lazy { FazendaDatabaseHandler(requireContext()) }

private var cadastroFazenda: FloatingActionButton? = null

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    // Inflate the layout for this fragment
    return inflater.inflate(R.layout.fragment_lista_fazenda, container, false)
}

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

private fun deleteAdapter(position: Int){
    fazendaList.removeAt(position)
    fazendaAdapter!!.notifyItemRemoved(position)
}

private fun initView() {
    fazendaList = fazendaDatabaseHandler.fazendas()
    fazendaAdapter = FazendaListAdapter(fazendaList, requireContext(), this::deleteAdapter)
    linearLayoutManager = LinearLayoutManager(context)
    recyclerview.layoutManager = linearLayoutManager
    recyclerview.adapter = fazendaAdapter
}

FazendaListAdapter

internal var fazendaList: List<Fazenda> = ArrayList<Fazenda>()
init{
    this.fazendaList = fazendaList
}

//Aqui é onde o ViewHolder é criado a partir do layout
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
    val view = LayoutInflater.from(context).inflate(R.layout.content_fazenda_list, parent, true)
    return ViewHolder(view)
}

//Nessa parte é onde se modifica o item do ViewHolder
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    val fazenda = fazendaList[position]

    holder.name.text = fazenda.nome
    holder.cnpj.text = fazenda.cnpj
    holder.endereco.text = fazenda.endereco

    holder.editbtn.setOnClickListener{
        val intent = Intent(context, CadastroFazendaActivity::class.java)
        intent.putExtra("edit", true)
        intent.putExtra("position", fazenda.id)
        context.startActivity(intent)
    }
    holder.delbtn.setOnClickListener{
        val view = View.inflate(context, R.layout.dialog_confirm_delete_fazenda, null)

        val builder = AlertDialog.Builder(context)
        builder.setView(view)

        val dialog = builder.create()
        dialog.show()
        dialog.window?.setBackgroundDrawableResource(android.R.color.transparent)

        view.btn_delete.setOnClickListener {
            val fazendaDatabaseHandler = FazendaDatabaseHandler(context)
            fazendaDatabaseHandler.deleteFazenda(fazenda.id)
            callbacks(position)
            dialog.dismiss()
        }
    }
}

//Devolve a quantidade de itens do fazendaList
override fun getItemCount(): Int {
    return fazendaList.size
}

//Aqui é a criação dos itens do ViewHolder
inner class ViewHolder(view: View): RecyclerView.ViewHolder(view){
    var name: TextView = view.tvAdpNome
    var cnpj: TextView = view.tvAdpCNPJ
    var endereco: TextView = view.tvAdpEndereco
    var delbtn: Button = view.deleteButton
    var editbtn: Button = view.editButton
}

ViewPagerAdapter

private val mFragmentList = ArrayList<Fragment>()
private val mFragmentTitleList = ArrayList<String>()

override fun getItem(position: Int): Fragment {
    return mFragmentList[position]
}

override fun getCount(): Int {
    return mFragmentList.size
}

override fun getPageTitle(position: Int): CharSequence? {
    return mFragmentTitleList[position]
}

fun addFragment(fragment: Fragment, title: String){
    mFragmentList.add(fragment)
    mFragmentTitleList.add(title)
}

The Log of the error is here:

E/AndroidRuntime: FATAL EXCEPTION: main
Process: br.com.appintellipec, PID: 6685
java.lang.RuntimeException: Unable to start activity ComponentInfo{br.com.appintellipec/br.com.appintellipec.FazendaActivity}: java.lang.IllegalStateException: Fragment FazendaListFragment{524ae27} (f65a2c7c-2524-45f7-908a-12db24890acc) not attached to a context.
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3449)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
    at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
    at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
    at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loop(Looper.java:223)
    at android.app.ActivityThread.main(ActivityThread.java:7656)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
 Caused by: java.lang.IllegalStateException: Fragment FazendaListFragment{524ae27} (f65a2c7c-2524-45f7-908a-12db24890acc) not attached to a context.
    at androidx.fragment.app.Fragment.requireContext(Fragment.java:900)
    at br.com.appintellipec.fragments.FazendaListFragment.<init>(FazendaListFragment.kt:32)
    at br.com.appintellipec.FazendaActivity.setupTabs(FazendaActivity.kt:22)
    at br.com.appintellipec.FazendaActivity.onCreate(FazendaActivity.kt:17)
    at android.app.Activity.performCreate(Activity.java:8000)
    at android.app.Activity.performCreate(Activity.java:7984)
    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3422)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601) 
    at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85) 
    at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) 
    at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) 
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066) 
    at android.os.Handler.dispatchMessage(Handler.java:106) 
    at android.os.Looper.loop(Looper.java:223) 
    at android.app.ActivityThread.main(ActivityThread.java:7656) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947) 

Solution

  • You cannot call requireContext() at a property initialization site because properties are initialized while the Fragment instance is still being constructed, which is before it can possibly be attached to a context. This line has your error:

    var fazendaDatabaseHandler = FazendaDatabaseHandler(requireContext())
    

    You can either load it lazily, so it only will call requireContext() when you first use the property:

    val fazendaDatabaseHandler by lazy { FazendaDatabaseHandler(requireContext()) }
    

    Or you can make it lateinit var and initialize it in onViewCreated(), when it will be safe to call requireContext():

    lateinit var fazendaDatabaseHandler: FazendaDatabaseHandler
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        fazendaDatabaseHandler = FazendaDatabaseHandler(requireContext())
        //...
    }