RecyclerView must not be null - within fragment - kotlin

I'm trying to view a recyclerview within a fragment that immediately runs via MainActivity. I keep getting the 'itemList_hot must not be null' right on startup. I tried initializing it in the MainActivity but I must be doing something wrong. Very new to Android Studio but this is a roadblock I can't seem to figure out. Code below, thanks.
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val sectionsPagerAdapter = SectionsPagerAdapter(this, supportFragmentManager)
val viewPager: ViewPager = findViewById(R.id.view_pager)
viewPager.adapter = sectionsPagerAdapter
val tabs: TabLayout = findViewById(R.id.tabs)
tabs.setupWithViewPager(viewPager)
//error is here
val recyclerView : RecyclerView = findViewById(R.id.listItems_hot)
recyclerView.layoutManager = LinearLayoutManager(this)
val dataManager = DataManager()
recyclerView.adapter = HotRecyclerAdapter(this, dataManager.getData())
}
override fun onResume() {
super.onResume()
}
}
Fragment XML
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/listItems_hot"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:listitem="#layout/item_list_hot">
</androidx.recyclerview.widget.RecyclerView>
Fragment code
class Tab1_Fragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_tab1, container, false)
}
}
Error code
findViewById(R.id.listItems_hot) must not be null

Related

Artefacts when implementing expandable items in RecyclerView

