I'm having troubles trying to display a recycler which contains more than 1 ViewHolder, i was testing and sending a custom List which contains only data to display a list of the 1st ViewType.
payments.add(Purchase(0,R.drawable.ic_cart,"",""))
payments.add(Purchase(0,R.drawable.ic_favorite,"",""))
payments.add(Purchase(0,R.drawable.ic_arrow_back,"",""))
This is the BottomSheetClass
class PaymentStepsFragment : SuperBottomSheetFragment() {
val payments = ArrayList<Any>()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState)
return inflater.inflate(R.layout.fragment_steps_payment, container, false)
}
private fun RecyclerAnimator(recyclerView: RecyclerView, adapter: PurchaseViewAdapter) {
val itemAnimator = DefaultItemAnimator()
itemAnimator.addDuration = 1000
itemAnimator.removeDuration = 1000
recyclerView.itemAnimator = itemAnimator
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(context)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
payments.add(Purchase(0,R.drawable.ic_cart,"",""))
payments.add(Purchase(0,R.drawable.ic_favorite,"",""))
payments.add(Purchase(0,R.drawable.ic_arrow_back,"",""))
val recycler = view.findViewById<RecyclerView>(R.id.payment_rv)
val adapter = PurchaseViewAdapter(payments, context!!)
RecyclerAnimator(recycler, adapter)
}
override fun getCornerRadius() = context!!.resources.getDimension(R.dimen.sheet_rounded_corner)
override fun getStatusBarColor() = Color.RED
}
and my RecyclerViewAdapter.
constructor(list: List<Any>, context: Context) : this() {
this.list = list
this.context = context
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when(viewType){
0 -> {
context = parent.context
val menuItemLayoutView = LayoutInflater.from(parent.context).inflate(R.layout.fragment_card_payment, parent, false)
PaymentViewHolder(menuItemLayoutView)
}
else -> {
context = parent.context
val menuItemLayoutView = LayoutInflater.from(parent.context).inflate(R.layout.fragment_card_payment, parent, false)
PaymentViewHolder(menuItemLayoutView)
}
}
}
override fun getItemViewType(position: Int): Int {
val data = list?.get(position) as Purchase
return data.type
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when(holder.itemViewType){
0 -> {
val holder_payment = holder as PaymentViewHolder
val menuItem = list!![position] as Purchase
holder_payment.paymentTimeline.marker = ContextCompat.getDrawable(context!!,menuItem.resource)
}
else -> {
val holder_payment = holder as PaymentViewHolder
val menuItem = list!![position] as Purchase
holder_payment.paymentTimeline.marker = ContextCompat.getDrawable(context!!,menuItem.resource)
}
}
}
However when I send the information to be displayed, it always return the list empty, I don't know where is the problem, the data it's seems to be sent nicely.
And the constructor of the RecyclerViewAdapter prints the List obtained, but that list won't display the DesiredViewHolders to be inflated in the RecyclerView...
As requested I added the Layout where I call the RecyclerView
<?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"
android:id="#+id/view_bottom_sheet_attributes"
tools:background="#color/colorWhite">
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="0dp"
android:layout_height="50dp"
android:id="#+id/text_attributes_heading"
android:gravity="center"
android:paddingLeft="12dp"
android:text="TEMPORAL"
android:textSize="18sp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<View
android:layout_width="0sp"
android:layout_height="1dp"
android:background="#color/colorDivider"
app:layout_constraintTop_toBottomOf="#id/text_attributes_heading"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/image_toggle"
android:layout_marginRight="5dp"
app:srcCompat="#drawable/ic_expand_less_black_24dp"
app:layout_constraintTop_toTopOf="#id/text_attributes_heading"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="#id/text_attributes_heading"/>
<androidx.recyclerview.widget.RecyclerView
android:layout_width="0dp"
android:layout_height="0dp"
android:id="#+id/payment_rv"
android:fillViewport="true"
android:padding="5dp"
android:clipToPadding="false"
app:layout_constraintTop_toBottomOf="#id/text_attributes_heading"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="#id/button_apply">
</androidx.recyclerview.widget.RecyclerView>
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:background="#color/colorDivider"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#id/payment_rv"/>
<com.google.android.material.button.MaterialButton
android:layout_width="match_parent"
android:layout_height="45dp"
android:id="#+id/button_apply"
android:layout_margin="5dp"
android:text="#string/purchase"
style="#style/Widget.MaterialComponents.Button"
android:insetTop="0dp"
android:insetBottom="0dp"
app:cornerRadius="0dp"
app:layout_constraintTop_toBottomOf="#id/payment_rv"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Also the type of the ViewHolder it seems that don't reach the override function...
Check your layout XML it looks like the RecyclerView view is hidden by other views
Related
Hello I am doing a little app for my homework and I want to display data from my php api on the app. But the Recycler View send this message 2022-04-25 00:41:36.662 13593-13593/net.robcorp.finalapp E/RecyclerView: No adapter attached; skipping layout before the adapter code runs because this message 2022-04-25 00:41:36.907 13593-13593/net.robcorp.finalapp I/System.out: [Drivers(pos=1, name=Charles Leclerc, nb=16, points=71, title=Ferrari), Drivers(pos=2, name=Esteban Ocon, nb=31, points=20, title=Alpine)] is displayed after the previous one and is suppose to run just before the adapter.
Here is the DriversFragment.kt:
package net.robcorp.finalapp
import android.content.Context
import android.graphics.Insets.add
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.OneShotPreDrawListener.add
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.android.volley.Request
import com.android.volley.Response
import com.android.volley.toolbox.StringRequest
import com.android.volley.toolbox.Volley
import net.robcorp.finalapp.R
import net.robcorp.finalapp.databinding.FragmentDriversBinding
import net.robcorp.finalapp.drivers.DriverFragmentAdapter
import net.robcorp.finalapp.drivers.Drivers
import org.json.JSONArray
class DriversFragment : Fragment(R.layout.fragment_drivers) {
private lateinit var recyclerView: RecyclerView
private lateinit var myAdapter: DriverFragmentAdapter;
lateinit var binding: FragmentDriversBinding
private val drivers = ArrayList<Drivers>()
val url = "https://robcorp.net/f1api/getdrivers.php"
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentDriversBinding.inflate(layoutInflater)
recyclerView = binding.driversRecyclerView
recyclerView.layoutManager = LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false)
myAdapter = DriverFragmentAdapter(drivers)
recyclerView.adapter = myAdapter
println("adapter loaded")
downloadDrivers()
}
fun downloadDrivers() {
val task = Volley.newRequestQueue(this.context)
val request = StringRequest(Request.Method.GET, url, {
response ->
val data = response.toString()
val jArray = JSONArray(data)
// Log.e("Error",response.toString())
for (i in 0..jArray.length()-1) {
val json_data = jArray.getJSONObject(i)
// Log.e("Jobject",json_data.toString())
val pos = i+1
val name = json_data.getString("Name")
val nb = json_data.getString("NB")
val points = json_data.getString("Points")
val title = json_data.getString("Title")
val driver = Drivers(pos, name, nb, points, title)
drivers.add(driver)
}
println(drivers)
myAdapter.notifyDataSetChanged()
}, {
error ->
println(error)
})
task.add(request)
}
}
Here is the DriverFragmentAdapter.kt:
package net.robcorp.finalapp.drivers
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import net.robcorp.finalapp.R
import net.robcorp.finalapp.databinding.DriverListBinding
class DriverFragmentAdapter(private var driversList: List<Drivers>): RecyclerView.Adapter<DriverFragmentAdapter.MyViewHolder>() {
class MyViewHolder(val binding: DriverListBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(driver: Drivers) {
val context = itemView.context
val pos = itemView.findViewById<TextView>(R.id.driver_position)
val bar = itemView.findViewById<TextView>(R.id.driver_bar)
val name = itemView.findViewById<TextView>(R.id.driver_name)
val nb = itemView.findViewById<TextView>(R.id.driver_number)
val team = itemView.findViewById<TextView>(R.id.driver_team)
val points = itemView.findViewById<TextView>(R.id.driver_points)
pos.text = driver.pos.toString()
name.text = driver.name
nb.text = driver.nb
team.text = driver.title
points.text = driver.points
if (driver.title == "Ferrari") {
bar.background = ContextCompat.getDrawable(context, R.drawable.ferrari_bar)
} else if (driver.title == "Alpine") {
bar.background = ContextCompat.getDrawable(context, R.drawable.alpine_bar)
} else if (driver.title == "Red Bull") {
bar.background = ContextCompat.getDrawable(context, R.drawable.redbull_bar)
} else if (driver.title == "Mercedes") {
bar.background = ContextCompat.getDrawable(context, R.drawable.mercedes_bar)
} else if (driver.title == "McLaren") {
bar.background = ContextCompat.getDrawable(context, R.drawable.mclaren_bar)
} else if (driver.title == "Alfa Romeo") {
bar.background = ContextCompat.getDrawable(context, R.drawable.alfaromeo_bar)
} else if (driver.title == "AlphaTauri") {
bar.background = ContextCompat.getDrawable(context, R.drawable.alphatauri_bar)
} else if (driver.title == "Williams") {
bar.background = ContextCompat.getDrawable(context, R.drawable.williams_bar)
} else if (driver.title == "Aston Martin") {
bar.background = ContextCompat.getDrawable(context, R.drawable.astonmartin_bar)
} else if (driver.title == "Haas") {
bar.background = ContextCompat.getDrawable(context, R.drawable.haas_bar)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
return MyViewHolder(DriverListBinding.inflate(LayoutInflater.from(parent.context)))
}
override fun getItemCount(): Int {
return driversList.size
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val driver = driversList[position]
holder.binding.apply {
println("je suis dans le binding")
holder.bind(driver)
}
}
fun setDriversList(driversList: List<Drivers>) {
this.driversList = driversList
notifyDataSetChanged()
}
}
fragment_drivers.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"
tools:context=".MainActivity">
<!-- TODO: Update blank fragment layout -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="#+id/header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/primary"
android:orientation="vertical"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginBottom="10dp">
<TextView
android:id="#+id/calendar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="30dp"
android:layout_marginTop="20dp"
android:layout_marginEnd="30dp"
android:layout_marginBottom="20dp"
android:fontFamily="#font/marianneb"
android:text="Pilotes"
android:textAlignment="center"
android:textColor="#color/white"
android:textSize="25sp" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/drivers_recycler_view"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintTop_toBottomOf="#+id/header"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
driver_list.xml
<?xml version="1.0" encoding="utf-8"?>
<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="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="10dp"
android:layout_marginEnd="20dp"
android:elevation="8dp"
android:orientation="horizontal"
android:padding="10dp"
android:background="#drawable/rectangle">
<TextView
android:id="#+id/driver_position"
android:layout_width="30dp"
android:layout_height="match_parent"
android:fontFamily="#font/marianneb"
android:text="1"
android:textColor="#color/black"
android:textSize="25sp"
android:textAlignment="center"/>
<ImageView
android:id="#+id/driver_bar"
android:layout_width="20px"
android:layout_height="match_parent"
android:background="#drawable/ferrari_bar"/>
<LinearLayout
android:layout_width="250dp"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/driver_name"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:fontFamily="#font/marianneb"
android:text="Charles Leclerc"
android:textColor="#color/black"
android:textSize="15sp"
android:layout_marginStart="10dp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="#+id/driver_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="2.5dp"
android:fontFamily="#font/mariannem"
android:text="16"
android:textColor="#color/black"
android:textSize="10sp" />
<TextView
android:id="#+id/driver_team"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="2.5dp"
android:fontFamily="#font/mariannem"
android:text="Ferrari"
android:textColor="#color/black"
android:textSize="10sp" />
</LinearLayout>
</LinearLayout>
<TextView
android:id="#+id/driver_points"
android:layout_width="50dp"
android:layout_height="match_parent"
android:fontFamily="#font/marianneb"
android:text="578"
android:textColor="#color/black"
android:textSize="25sp"/>
</LinearLayout>
It’s a harmless warning. Since your RecyclerView is already in the layout, it attempts to draw it the moment your view hierarchy becomes visible, sees that there is no adapter assigned, and so it just issues the warning and doesn’t draw it.
Perhaps it would be cleaner to create your adapter (with an empty list) and assign it immediately upon inflating the view, and just update its list when the data is ready.
By the way, it is incorrect to inflate your layout in onCreate. That should be done in onCreateView.
Edit:
Your code here is inflating the layout twice, once for the variable v, and once for the binding property. First of all, there's no reason to inflate it twice. Secondly, since you use v as the return value of onCreateView, anything you do to the views in the binding is pointless and has no effect on the views that are actually on screen.
val v = inflater.inflate(R.layout.fragment_drivers, container, false)
binding = FragmentDriversBinding.inflate(layoutInflater)
// ...
return v
You should only inflate the view once (using the binding) and return the binding's view:
// DELETE THIS LINE: val v = inflater.inflate(R.layout.fragment_drivers, container, false)
binding = FragmentDriversBinding.inflate(layoutInflater)
// ...
return binding.root
Also, it is more proper to set up your views in onViewCreated() instead of in onCreateView(). And if you do that, you actually can move inflation into the super-constructor call by passing the view ID to the Fragment constructor, and then instead of inflating your binding, you bind to the existing view. It would look like this:
class DriversFragment : Fragment(R.layout.fragment_drivers) {
private lateinit var recyclerView: RecyclerView
private lateinit var myAdapter: DriverFragmentAdapter;
lateinit var binding: FragmentDriversBinding
private val drivers = ArrayList<Drivers>()
val url = "https://robcorp.net/f1api/getdrivers.php"
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentDriversBinding.bind(view)
recyclerView = binding.driversRecyclerView
recyclerView.layoutManager = LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false)
myAdapter = DriverFragmentAdapter(drivers)
recyclerView.adapter = myAdapter
println("adapter loaded")
downloadDrivers()
}
//...
}
To add a little bit of clarity to #Tenfour04 's answer.
You can't remove this. They are telling you how to properly inflate it with viewbinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentDriversBinding.inflate(layoutInflater, container, false)
return binding.root
}
also a little side note:
You should destroy your binding like so to prevent memory leaks
override fun onDestroyView() {
super.onDestroyView()
binding = null
}
Now to the question.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//binding = FragmentDriversBinding.bind(view) this is not needed since its already been inflated
//recyclerView = binding.driversRecyclerView why keep recyclerview as another variable when you can access it with binding.driversRecyclerView?
binding.driversRecyclerView.layoutManager = LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false)
myAdapter = DriverFragmentAdapter(drivers)
binding.driversRecyclerView.adapter = myAdapter
println("adapter loaded")
downloadDrivers()
}
Finally the issue is most likely your layout.
Since everything is wrapped inside a linear layout, the constraint layouts position lines don't do anything.
I removed the useless linear layout.
Hopefully this fixes any issues.
sorry about the formatting btw just use CTRL + ALT + L to auto format it
<?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"
tools:context=".MainActivity">
<LinearLayout
android:id="#+id/header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/primary"
android:orientation="vertical"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginBottom="10dp">
<TextView
android:id="#+id/calendar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="30dp"
android:layout_marginTop="20dp"
android:layout_marginEnd="30dp"
android:layout_marginBottom="20dp"
android:fontFamily="#font/marianneb"
android:text="Pilotes"
android:textAlignment="center"
android:textColor="#color/white"
android:textSize="25sp" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/drivers_recycler_view"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintTop_toBottomOf="#+id/header"/>
</androidx.constraintlayout.widget.ConstraintLayout>
My RecyclerView is not able to find element's id. I tried to use findViewById but it is not working as my app gets crashed.
This is my Custom Adapter - MyRecyclerAdapter.kt:
class MyRecyclerAdapter(
private var notiList : ArrayList<NotificationClass>) :RecyclerView.Adapter<MyRecyclerAdapter.RecyclerViewHolder>(){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.item_layout,parent,false)
return RecyclerViewHolder(itemView)}
override fun onBindViewHolder(holder: RecyclerViewHolder, position: Int) {
//Problem Arise here
holder.itemView.titleView.text = notiList[position].title }
override fun getItemCount(): Int = notiList.size
class RecyclerViewHolder(itemView : View) : RecyclerView.ViewHolder(itemView)
}
This is my item's Layout - item_layout.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:elevation="30dp"
android:layout_margin="5dp"
app:cardCornerRadius="10dp" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="#+id/titleView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/name"
android:textSize="25sp"
android:textColor="#color/black"
android:fontFamily="#font/raleway_bold"
android:maxLines="1"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp" />
<TextView
android:id="#+id/descView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/name"
android:textSize="20sp"
android:textColor="#color/black"
android:fontFamily="#font/raleway_semibold"
android:maxLines="3"
android:scrollbars="vertical"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp" />
</LinearLayout>
</androidx.cardview.widget.CardView>
You haven't defined the ViewHolder for your RecyclerView. It should be like below:
class MyRecyclerAdapter(
private var notiList : ArrayList<NotificationClass>) :RecyclerView.Adapter<MyRecyclerAdapter.RecyclerViewHolder>(){
// Define below ViewHolder
internal inner class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) {
var titleView: TextView = view.findViewById(R.id.titleView)
var descView: TextView = view.findViewById(R.id.descView)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.item_layout,parent,false)
return RecyclerViewHolder(itemView)}
override fun onBindViewHolder(holder: RecyclerViewHolder, position: Int) {
//Problem Arise here
holder.itemView.titleView.text = notiList[position].title }
override fun getItemCount(): Int = notiList.size
class RecyclerViewHolder(itemView : View) : RecyclerView.ViewHolder(itemView)
}
Try not to access from android.R. Rather, try something like this:
CategoryAdapterHolder(LayoutInflater.from(parent.context)
.inflate(com.example.secondhand.R.layout.category_list, parent, false))
I'm kinda new to Android App Development. Well, I'm playing around with RecyclerView. I have a parent recyclerview with a modal layout. Now the modal layout has a recyclerview (child recyclerview). I have managed to create adapters and scroll the list of main recyclerview. Unfortunately I don't find a way to scroll the child recyclerview. Here is the code that I'm playing around:
I've already tried setting the adapter of child recyclerview in onBindViewHolder method of parent recyclerview adapter.
Also, I tried setting the attributes nestedScrollingEnabled=true, descendantFocusability=blocksDescendants and focusableInTouchMode=true for child recyclerView.
Here's my activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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=".MainActivity">
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
Parent recyclerview model (model.xml):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:text="Testing" />
<View
android:layout_width="wrap_content"
android:layout_height="1dp"
android:background="#android:color/darker_gray"
android:layout_marginVertical="8dp"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/modalRecyclerView"
android:layout_width="wrap_content"
android:layout_height="100dp" />
</LinearLayout>
Child recyclerview model (child_modal.xml):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Testing"/>
</LinearLayout>
In the MainActivity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerView.apply {
layoutManager = LinearLayoutManager(this#MainActivity, RecyclerView.VERTICAL, false)
adapter = ModalAdapter()
}
}
ModalAdapter:
class ModalAdapter : RecyclerView.Adapter<ModalAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context).inflate(R.layout.model, parent, false)
return ViewHolder(inflater)
}
override fun getItemCount(): Int {
return 5
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.view.modalRecyclerView.adapter = ChildModalAdapter()
holder.view.modalRecyclerView.layoutManager = LinearLayoutManager(holder.view.context, RecyclerView.VERTICAL, false)
}
class ViewHolder(val view: View): RecyclerView.ViewHolder(view)
}
ChildModalAdapter:
class ChildModalAdapter : RecyclerView.Adapter<ChildModalAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context).inflate(R.layout.child_modal, parent, false)
return ViewHolder(inflater)
}
override fun getItemCount(): Int {
return 10
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
}
class ViewHolder(val view: View): RecyclerView.ViewHolder(view)
}
Internal recyclerview doesn't scroll while the parent recyclerview scrolls fine. I'm trying to find a way to make the internal recyclerview to scroll along with parent recyclerview (I want both the recyclerviews to scroll).
Okay, I fixed this by setting a listener.
Here's the code below that fixed it:
val mScrollChangeListener = object : RecyclerView.OnItemTouchListener {
override fun onTouchEvent(rv: RecyclerView, e: MotionEvent) {}
override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
when (e.action) {
MotionEvent.ACTION_MOVE -> {
rv.parent.requestDisallowInterceptTouchEvent(true)
}
}
return false
}
override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {}
}
modalRecyclerView.addOnItemTouchListener(mScrollChangeListener)
I've added this code in the onBindViewHolder() of parent RecyclerView's adapter.
I didn't know what proper title for this issue could be.
I'm doing a chat app and this is the part inside the adapter:
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val msg = messages[position]
if (msg.who == "you"){
holder.messagemelayout.visibility = View.GONE
holder.messageyou.text = msg.message
}else{
holder.messageyoulayout.visibility = View.GONE
holder.messageme.text = msg.message
}
}
So when the message is sent be me then the layout for "you" (messageyoulayout) get's hidden and vice versa
Not when I add new messages like this:
var count = 1
bbb.setOnClickListener {
messageslist.add(Chat("hey " + count.toString(), "me"))
adapter.notifyItemInserted(adapter.itemCount - 1)
count++
}
The result is this:
And when I don't hide any layouts then the text inside the layout which don't get any update is still getting filled by random old stuff:
I hope the issue is understandable.
How can I fix this? Like completely removing the layout which don't get an update or something.
Thanks in advance
EDIT:
Whole adapter:
class ChatAdapter(val context: Context, private val messages: MutableList<Chat>) : RecyclerView.Adapter<ChatAdapter.ViewHolder>(){
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val msg = messages[position]
if (msg.who == "you"){
holder.messageyou.text = msg.message
holder.messageme.text = ""
holder.messageme.setBackgroundResource(0)
holder.messageyou.setBackgroundResource(R.drawable.round_corners_lightgray_color)
}else{
holder.messageme.text = msg.message
holder.messageyou.text = ""
holder.messageme.setBackgroundResource(R.drawable.round_corners_accent_color)
holder.messageyou.setBackgroundResource(0)
}
}
override fun getItemCount() = messages.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.chat, parent, false)
return ViewHolder(view)
}
class ViewHolder(itemView: View?) : RecyclerView.ViewHolder(itemView!!){
val messageyou = itemView!!.messageyou!!
val messageme = itemView!!.messageme!!
val messageyoulayout = itemView!!.messageyoulayout!!
val messagemelayout = itemView!!.messagemelayout!!
}
}
chat.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:card_view="http://schemas.android.com/tools"
android:id="#+id/malsehn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="6dp"
app:cardBackgroundColor="#android:color/transparent"
app:cardElevation="0dp">
<LinearLayout
android:id="#+id/messagemelayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:weightSum="4">
<android.support.constraint.ConstraintLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
</android.support.constraint.ConstraintLayout>
<android.support.constraint.ConstraintLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3">
<TextView
android:id="#+id/messageme"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/round_corners_accent_color"
android:paddingLeft="8dp"
android:paddingTop="6dp"
android:paddingRight="8dp"
android:paddingBottom="6dp"
android:text="TextView"
android:textColor="#android:color/white"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
</LinearLayout>
<LinearLayout
android:id="#+id/messageyoulayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:weightSum="4">
<android.support.constraint.ConstraintLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3">
<TextView
android:id="#+id/messageyou"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/round_corners_lightgray_color"
android:paddingLeft="8dp"
android:paddingTop="6dp"
android:paddingRight="8dp"
android:paddingBottom="6dp"
android:text="TextView"
android:textColor="#android:color/black"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
<android.support.constraint.ConstraintLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
</android.support.constraint.ConstraintLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
Don't call it bugged mess when you don't understand how ViewHolder pattern works.
You should have used multiple itemViewTypes instead of flipping views visibility back-to-back (note this entire code goes into your RecyclerView.Adapter):
// constants
companion object {
const val TYPE_YOU = 1
const val TYPE_ME = 2
}
/* This method is called to determine what type of ViewHolder should be used to represent item at [position] */
override fun getItemViewType(position: Int) = if(messages[position].who == "you") TYPE_YOU else TYPE_ME
/* [viewType] determines what ViewHolder we should create. */
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val layoutId = when(viewType){
TYPE_YOU -> {
// this viewholder will display only messages from "you", inflate only "you" layout
R.layout.chat_item_you
}
TYPE_ME ->{
// this viewholder will display only messages from "me", inflate only "me" layout
R.layout.chat_item_me
}
else -> throw IllegalArgumentException("Unknown viewType: $viewType")
}
val itemView = LayoutInflater.from(parent.context).inflate(layoutId, parent, false)
return ViewHolder(itemView)
}
// then you can use your original bind method, because "you" and "me" messages will never re-user others ViewHolder
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val msg = messages[position]
if (msg.who == "you"){
// holder.itemViewtype should always be equal to TYPE_YOU here
}else{
// holder.itemViewtype should always be equal to TYPE_ME here
}
holder.message.text = msg.message
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
val txtMessage : TextView = itemView.findViewById(R.id.message)
}
Then split chat bubbles into two separate layouts:
chat_item_you.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_margin="6dp"
android:layout_height="wrap_content">
<TextView
android:id="#+id/message"
style="#style/ChatBubbleStyle"
android:background="#drawable/round_corners_lightgray_color"
android:text="TextView"
android:layout_gravity="end" />
</FrameLayout>
And similar but with other color and gravity:
chat_item_me.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_margin="6dp"
android:layout_height="wrap_content">
<TextView
android:id="#+id/message"
style="#style/ChatBubbleStyle"
android:background="#drawable/round_corners_blue_color"
android:text="TextView"
android:layout_gravity="start" />
</FrameLayout>
Use shared style in styles.xml to prevent redundant code:
<style name="ChatBubbleStyle">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:paddingLeft">8dp</item>
<item name="android:paddingTop">6dp</item>
<item name="android:paddingRight">8dp</item>
<item name="android:paddingBottom">6dp</item>
<item name="android:textColor">#android:color/black</item>
<item name="android:textSize">16sp</item>
</style>
After tryng to set width/height of non used holder to 0, removing background and some other stuff which all "worked" but created other strange behaviors I found the only working non bugged solution:
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val msg = messages[position]
if (msg.who == "you"){
holder.messageyou.text = msg.message
holder.messageme.text = ""
holder.messageme.setBackgroundResource(0)
holder.messageyou.setBackgroundResource(R.drawable.round_corners_lightgray_color)
}else{
holder.messageme.text = msg.message
holder.messageyou.text = ""
holder.messageme.setBackgroundResource(R.drawable.round_corners_accent_color)
holder.messageyou.setBackgroundResource(0)
}
}
Yes you have to define the background on BOTH layouts over and over again and tell the holder that there is nothing to fill where it really isn't. Android is a bugged time wasting mess..
I have this drawer which opens normally but the scrolling in it is very slow, and I don't know why knowing that the image it has on top of it is not HD image, and available in the 4 sizes, I've done various examples in Java and it was fine, I wonder if the issue is that I'm using Kotlin in the adapter.
I would appreciate any help.
The activity's layout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/white"
android:fitsSystemWindows="true">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/gradient_background"
android:orientation="vertical">
<include
android:id="#+id/top"
layout="#layout/top_bar"
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize" />
<TextView
android:layout_below="#+id/top"
android:id="#+id/tvTradesTicker"
android:layout_width="match_parent"
android:layout_height="#dimen/trades_ticker_height"
android:background="#color/colorPrimaryLight"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
android:focusable="true"
android:freezesText="true"
android:marqueeRepeatLimit="marquee_forever"
android:padding="#dimen/small_margin"
android:scrollHorizontally="true"
android:singleLine="true"
android:textColor="#color/darkGray"
android:textSize="#dimen/font"
android:visibility="visible" />
<android.support.v7.widget.RecyclerView
android:id="#+id/rvGrid"
android:layout_below="#+id/tvTradesTicker"
android:layout_above="#+id/llFooter"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<include
layout="#layout/footer"
android:id="#+id/llFooter"
android:layout_width="match_parent"
android:layout_height="#dimen/footer_height"
android:layout_alignParentBottom="true"/>
</RelativeLayout>
<android.support.design.widget.NavigationView
android:id="#+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="#color/white"
android:fitsSystemWindows="true" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/white"
android:paddingTop="#dimen/big_margin_padding"
android:orientation="vertical">
<include
layout="#layout/item_drawer_header"
android:id="#+id/header"
android:background="#color/white"
android:layout_width="match_parent"
android:layout_height="#dimen/drawer_header_height" />
<ListView
android:id="#+id/lvItems"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="#color/white"
android:divider="#android:color/transparent"
android:paddingTop="#dimen/small_margin" />
</LinearLayout>
</android.support.design.widget.NavigationView>
The Drawer's Adapter:
class DrawerListAdapter : BaseAdapter {
private var drawerItems: MutableList<DrawerItem> = mutableListOf()
private var context: Context? = null
constructor(context: Context, notesList: MutableList<DrawerItem>) : super() {
this.drawerItems = notesList
this.context = context
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View? {
val view: View?
val vh: ViewHolder
val inflater = LayoutInflater.from(context)
val type = getItemViewType(position)
if (convertView == null) {
view = if (type == 0){
inflater.inflate(R.layout.item_drawer_section, parent, false)
}else{
inflater.inflate(R.layout.item_drawer_child, parent, false)
}
vh = ViewHolder(view)
view.tag = vh
} else {
view = convertView
vh = view.tag as ViewHolder
}
val drawerItem = drawerItems[position]
vh.name.text = drawerItem.name
if (type == 0){
vh.rel.setBackgroundColor(ContextCompat.getColor(context!!, R.color.blue))
vh.name.setBackgroundColor(ContextCompat.getColor(context!!, R.color.colorAccent))
vh.name.setTextColor(ContextCompat.getColor(context!!, R.color.white))
}else if (type == 1){
vh.rel.setBackgroundColor(ContextCompat.getColor(context!!, R.color.white))
vh.name.setBackgroundColor(ContextCompat.getColor(context!!, R.color.white))
vh.name.setTextColor(ContextCompat.getColor(context!!, R.color.colorPrimary))
vh.separator.visibility = View.VISIBLE
}else{
vh.rel.setBackgroundColor(ContextCompat.getColor(context!!, R.color.white))
vh.name.setBackgroundColor(ContextCompat.getColor(context!!, R.color.white))
vh.name.setTextColor(ContextCompat.getColor(context!!, R.color.colorPrimary))
vh.separator.visibility = View.GONE
}
Actions.overrideFonts(context!!, vh.rel)
return view
}
override fun getItem(position: Int): DrawerItem {
return drawerItems[position]
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getCount(): Int {
return drawerItems.size
}
override fun getItemViewType(position: Int): Int {
if (drawerItems[position].isHeader)
return 0
else if (!drawerItems[position].isLast)
return 1
else
return 2
}
override fun getViewTypeCount(): Int {
return 3
}
}
private class ViewHolder(view: View) {
val rel: RelativeLayout = view.findViewById(R.id.rel) as RelativeLayout
val name: TextView = view.findViewById(R.id.name) as TextView
val separator = view.findViewById<View>(R.id.separator)
}
What should I change, the activity layout, or the adapter or what i realy have no idea.
EDIT
I set the visibility of the marquee text view in the layout to gone, and everything worked just fine, does anybody know why? because I don't want to replace this text view with a horizontal recycler view
SOLVED.
Changed the approach completely, I stopped the marquee and applied cross fade animation to the text view, with a handler that changes the object being set in the text view at each iteration, and now it looks perfect and way more elegnat