I'm currently trying to create a dynamic header with a recyclerView. I have written the ListAdapter aswell as the ViewHolder. The custom list elements are added, and also the numer of the elements within the list is correct, but somehow it's not showing the object data, but only the dummyText that was added at the layoutdesign.
HeaderlistAdapter:
class HeaderListAdapter(val context: Context, val headers: List<CustomHeader>) : RecyclerView.Adapter<HeaderViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): HeaderViewHolder {
val view = LayoutInflater.from(context).inflate(R.layout.ui_basic_custom_list_element_header, parent, false)
return HeaderViewHolder(view)
}
override fun getItemCount(): Int {
return headers.size
}
override fun onBindViewHolder(holder: HeaderViewHolder?, position: Int) {
holder?.bindHeader(headers[position])
}
fun setFocus(step:UIStep)
{
for(header in headers)
header.Active=header.MainContent==step
notifyDataSetChanged()
}
}
HeaderViewHolder:
class HeaderViewHolder:RecyclerView.ViewHolder{
#Bind(R.id.ui_adapter_main) var mainText:TextView?=null
#Bind(R.id.ui_adapter_additional) var additionalText:TextView?=null
#Bind(R.id.ui_adapter_layout) var layout:LinearLayout?=null
constructor(itemView: View): super(itemView){
ButterKnife.bind(this,itemView)
}
fun bindHeader(header:CustomHeader){
if(header.Active) {
mainText?.text = header.MainContent.description
additionalText?.text=header.AdditionalText
layout?.setBackgroundColor(R.color.colorBackgroundActive.toInt())
}
else{
mainText?.text=header.MainContent.number.toString()
additionalText?.text=""
layout?.setBackgroundColor(R.color.colorBackgroundInactive.toInt())
}
}
}
Here is, how the listAdapter looks within the view
<android.support.v7.widget.RecyclerView
android:id="#+id/ui_basic_lv_header"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:background="#color/colorBackgroundInactive"
android:layout_weight="1" />
Below here you se the xml of the custom element
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
android:background="#color/colorBackgroundInactive"
android:textColor="#color/colorHeaderFont"
android:id="#+id/ui_adapter_layout">
<TextView
android:id="#+id/ui_adapter_main"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="Main Info"
android:textSize="36sp"/>
<TextView
android:id="#+id/ui_adapter_additional"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="additional"
android:gravity="center_vertical"/>
Looks like there was something wrong with the
#Bind
I replaced that by using findViewById within the binHeader function:
fun bindHeader(header:CustomHeader){
val mainText = itemView.findViewById<TextView>(R.id.ui_adapter_main)
val additionalText = itemView.findViewById<TextView>(R.id.ui_adapter_additional)
val layout = itemView.findViewById<LinearLayout>(R.id.ui_adapter_layout)
if(header.Active) {
mainText?.text = header.MainContent.description
additionalText?.text=header.AdditionalText
layout?.setBackgroundColor(R.color.colorBackgroundActive.toInt())
}
else{
mainText?.text=header.MainContent.number.toString()
additionalText?.text=""
layout?.setBackgroundColor(R.color.colorBackgroundInactive.toInt())
}
}
contentList may be null ,if contentList is Null, the method following '?' will not execute . and after setting the adapter can not call notifyDataSetChanged ;
You should set layout manager for recyclerview
myRecyclerView.setLayoutManager(linearLayoutManagerVertical);
Related
I stocked in a problem. I have a listView and this listView is just for showing data row by row and items are all disabled using override function isEnabled to false.
so this is my code:
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/baseLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:paddingTop="10dp"
android:paddingBottom="10dp">
<ListView
android:id="#+id/listView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="false"
android:divider="#null"
android:dividerHeight="0dp"
android:focusable="false"
android:focusableInTouchMode="false"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
I need to set click listener on baseLayout but every thing I've done not made the focus on baseLayout and listview calls absListView touch listener. any idea?
...
You have to set listener on list cell, such as.
It is example for recyler view, but it is same case on listview.
class SearchListAdapter(private val context: Context) :
RecyclerView.Adapter() {
private var products: List<Repo> = arrayListOf()
var listener: AdapterListener? = null
override fun onCreateViewHolder(parent: ViewGroup, i: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_repo, parent, false)
return ViewHolder(view)
}
override fun getItemCount(): Int {
return products.size
}
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
viewHolder.bind(position, products[position])
}
fun setList(contacts: List<Repo>) {
this.products = contacts
notifyDataSetChanged()
}
inner class ViewHolder(itemView: View) : BindingViewHolder<ItemRepoBinding>(itemView) {
fun bind(pos: Int, contact: Repo) {
if (binding == null) {
return
}
binding.tvTitle.text = contact.name
binding.setVariable(
BR.tintlist,
context.resources.getColorStateList(R.color.xml_color_btn_title)
)
binding.root.setOnClickListener(View.OnClickListener {
if (listener != null) {
listener?.onClick(pos, contact)
}
})
}
}
interface AdapterListener {
fun onClick(position: Int, info: Repo)
}
}
Previously I have asked about calling API in fragments and now that I have my data returning in my app I would like to know how to make RecyclerView in this fragments?
Basically this is what i'm looking for to achieve at the end
Code
HomeFragment.kt
class HomeFragment : Fragment() {
private lateinit var homeViewModel: HomeViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
homeViewModel = ViewModelProviders.of(this).get(HomeViewModel::class.java)
val root = inflater.inflate(R.layout.fragment_home, container, false)
val textView: TextView = root.findViewById(R.id.text_home)
//calling the API
callAPIDemo(textView)
// homeViewModel.text.observe(this, Observer {
// textView.text = it
// })
return root
}
fun callAPIDemo(textView: TextView) {
// Instantiate the RequestQueue.
val queue = Volley.newRequestQueue(activity)
val url = "https://example.com/api/listings"
// Request a string response from the provided URL.
val stringRequest = StringRequest(
Request.Method.GET, url,
Response.Listener<String> { response ->
// Display the first 500 characters of the response string.
textView.text = "Response is: ${response.substring(0, 500)}"
},
Response.ErrorListener { textView.text = "Something is wrong!" })
// Add the request to the RequestQueue.
queue.add(stringRequest)
}
}
fragment_home.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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/text_home"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:textAlignment="center"
android:textSize="20sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
This is all i have at the moment.
Please note that I'm newbie in this, giving help with little bit of detailed explanation is much appreciated.
Update
Here is what I've created so far
HomeFragment.kt
class HomeFragment : Fragment() {
private lateinit var homeViewModel: HomeViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
homeViewModel = ViewModelProviders.of(this).get(HomeViewModel::class.java)
val root = inflater.inflate(R.layout.fragment_home, container, false)
val textView: TextView = root.findViewById(R.id.recycler)
//calling the API
//callAPIDemo(textView)
val adapter = RecyclerAdapter(callAPIDemo(textView))
recycler.adapter = adapter
return root
}
fun callAPIDemo(textView: TextView) {
// Instantiate the RequestQueue.
val queue = Volley.newRequestQueue(activity)
val url = "https://example.com/api/listings"
// Request a string response from the provided URL.
val stringRequest = StringRequest(
Request.Method.GET, url,
Response.Listener<String> { response ->
// Display the response.
textView.text = "Response is: ${response.substring(0)}"
},
Response.ErrorListener { textView.text = "Something is wrong!" })
// Add the request to the RequestQueue.
queue.add(stringRequest)
}
}
ListingAdapter.kt
This class is red and returning error of
Parameters must have type annotation
class RecyclerAdapter(List) :
RecyclerView.Adapter<RecyclerAdapter.RecyclerViewHolder>() {
//Binding data for each tile
override fun onBindViewHolder(holder: RecyclerViewHolder, position: Int) {
}
//Creation of view holder for each tile
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerViewHolder {
val inflater = LayoutInflater.from(parent.context)
context = parent.context //this context is red
return RecyclerViewHolder(inflater, parent)
}
//View Holder for each item in recycler
inner class RecyclerViewHolder(inflater: LayoutInflater, parent: ViewGroup) :
RecyclerView.ViewHolder(inflater.inflate(R.layout.listings_layout, parent, false)) {
}
override fun getItemCount(): Int {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
listings_layout.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:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.cardview.widget.CardView
android:layout_margin="3dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/imageViewImage"
android:layout_width="match_parent"
android:layout_height="195dp"
android:background="#color/colorPrimary"
android:contentDescription="#string/Image"
android:scaleType="matrix" />
<TextView
android:id="#+id/textViewTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/MakeUp"
android:textAppearance="#style/TextAppearance.AppCompat.Large" />
<TextView
android:id="#+id/textViewSlug"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/MakeUp"
android:textAppearance="#style/TextAppearance.AppCompat.Large" />
</RelativeLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
And finally my fragment_home.xml
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:textAlignment="center"
android:textSize="20sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:id="#+id/recycler" />
First You will need to create a layout for how a single row in your list will look(considering we are creating horizontal listing)
navigate to res > layout folder in your project, in layout folder create a Layout Resource File (fancy name, but it's just a XML file)
for example:-
layout_demo_item.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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp">
<ImageView
android:id="#+id/a_image"
android:layout_width="80dp"
android:layout_height="80dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/text_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="#+id/a_image"
app:layout_constraintStart_toEndOf="#+id/a_image"
app:layout_constraintTop_toTopOf="#+id/a_image" />
<TextView
android:id="#+id/text_random"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="#+id/text_title"
app:layout_constraintStart_toEndOf="#+id/text_title"
app:layout_constraintTop_toTopOf="#+id/text_title" />
</androidx.constraintlayout.widget.ConstraintLayout>
Second you need an Adapter.
An Adapter will take a list/array of data from you, render number of rows based on size of list and assign the data to each row based on position on list
for example:-
You have an array of 6 names, then the adapter will create 6 identical row based on layout_demo_item.xml and assign data like 0th position data in array will be assigned to first element of list
MyAdapter.kt
class MyAdapter(var dataList: MutableList<DataModelObject>) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
// inflating a layout in simple word is way of using a xml layout inside a class.
// if you look this is the same thing that your fragment doing inside onCreateView and storing that into "root" variable, we just did that in "view"
// we passed "view" to MyViewHolder and returned an object of MyViewHolder class(this object actually a single row without data)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.layout_demo_item, parent, false)
return MyViewHolder(view)
}
//the returned object of MyViewHolder class will come here and also with the position on which it gonna show,
// now based on this position we can get the value from our list and finally set it to the corresponding view that we have here in variable "holder"
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.textView.text = dataList.get(position).title
holder.textViewRandom.text = dataList.get(position).randomText
//hardcoding the image, just for simplicity, you can set this also from data list same as above
holder.aImage.setImageResource(R.mipmap.ic_launcher)
//here is the image setup by using glide
//Glide.with(holder.aImage.context).load(dataList.get(position).image).into(holder.aImage)
}
// this method telling adapter to how many total rows will be rendered based on count, so we returning the size of list that we passing
override fun getItemCount(): Int {
return dataList.size
}
// while creating the object in onCreateViewHolder, we received "view" here, and as you can see in your fragment,
// we can do find views by id on it same as we doing in fragment
// so your MyViewHolder now hold the reference of the actual views, on which you will set data
class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var textView: TextView
var textViewRandom: TextView
var aImage: ImageView
init {
textViewRandom = itemView.findViewById(R.id.text_random)
textView = itemView.findViewById(R.id.text_title)
aImage = itemView.findViewById(R.id.a_image)
}
}
}
here is the data class that we using as our model class(same as a POJO class in Java but it's just in Kotlin)
data class DataModelObject(var randomText:String,var title :String) {
}
We have all things set to create a recyclerView, so now we will define recyclerView in Fragment like
fragment_home.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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/text_home"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:textAlignment="center"
android:textSize="20sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler_view_home"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/text_home" />
</androidx.constraintlayout.widget.ConstraintLayout>
Finally Inside kotlin class setting up a recyclerView
HomeFragment.kt
(i'm not including the irrelevant methods and variable that you have in this fragment, please consider them)
class HomeFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val root = inflater.inflate(R.layout.fragment_home, container, false)
val textView: TextView = root.findViewById(R.id.text_home)
val recyclerView: RecyclerView = root.findViewById(R.id.recycler_view_home)
callAPIDemo(textView)
setUpRecyclerView(recyclerView);
return root
}
private fun setUpRecyclerView(recyclerView: RecyclerView) {
// first our data list that can come from API/Database
// for now i'm just manually creating the list in getDummyData()
val list: MutableList<DataModelObject> = getDummyData()
// created an adapter object by passing the list object
var myAdapter = MyAdapter(list)
//it's a layoutManager, that we use to tell recyclerview in which fashion we want to show list,
// default is vertical even if you don't pass it and just pass the activity , and we don't want to reversing it so false
// by customizing it you can create Grids,vertical and horizontal lists, even card swipe like tinder
val linearLayoutManager = LinearLayoutManager(activity,LinearLayoutManager.VERTICAL,false)
// just set the above layout manager on RecyclerView
recyclerView.layoutManager = linearLayoutManager
//and give it a adapter for data
recyclerView.adapter = myAdapter
}
private fun getDummyData(): MutableList<DataModelObject> {
val list: MutableList<DataModelObject> = mutableListOf(
DataModelObject("dfgdfg", "title 1"),
DataModelObject("tyuityui", "title 2"),
DataModelObject("yuti", "title 3"),
DataModelObject("uY9NOrc-=s180-rw", "title 4"),
DataModelObject("logo_color_272x92dp.png", "title 5"),
DataModelObject("NOVtP26EKH", "title 6"),
DataModelObject("googlelogo_color", "title 7"),
DataModelObject("sNOVtP26EKHePkwBg-PkuY9NOrc-", "title 8")
)
return list
}
}
At first create a single item xml for each item of your recycler.
Then add the recycler in your fragment xml:
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler"
android:layout_width="match_parent"
android:layout_width="wrap_content" />
Then create an adapter class as shown below:
class RecyclerAdapter(List) :
RecyclerView.Adapter<RecyclerAdapter.RecyclerViewHolder>() {
//Binding data for each tile
override fun onBindViewHolder(holder: RecyclerViewHolder, position: Int) {
}
//Creation of view holder for each tile
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerViewHolder {
val inflater = LayoutInflater.from(parent.context)
context = parent.context
return RecyclerViewHolder(inflater, parent)
}
//View Holder for each item in recycler
inner class RecyclerViewHolder(inflater: LayoutInflater, parent: ViewGroup) :
RecyclerView.ViewHolder(inflater.inflate(R.layout.single_item, parent, false)) {
}
}
Then use the adapter like below in fragment:
val adapter = RecyclerViewAdapter(List)
recyclerview.adapter = adapter
Hope this helps
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 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()