I'm trying to make a simple RecyclerView with expandable items using Kotlin. Basically a list of bins which expand out to show descriptions of what should go in them. I tried to follow the Android Studio Recycler View example as closely as possible. The problem is when I expand each item, some artefacts would show.
Unexpanded view
Expanded view
Note: The expanded view is the result of pressing on "Recycling Bin", the artefact is made up of the bin's description text and also "Garden Waste Bin" somehow being duplicated.
Below is my implementation
Adapter class
class BinAdapter(private val dataSet: List<Bin>) : RecyclerView.Adapter<BinViewHolder>() {
var expandedPosition = -1
var previousExpandedPosition = -1
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BinViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.bin_row_item, parent, false)
return BinViewHolder(view)
}
override fun onBindViewHolder(viewHolder: BinViewHolder, position: Int) {
viewHolder.itemView.isActivated
viewHolder.titleView.text = dataSet[position].name
viewHolder.descriptionView.text = dataSet[position].description
val isExpanded = position == expandedPosition
viewHolder.descriptionView.visibility = if (isExpanded) View.VISIBLE else View.GONE
viewHolder.itemView.isActivated = isExpanded
if (isExpanded) {
previousExpandedPosition = position
}
viewHolder.itemView.setOnClickListener {
expandedPosition = if (isExpanded) -1 else position
notifyItemChanged(previousExpandedPosition)
notifyItemChanged(position)
}
}
override fun getItemCount() = dataSet.size
Fragment class
class BinLookupFragment : Fragment() {
private var _binding: FragmentBinLookupBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentBinLookupBinding.inflate(inflater, container, false)
binding.binRecyclerView.adapter = BinAdapter(BinList(resources))
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
ViewHolder class
class BinViewHolder(itemView: View) : ViewHolder(itemView) {
val titleView: TextView
val descriptionView: TextView
init {
titleView = itemView.findViewById(R.id.titleView)
descriptionView = itemView.findViewById(R.id.descriptionView)
}
}
Row item layout
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/titleView"
android:layout_width="#dimen/bin_list_width"
android:layout_height="#dimen/bin_list_item_title_height"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
<TextView
android:id="#+id/descriptionView"
android:layout_width="#dimen/bin_list_width"
android:layout_height="#dimen/bin_list_item_description_height"
app:layout_constraintTop_toBottomOf="#id/titleView"
app:layout_constraintStart_toStartOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
fragment layout
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/bin_recycler_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="200dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Has anyone come across this before or have some debugging tips?

No Adapter Attached RecyclerView

I want to implement my app. My app went smoothly, but I also got an error with 2022-05-31 14:14:32.663 8626-8626/id.kotlin.belajar E/RecyclerView: No adapter attached; skipping layout; skipping layout.
E/RecyclerView: No adapter attached; skipping layout; skipping layout
Home Activity:
class HomeActivity : DaggerAppCompatActivity(), HomeView {
#Inject
lateinit var presenter: HomePresenter
private lateinit var progressBar: ProgressBar
private lateinit var recyclerView: RecyclerView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_home)
progressBar = findViewById(R.id.pb_home)
recyclerView = findViewById(R.id.rv_home)
presenter.discoverMovie()
}
override fun onShowLoading() {
progressBar.visibility = View.VISIBLE
}
override fun onHideLoading() {
progressBar.visibility = View.GONE
recyclerView.visibility = View.VISIBLE
}
override fun onResponse(results: List<Result<String>>) {
recyclerView.addItemDecoration(DividerItemDecoration(this#HomeActivity,
DividerItemDecoration.VERTICAL))
recyclerView.adapter = HomeAdapter(results)
}
override fun onFailure(t:Throwable) {
Log.e(HomeActivity::class.java.simpleName, "${t.printStackTrace()}")
}
}
Home Adapter:
class HomeAdapter (private val results: List<Result>):
RecyclerView.Adapter<HomeAdapter.HomeViewHolder>(){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeViewHolder {
return HomeViewHolder(
LayoutInflater
.from(parent.context).inflate(
R.layout.item_home,
parent,
false
)
)
}
override fun onBindViewHolder(holder: HomeViewHolder, position: Int){
holder.bind()
}
override fun getItemCount(): Int{
return results.count()
}
inner class HomeViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
fun bind() {
with(itemView) {
val title = findViewById<TextView>(R.id.original_title)
title.text
val overview = findViewById<TextView>(R.id.overview)
overview.text
}
}
}
}
Home Presenter:
class HomePresenter(private val view: HomeView, datasource: HomeDatasource) {
fun discoverMovie(){
view.onShowLoading()
val dataSource = Networkmodule.providesHttpAdapter(client =
OkHttpClient()).create(HomeDatasource::class.java)
dataSource.discoverMovie().enqueue(object : Callback<HomeResponse> {
override fun onResponse(call: Call<HomeResponse>, response: Response<HomeResponse>){
view.onHideLoading()
view.onResponse(((response.body()?.results ?: emptyList()) as List<Result<String>>))
}
override fun onFailure(call: Call<HomeResponse>, t:Throwable){
view.onHideLoading()
view.onFailure(t)
}
})
}
}
I need your help.
This is my .xml files:
activity_home:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".presentation.HomeActivity">
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/white"
android:elevation="4dp"
app:layout_constraintTop_toTopOf="parent"
app:title="Belajar"
app:titleTextColor="#color/black"/>
<ProgressBar
android:id="#+id/pb_home"
style="#style/Widget.AppCompat.ProgressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminate="true"
android:indeterminateTint="#color/black"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="gone" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rv_home"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="?android:attr/actionBarSize"
android:scrollbars="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:listitem="#layout/item_home" />
</androidx.constraintlayout.widget.ConstraintLayout>
I can see two major problems here :
Adapter instantiation
You do not need to re-instantiate the adapter each time you receive data.
Just instanciate it in the onCreate method with an empty list and update the list each time you have new data (don't forget to call notify method).
And attach the adapter to the recyclerView in the onCreate method as well
Exemple
In your activity you need to :
Declare and instantiate your Adapter as a class attribute.
In the onCreate method attach it to the recyclerView
On the onResponse method call a new method of your adapter called " update "
class HomeActivity : DaggerAppCompatActivity(), HomeView {
#Inject
lateinit var presenter: HomePresenter
private lateinit var progressBar: ProgressBar
private lateinit var recyclerView: RecyclerView
private val homeAdapter = HomeAdapter(emptyList())
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_home)
progressBar = findViewById(R.id.pb_home)
recyclerView = findViewById(R.id.rv_home)
recyclerView.adapter = homeAdapter
presenter.discoverMovie()
}
override fun onShowLoading() {
progressBar.visibility = View.VISIBLE
}
override fun onHideLoading() {
progressBar.visibility = View.GONE
recyclerView.visibility = View.VISIBLE
}
override fun onResponse(results: List<Result<String>>) {
recyclerView.addItemDecoration(DividerItemDecoration(this#HomeActivity,
DividerItemDecoration.VERTICAL))
homeAdapter.update(results)
}
override fun onFailure(t:Throwable) {
Log.e(HomeActivity::class.java.simpleName, "${t.printStackTrace()}")
}
}
In your adapter :
Make results a mutableList
Create the update method
Clear the results list
Add your newResults list to your list
notifyDataSetChanged to redraw the list with new values
class HomeAdapter (private val results: MutableList<Result>):
RecyclerView.Adapter<HomeAdapter.HomeViewHolder>(){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeViewHolder {
return HomeViewHolder(
LayoutInflater
.from(parent.context).inflate(
R.layout.item_home,
parent,
false
)
)
}
override fun onBindViewHolder(holder: HomeViewHolder, position: Int){
holder.bind()
}
override fun getItemCount(): Int{
return results.count()
}
fun update(newResults: List<Result>) {
results.apply {
clear()
addAll(newResults)
}
notifyDataSetChanged()
}
inner class HomeViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
fun bind() {
with(itemView) {
val title = findViewById<TextView>(R.id.original_title)
title.text
val overview = findViewById<TextView>(R.id.overview)
overview.text
}
}
}
}
Layout manager missing ?
We don't have your xml code so I don't know if you added the layoutManager on the xml like this (assuming you want a linearManager) :
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
You can do it programaticaly using the layoutManager attribute on the recyclerView :
recyclerView.layoutManager = LinearLayoutManager()
You can set it in the onCreate method (if it is an activity) or onViewCreated (if it's a fragment) before attaching your adapter to the recyclerView
To avoid errors like that, the best way is to setup your recyclerView as soon as possible in the activity (onCreate) or fragment (onViewCreated) lifecycle.

Recyclerview Binding in Another Fragment

With the help of room, i am testing a simple app to save words. I have 2 layout fragment- home(main) and myword(second). I am trying to put the following code in myword fragment to initialize recyclerview, i am getting unresolve error. What am i putting wrong?
frag_mywords.xml
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/words_dail"
android:layout_width="0dp"
android:layout_height="0dp"
tools:listitem="#layout/recykel_list"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
TakerFragment.kt
class TakerFragment : Fragment() {
private lateinit var takerViewModel: TakerViewModel
private var _binding: FragMywordsBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
takerViewModel =
ViewModelProvider(this).get(TakerViewModel::class.java)
_binding = FragMywordsBinding.inflate(inflater, container, false)
val root: View = binding.root
val recyclerView = findViewById<RecyclerView>(R.id.words_dail)
val adapter = WordListAdapter(this)
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(this)
return root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
How do i correctly solved this line?
val recyclerView = findViewById<RecyclerView>(R.id.words_dail)
val adapter = WordListAdapter(this)
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(this)
There are multiple issues here:
First change val recyclerView = findViewById<RecyclerView>(R.id.words_dail) with val recyclerView = words_dail.
Then, the context of your WorldListAdapter is wrong, it should be val adapter = WordListAdapter(requireContext()) and not this.
Third, you can set the layoutManager in the xml with this:
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/words_dail"
android:layout_width="0dp"
android:layout_height="0dp"
tools:listitem="#layout/recykel_list"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" <--
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
With Databinding you could also set the adapter in the xml but I don't know if you use databinding in your adapter.

ViewPager2 must not be null in NavigationDrawer and SwipeViewTabs

I am trying to link my Navigation Drawer with SwipeView Tabs, the problem is that the logcat tells me that my Viewpager must not be null, I have tried to solve this problem in many ways but could not.
PageAdaper.kt
class ViewPagerAdapter(fragmanetActivity: TabFragment): FragmentStateAdapter(fragmanetActivity) {
override fun getItemCount(): Int = 3
override fun createFragment(position: Int): Fragment {
when (position) {
0 -> return FirstFragment()
1 -> return SecondFragment()
2 -> return ThirdFragment()
}
return Fragment()
}
}
Fragment
class TabFragment : Fragment() {
private val adapter by lazy { ViewPagerAdapter(this) }
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val x = inflater.inflate(R.layout.contain_main, container, false)
pager.adapter = adapter // This is the error
TabLayoutMediator(tab_layout, pager) { tab, position ->
when (position) {
0 -> tab.text = "option1"
1 -> tab.text = "option2"
2 -> tab.text = "option3"
}
}.attach()
return x
}
}
contain_main.xml
I linked this file with ( class TabFragment : Fragment() )
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.tabs.TabLayout
android:id="#+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabSelectedTextColor="#E91E63" />
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
In your class TabFragment you are trying to access pager view. Since you are in the onCreateView method, synthetic doesn’t know how to access view and give you reference on pager.
You can do that in onViewCreated and later callbacks, or you can use val x to access pager.MikibeMiki
code:
class TabFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.contain_main, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val adapter by lazy { ViewPagerAdapter(this) }
pager.adapter = adapter
TabLayoutMediator(tab_layout, pager) { tab, position ->
when (position) {
0 -> tab.text = "option1"
1 -> tab.text = "option2"
2 -> tab.text = "option3"
}
}.attach()
}
}

