I know there are few questions about this problem. But none of them didn't solve my problem especially my code is in Kotlin and new working with Fragments. Don't rush to say my question is duplicated.
My problem is exactly what title said, my RecyclerView is populated just with one item(child) from Firebase in my Fragment.
Adapter:
class NewsList(private val userList: List<News>) : RecyclerView.Adapter<NewsList.ViewHolder>() {
private val Context = this
override fun onBindViewHolder(p0: ViewHolder?, p1: Int) {
val news: News = userList[p1]
p0?.mesajTextView?.text = news.text
val time = news.time
val getTimeAgo = GetTimeAgo()
val lastMsg = getTimeAgo.getTimeAgo(time, Context)
p0?.timeNewsTextView!!.text = lastMsg
}
override fun onCreateViewHolder(p0: ViewGroup?, p1: Int): ViewHolder {
val v = LayoutInflater.from(p0?.context).inflate(R.layout.news_layout, p0, false)
return ViewHolder(v)
}
override fun getItemCount(): Int {
return userList.size
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val mesajTextView = itemView.findViewById(R.id.mesajTextView) as TextView
val timeNewsTextView = itemView.findViewById(R.id.timeNewsTextView) as TextView
}
}
My fragment where ReyclerView is populated:
override fun onActivityCreated(savedInstanceState: Bundle?) {
newsRecyclerView.layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
}
private fun populalteQuestionsList() {
val mChatDatabaseReference = FirebaseDatabase.getInstance().reference.child(Constants.NEWS)
mListenerPopulateList = mChatDatabaseReference.addValueEventListener(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (convSnapshot in dataSnapshot.children) {
val news = ArrayList<News>()
val conv = convSnapshot.getValue(News::class.java)
news.add(conv!!)
val adapter = NewsList(news)
newsRecyclerView!!.adapter = adapter
adapter.notifyDataSetChanged()
}
}
override fun onCancelled(databaseError: DatabaseError) {
}
})
mChatDatabaseReference.addListenerForSingleValueEvent(mListenerPopulateList)
}
Layout for items:
<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="wrap_content"
android:orientation="horizontal">
<android.support.v7.widget.CardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="#+id/card_view"
android:layout_gravity="center"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
card_view:cardCornerRadius="2dp"
card_view:contentPadding="10dp"
card_view:cardElevation="10dp">
<RelativeLayout
android:id="#+id/relativeLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="10dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="10dp">
<TextView
android:id="#+id/mesajTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginStart="64dp"
android:textAppearance="?android:attr/textAppearanceLarge"
tools:text="Mesaj" />
<TextView
android:id="#+id/timeNewsTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginEnd="16dp"
android:layout_marginTop="4dp"
android:maxLength="15"
android:maxLines="1"
android:textAppearance="?android:attr/textAppearanceSmall"
tools:text="14:20" />
</RelativeLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
Hope you understand, please help me I really need to finish this app.
You are creating new list with having single element in loop and passing it to adapter so it has only one element to show so
Move this outside loop
val adapter = NewsList(news)
newsRecyclerView!!.adapter = adapter
adapter.notifyDataSetChanged()
and initialise list outside for loop
val news = ArrayList<News>()
for (convSnapshot in dataSnapshot.children) {
val conv = convSnapshot.getValue(News::class.java)
news.add(conv!!)
}
val adapter = NewsList(news)
newsRecyclerView!!.adapter = adapter
adapter.notifyDataSetChanged()
Note : fill_parent has been deprecated so us match_parent
You're recreating the dataset at every iteration, so it will always have the last added item, move the instantiation of the datasource to outside the loop. Also, try not adding the values for the adapter at every iteration. When you're done adding items to the datasource, then you should add them to the adapter and set the adapter in the recyclerview. :
val news = ArrayList<News>()
for (convSnapshot in dataSnapshot.children) {
val conv = convSnapshot.getValue(News::class.java)
news.add(conv!!)
}
val adapter = NewsList(news)
newsRecyclerView!!.adapter = adapter
adapter.notifyDataSetChanged()
Related
I am trying to fetch the data from Google Spreadsheet and display it in a recyclerView in Kotlin. I could do that without any error but the issue I am facing is when I scroll up or down the data in the recyclerView get disappeared. When I scroll up and then scroll down I can see that all the data that went up is missing and the same with scrolling down. But if I scroll up for more I can see one line of data after every few scrolls.
Another issue I have is with the date that is being displayed. My data in the Google Spreadsheet starts from 01-Jan-2023 (this is how it's shown in the spreadsheet, and without time in it), when it's shown in the recyclerView, all dates are one day earlier. I mean, it shows 31-Dec-2022 for 01-Jan-2023, 01-Jan-2023 for 02-Jan-2023 and so on.
Can somebody help correct my mistakes and improve my code? I have been after this for a couple of days and I couldn't fix the issue.
My code is,
SalesData.kt
class SalesData : AppCompatActivity() {
private lateinit var binding: ActivitySalesDataBinding
#SuppressLint("NotifyDataSetChanged")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivitySalesDataBinding.inflate(layoutInflater)
setContentView(binding.root)
val salesList = arrayListOf<SalesDataModel>()
val queue = Volley.newRequestQueue(this)
val url = "https://script.google.com/macros/s/AKfsdaffdbghjhfWVM2FeIH3gZY5kAnb6JVeWpg2XeBOZyU6sghhfkuytytg/exec"
val jsonObjectRequest = object: JsonObjectRequest(
Request.Method.GET,url,null,
Response.Listener {
val data = it.getJSONArray("items")
for(i in 0 until data.length()){
val salesJasonObject = data.getJSONObject(i)
val dt = salesJasonObject.getString("Date")
val dateFmt = SimpleDateFormat("yyyy-MM-dd", Locale.US).parse(dt)
val formattedDatesString = dateFmt?.let { it1 -> SimpleDateFormat("dd-MMM-yyyy", Locale.US).format(it1) }
val salesObject = formattedDatesString?.let { it1 ->
SalesDataModel(
// salesJasonObject.getString("Date"),
it1,
salesJasonObject.getString("Branch"),
salesJasonObject.getDouble("NetSale"),
salesJasonObject.getDouble("Profit"),
)
}
if (salesObject != null) {
salesList.add(salesObject)
}
val adapter = SalesDataRecyclerAdapter(this#SalesData,salesList)
binding.rvSalesData.adapter = adapter
binding.rvSalesData.layoutManager = LinearLayoutManager(this#SalesData)
binding.rvSalesData.setHasFixedSize(true)
adapter.notifyDataSetChanged()
}
Toast.makeText(this#SalesData, "Data loaded successfully", Toast.LENGTH_LONG).show()
},Response.ErrorListener {
Toast.makeText(this#SalesData, it.toString(), Toast.LENGTH_LONG).show()
}
){
override fun getHeaders(): MutableMap<String, String> {
return super.getHeaders()
}
}
Toast.makeText(this#SalesData, "Hi", Toast.LENGTH_LONG).show()
queue.add(jsonObjectRequest)
}
}
SalesDataRecyclerAdapter.kt
class SalesDataRecyclerAdapter(
val context: Context,
private val saleDataList:ArrayList<SalesDataModel>
):RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return MyViewHolder(
SalesDataLayoutBinding.inflate(
LayoutInflater.from(parent.context),
parent, false
)
)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val model = saleDataList[position]
if (holder is MyViewHolder){
holder.binding.tvSales.text = model.salesAmount.toString()
holder.binding.tvBranch.text = model.branch
holder.binding.tvDate.text = model.date
holder.binding.tvProfit.text = model.profit.toString()
}
}
override fun getItemCount(): Int {
return saleDataList.size
}
class MyViewHolder(val binding: SalesDataLayoutBinding) : RecyclerView.ViewHolder(binding.root)
}
activity_sales_data.xml
<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:background="#color/white"
tools:context=".SalesData">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<!--
<androidx.core.widget.ContentLoadingProgressBar
android:id="#+id/progressbar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>-->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/new_green"
android:padding="10dp"
android:text="SALES DATA"
android:textColor="#color/white"
android:textSize="24sp"
android:layout_gravity="bottom|end"/>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rvSalesData"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
sales_data_layout.xml
<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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="#+id/tvDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:text="Date"
android:layout_weight="1"/>
<TextView
android:id="#+id/tvBranch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:text="Branch"
android:layout_weight="1"/>
<TextView
android:id="#+id/tvSales"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:text="Sales"
android:layout_weight="1"/>
<TextView
android:id="#+id/tvProfit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:text="Profit"
android:layout_weight="1"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Since your root view in sales_data_layout.xml has the size:
android:layout_width="match_parent"
android:layout_height="match_parent"
Each item will take up the whole parent area, which in your case will end up with each individual item taking up the whole screen, and thus needing multiple scrolls to see the next item. You probably want to change the height to wrap_content for the root view, to see more items on the screen at once.
Add a comparator that tells the recylcer view exactly when to redraw.
class WordsComparator : DiffUtil.ItemCallback<Word>() {
override fun areItemsTheSame(oldItem: Word, newItem: Word): Boolean {
//=== here doesn't work for complex objects
// simple high-speed code goes here it is called over and over
// my app the same item has the same id easy compare
return (oldItem._id == newItem._id)
}
override fun areContentsTheSame(oldItem: Word, newItem: Word): Boolean { // you developer have to compare the contents of complex objects
// you need high speed code here for best results
// if possible don't call any functions that could do other
// unecessary things.
// compare the contents of the complex items.
return (oldItem._id == newItem._id
&& oldItem.checked == newItem.checked
&& oldItem.word == newItem.word
&& oldItem.color == newItem.color
&& oldItem.recolor == newItem.recolor
&& oldItem.rechecked == newItem.rechecked)
}
}
I have implemented RecyclerView in my app with Kotlin using Refrofit, MVVM, DataBinding, Coroutines. The problem is i have some nested arrays which i want to parse using viewModel. Here is the structure of the api:
What i want is to parse the inner services arrays inside the categoryService array.
The xml items layout is like this:
<data>
<variable
name="services"
type="com.techease.fitness.models.categoryServices.Service" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_margin="#dimen/tenDP"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/round_card_view"
android:orientation="vertical"
android:padding="#dimen/tenDP"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="#+id/ivService"
android:layout_width="150dp"
android:layout_height="150dp"
android:scaleType="fitXY"
android:padding="#dimen/tenDP"
android:src="#drawable/test_image_24"
bind:avatar="#{services.attachment}" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:hint="#string/user_name"
android:text="#{services.title}"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:hint="#string/user_name"
android:text="#{services.duration}" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Here is my existing viewModel which only returns the last item from categoryService[last] and displaying the services items.
private lateinit var job: Job
private val catServices = MutableLiveData<List<Service>>()
val discoverServices: MutableLiveData<List<Service>>
get() = catServices
fun discoverServices(context: Context) {
job = CoroutinesIO.ioThenMain(
{
repository.discoverServices(context)
}, {
for (i in 0 until it!!.data.categoryServices.size) {
for (services in 0 until it.data.categoryServices.get(i).services.size) {
catServices.value = it.data.categoryServices.get(i).services
}
}
}
)
}
i have nested for loops in viewModel which is parsing the target array correctly but only displaying the last item service array. Here is the recyclerView implementation:
val apiRepository = ApiRepository()
factory = DiscoverViewModelFactory(apiRepository)
viewModel = ViewModelProvider(this, factory).get(DiscoverServiceViewModel::class.java)
binding.viewModel = viewModel
viewModel.discoverServices(this)
viewModel.discoverServices.observe(this, Observer { services ->
rvFeatured.also {
it.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
it.setHasFixedSize(true)
if (services != null) {
it.adapter = DiscoverServicesAdapter(services, this)
}
}
})
And here is the adapter which is fully binded:
private val services: List<Service>,
private val listenerHomeServices: DiscoverServicesClickListener
) : RecyclerView.Adapter<DiscoverServicesAdapter.ServicesViewHolder>() {
override fun getItemCount() = services.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
ServicesViewHolder(
DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.custom_discover_layout,
parent,
false
)
)
override fun onBindViewHolder(holder: ServicesViewHolder, position: Int) {
holder.recyclerViewServicesBinding.services = services[position]
}
class ServicesViewHolder(
val recyclerViewServicesBinding: CustomDiscoverLayoutBinding
) : RecyclerView.ViewHolder(recyclerViewServicesBinding.root)}
Again i want to parse the nested service array, but currently only the last categoryService1 service array items are displayed by recyclerView. Hope this make sense.
I am trying to develop social networking app which uses Android Jetpack Libraries but while using Navigation Component to use bottom navigation to navigate through fragments inside an activity , This Adapter throws an error at LayoutInflator() which causes app to crash
Can anyone help me through this:
My Adapter Class:
class FeedAdapter : PagedListAdapter<feed,FeedAdapter.ViewHolder>(FeedDiffCallBack()){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val userPost = LayoutInflater.from(parent.context)
.inflate(R.layout.feedrow,parent,false)
return ViewHolder(userPost)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val feedItem = getItem(position)
if(feedItem != null){
holder.bind(feedItem)
}
}
class ViewHolder(itemView:View):RecyclerView.ViewHolder(itemView) {
//Retrieve data
private val username:TextView = itemView.post_name
private val userPic:ImageView = itemView.feedImage1
private val location:TextView = itemView.postLocation
private val time:TextView = itemView.postTime
private val post:ImageView = itemView.postImage
fun bind(feed: feed) = with(itemView){
//TODO:Bind Data with View
showFeedData(feed)
}
private fun showFeedData(feed: feed) {
username.text = feed.username
userPic.setImageURI(null)
userPic.visibility = View.GONE
location.text = feed.location
time.text = feed.timeStamp.toString()
post.setImageURI(Uri.parse(feed.mUrl))
}
}
}
class FeedDiffCallBack : DiffUtil.ItemCallback<feed>() {
override fun areItemsTheSame(oldItem:feed, newItem: feed): Boolean {
return oldItem?.id == newItem?.id
}
override fun areContentsTheSame(oldItem: feed, newItem: feed): Boolean {
return oldItem == newItem
}
}
Fragment Class:
class FeedFragment : Fragment() {
companion object {
fun newInstance() = FeedFragment()
}
private lateinit var viewModel: FeedViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.feed_fragment, container, false)
val context = getContext() ?: return view
val factory = InjectorUtils.provideViewModelFactory(context)
viewModel =
ViewModelProviders.of(this,factory).get(FeedViewModel::class.java)
val adapter = FeedAdapter()
view.findViewById<RecyclerView>(R.id.feedView).adapter = adapter
view.findViewById<RecyclerView>(R.id.feedView).layoutManager =
LinearLayoutManager(MyApplication.getContext())
subscribeUI(adapter)
return view
}
private fun subscribeUI(adapter: FeedAdapter) {
viewModel.showFeed().observe(this, object:Observer<PagedList<feed>>{
override fun onChanged(t: PagedList<feed>?) {
adapter.submitList(t)
adapter.notifyDataSetChanged()
}
})
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
}
}
feed_row.xml
Individual item for recycler view -->
<RelativeLayout
android:id="#+id/postContainer"
android:layout_margin="10dp"
android:elevation="2dp"
android:background="#drawable/bg_parent_rounded_corner"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!--TODO:Change to Circle Image View-->
<ImageView
android:id="#+id/profileImage"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginTop="5dp"
android:layout_marginLeft="5dp"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
/>
<LinearLayout
android:id="#+id/postDetail_1"
android:orientation="vertical"
android:layout_marginTop="5dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="20dp"
android:layout_alignParentRight="true">
<TextView
android:id="#+id/post_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="#style/LabelStyle"
android:textSize="15sp"
android:fontFamily="#font/sf_pro_display_semibold" />
<LinearLayout
android:id="#+id/postDetail_2"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="#+id/postLocation"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/postTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="120dp" />
</LinearLayout>
</LinearLayout>
<ImageView
android:id="#+id/postImage"
android:layout_height="200dp"
android:layout_width="match_parent"
android:layout_below="#+id/postDetail_1"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:layout_marginTop="6dp"
/>
</RelativeLayout>
Your issue might be in this method,
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val feedItem = getItem(position)
if(feedItem != null){
holder.bind(feedItem)
}
}
Your ViewHolder is using poisition from method parameter which might be inconsistent
See from here,
So, you should change line to this:
val feedItem = getItem(holder.adapterPosition)
instead of
val feedItem = getItem(position)
I hope it resolves the issue.
This might help some one looking for the same Exception to be cleared :
What I did is that my feed_row.xml above is included inside < layout > tags and I changed it in this way , So the exception got cleared:
Before Exception:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
After changing to this Exception cleared:
<layout 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">
I don't know how it works but it did work!!! So Anyone who knows what happens there can explain please!
I was trying to make a custom RecyclerView with Adapter which takes a HashMap as parameter, I succeeded but now only 1 item is showing in recyclerView list and I have no idea why.
Here my code snippet:
Custom RecyclerAdapter:
class ProductsRecyclerAdapter(private val dataSet: HashMap<Int, ProductEntry>) : RecyclerView.Adapter<ProductsRecyclerAdapter.ViewHolder>() {
class ViewHolder(val linearLay: LinearLayout) : RecyclerView.ViewHolder(linearLay)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) : ProductsRecyclerAdapter.ViewHolder {
val linearLay = LayoutInflater.from(parent.context).inflate(R.layout.test_text_view, parent, false) as LinearLayout
return ViewHolder(linearLay)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.linearLay.textView.text = dataSet[position]?.pName
holder.linearLay.textView2.text = dataSet[position]?.pExpiry
}
override fun getItemCount(): Int {
return dataSet.size
}
}
Initialization:
// Setup RecyclerView
val prod1 = ProductEntry("Milk", "04/06/1996")
val prod2 = ProductEntry("Bread", "04/01/2012")
val testArray : HashMap<Int, ProductEntry> = hashMapOf(1 to prod1, 2 to prod2)
viewManager = LinearLayoutManager(this)
viewAdapter = ProductsRecyclerAdapter(testArray)
recyclerView = findViewById<RecyclerView>(R.id.productsRecyclerView).apply {
setHasFixedSize(true)
layoutManager = viewManager
adapter = viewAdapter
}
And finally, custom XML for row:
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/linearLay"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView" />
<TextView
android:id="#+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="40dp"
android:layout_weight="1"
android:text="TextView" />
Despite everything seems to be OK, I get only 1 row showing "milk".
Thanks for all help!
position in override fun onBindViewHolder(holder: ViewHolder, position: Int) starts at index 0.
So in your case, it will be [0, 1] and your map keys are [1, 2]. Only key "1" will be displayed correctly.
try:
val testArray : HashMap<Int, ProductEntry> = hashMapOf(0 to prod1, 1 to prod2)
But no need to use a map here! Just use a simple list of ProductEntry.
I have a recyclerView with a list of items that I want to scroll to when the certain event happens. I am using smoothScrollToPosition but to my surprise, it is not only not smooth at all, but also I get a flicker effect like it has to restore a base position before actually scrolling. The effect can be seen here:
http://i.imgur.com/rrpLr7N.mp4
Is this normal behavior?
Adapter code:
#ActivityScope
class HighlightListAdapter #Inject constructor() : RecyclerView.Adapter<HighlightListAdapter.ViewHolder>() {
private var highlights: List<Highlight> = emptyList()
private var itemClick: ((Highlight, Int) -> Unit)? = null
private var selectedRow: MutableList<Int> = mutableListOf()
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val binding = holder.binding
val highlight = highlights[position]
var viewModel = binding.viewModel
viewModel?.unbind()
viewModel = HighlightViewModel(highlight)
binding.viewModel = viewModel
viewModel.bind()
if(selectedRow.contains(position)) {
binding.rootItemView.alpha = 1.0f
}
else {
binding.rootItemView.alpha = 0.5f
}
holder.setClickListener(itemClick)
}
override fun getItemCount(): Int = highlights.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding = DataBindingUtil.inflate<ItemHighlightListBinding>(
LayoutInflater.from(parent.context),
R.layout.item_highlight_list,
parent,
false
)
return ViewHolder(binding)
}
fun updateEvents(highlights: List<Highlight>) {
this.highlights = highlights
notifyDataSetChanged()
}
fun setClickListener(itemClick: ((Highlight, Int) -> Unit)?) {
this.itemClick = itemClick
}
fun enableRow(index: Int) {
//Clear previous selection (only if we want single selection)
selectedRow.clear()
//Select specified row
selectedRow.add(index)
//Let the adapter redraw
notifyDataSetChanged()
}
class ViewHolder(val binding: ItemHighlightListBinding) : RecyclerView.ViewHolder(binding.root) {
fun setClickListener(callback: ((Highlight, Int) -> Unit)?) {
binding.viewModel.clicks().subscribe {
callback?.invoke(binding.viewModel.highlight, adapterPosition)
}
}
}
fun getSelected() = selectedRow
}
layout file:
<data>
<variable
name="viewModel"
type="tv.mycujoo.mycujooplayer.ui.video.highlights.HighlightViewModel" />
</data>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/root_item_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:alpha="0.5"
android:orientation="horizontal"
android:clickable="true"
android:onClick="#{() -> viewModel.onClick()}"
android:background="#color/dark_black"
android:padding="#dimen/single_padding"
>
<TextView
android:layout_width="48dp"
android:layout_height="wrap_content"
android:textStyle="bold"
android:gravity="right"
android:text="#{viewModel.time}"
android:textColor="#color/light_gray"
android:singleLine="true"
android:ellipsize="marquee"
android:layout_marginRight="#dimen/single_padding"
style="#style/Base.TextAppearance.AppCompat.Title"
tools:text="122''"/>
<ImageView
android:background="#drawable/bg_circle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="#dimen/single_padding"
android:padding="#dimen/single_padding"
tools:src="#mipmap/ic_launcher_round"
app:imageResource="#{viewModel.image}"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#color/light_gray"
tools:text="#{viewModel.name}"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#color/light_gray"
tools:text="#{viewModel.team}"/>
</LinearLayout>
<ImageView
android:visibility="gone"
android:layout_width="48dp"
android:layout_height="48dp"
android:src="#drawable/tv_avatar_default"
/>
</LinearLayout>
</layout>
I think the problem lies in enable row notifyDataSetChanged().It refreshes the entire dataset which results in flicking of the list.Try this
fun enableRow(index: Int) {
//Clear previous selection (only if we want single selection)
selectedRow.clear()
//Select specified row
selectedRow.add(index)
//Let the adapter redraw
notifyItemRangeChanged(index,highlights.size())
}