Using recycleview in android

I read some document about using recyclingview for activity. Now i try to use recycleview to my fragment. the problem is my fragment look empty when i execute.
fragment:
class KategoriFragment : Fragment() {
var araclarKategori = ArrayList<AracMarka>()
private lateinit var galleryViewModel: GalleryViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
veriKaynaginiDoldur()
galleryViewModel =
ViewModelProviders.of(this).get(GalleryViewModel::class.java)
var root = inflater.inflate(R.layout.fragment_category, container, false)
veriKaynaginiDoldur()
var rvarackategori = root.findViewById(R.id.rvarackategori) as RecyclerView
var MyAdapter = AracMarkaAdapter(araclarKategori)
rvarackategori.adapter = MyAdapter
/
return root
}
fun veriKaynaginiDoldur(): ArrayList<AracMarka> {
var aracLogo = arrayOf(R.drawable.opellogo, R.drawable.chevroletlogo)
var aracismi = resources.getStringArray(R.array.arabaisim)
for (i in 0 until min(aracismi.size, aracLogo.size)) {
var eklenecekaracKategori = AracMarka(aracismi[i], aracLogo[i])
araclarKategori.add(eklenecekaracKategori)
}
return araclarKategori
}
}
I create an adapter. I think there is no problem on it.
adapter:
class AracMarkaAdapter(tumKategori: ArrayList<AracMarka>) :
RecyclerView.Adapter<AracMarkaAdapter.AracMarkaViewHolder>() {
var araclar = tumKategori
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AracMarkaViewHolder {
var inflater = LayoutInflater.from(parent.context)
var arackategori = inflater.inflate(R.layout.arac_kategori, parent, false)
return AracMarkaViewHolder(arackategori)
}
override fun getItemCount(): Int {
return araclar.size
}
override fun onBindViewHolder(holder: AracMarkaViewHolder, position: Int) {
holder.aracismi.text=araclar.get(position).aracAdi
holder.aracLogo.setImageResource(araclar.get(position).aracLogo)
}
class AracMarkaViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var teksatirKategori= itemView
var aracismi= teksatirKategori.tvaracAdi
var aracLogo=teksatirKategori.img_arac_sembol
}
}
fragment xml:
<?xml version="1.0" encoding="utf-8"?>
<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="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rvarackategori"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
So, when i click button, fragment opens but it is empty. Do you have any idea about it?
After modifying the data in your list in veriKaynaginiDoldur(), you need to call myAdapter.notifyDataSetChanged() so it knows to rebind the data. Or you could call veriKaynaginiDoldur() before you instantiate your adapter.
Edit: Your other error is in your for loop within veriKaynaginiDoldur(). You are making a range using the size of the araclarKategori list when it is still zero.
Instead of
for (i in 0..araclarKategori.size - 1)
use
for (i in 0 until min(aracLogo.size, aracismi.size))
you have to call this veriKaynaginiDoldur() function after the below
lines of code below I have mentioned please check
var rvarackategori = root.findViewById(R.id.rvarackategori) as RecyclerView
var MyAdapter = AracMarkaAdapter(araclarKategori)
rvarackategori.adapter = MyAdapter
veriKaynaginiDoldur()