Recyclerview view not populating - android

I'm a Kotlin newbie learning how to create simple recyclerview apps. My code is supposed to list the integers 1..10 in vertically stacked cells. However, it only lists the first item. I've consulted several tutorials and reviewed my code several times(after long breaks), but I can't see anything wrong in my code.
I got the bright idea early today to print Log statements. Examining them, I note that my onBindViewHolder function is only called once. What blunder am I making?
Here is my log output:
D/QuoteAdapter: value is: 1
D/QuoteAdapter: index is: 0
D/QuoteAdapter: Size is: 10
my activity:
class MainActivity : AppCompatActivity() {
lateinit var mRecyclerView: RecyclerView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mRecyclerView = findViewById(R.id.recyclerView)
mRecyclerView.layoutManager = LinearLayoutManager(this)
mRecyclerView.adapter = QuoteAdapter()
//mRecyclerView.setHasFixedSize(true)
}
}
my adapter:
class QuoteAdapter : RecyclerView.Adapter<QuoteViewHolder>() {
private val listOfInts = intArrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
private val TAG = "QuoteAdapter"
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QuoteViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.recyclerview_item_row, parent, false)
return QuoteViewHolder(view)
}
override fun getItemCount(): Int {
Log.d(TAG, "Size is: ${listOfInts.size.toString()}")
return listOfInts.size
}
override fun onBindViewHolder(holder: QuoteViewHolder, position: Int) {
val item = listOfInts[position]
Log.d(TAG, "value is: ${item.toString()}")
Log.d(TAG, "index is: ${position.toString()}")
holder.listTitle.text = item.toString()
}
}
my viewholder:
class QuoteViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val listTitle = itemView.findViewById(R.id.itemString) as TextView
}
my layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/itemString"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
my main layout is shown below:
<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">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

In your "my layout" try this:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/itemString"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
Notice layout_height of LinearLayout has changed to wrap_content
Also doubt you need the android:orientation="vertical" on your ViewHolders item xml unless you will add more than just 1 TextView in the future.

Like Zain says, you can just use a TextView on its own in a layout file, which will also fix the problem (so long as its height is wrap_content!)
There are actually a few included with Android - type android.R.layout. and you'll see a few things, like simple_list_item_1 which is just a styled TextView (you can ctrl+click the reference or whatever to look at the file). Can be nice if you just want to make a quick thing!
The ID of the TextView in android.R.layout.simple_list_item_1 is #android:id/text1 - note the android prefix, because its part of the android resources, not your app's. Which means you have to reference the ID in the same way as the layout, with android at the front: android.R.id.text1

You can get rid of the LinearLayout in the list item layout, and only keep the TextView.
So, replace your list item layout with:
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/itemString"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

Related

Call to Initialise and Display Fragment from Adapter Class from Buttons/Menu Generated at Runtime

I'm trying to write a simple runtime user interface that's generated from the contents of files read from the SD card. Am using a RecyclerView to generate buttons, the user can then make a selection (via the button) before moving onto the next menu on the next fragment. This program combines, RecyclerViews, Viewbinding, Fragments and Filehandling (the filehandling being the least important).
For example (I can't get a photo of the first fragment running unfortunately):
would be generated from this text file:
Priory Hotel, Victoria Street, Bristol
Landsdown Hotel, Landsdown, Bristol
The Hilton, Walcot Street, Bristol
MerryFields, Milsom Street, Bristol
Loz's Kitchen, St Jame's Square, Bristol
The top button would contain the text Priory Hotel, Victoria Street, Bristol the second button Landsdown Hotel, Landsdown, Bristol the 3rd The Hilton, Walcot Street, Bristol and so forth, the number of buttons displayed being the same as the number of lines (number of clients) of text.
I'm hoping to generate the 'button/menu interface' at runtime. The next fragment would be another list of buttons, also generated from a different text file (in the same way) and so on and so forth.
Is doing this even a good idea ? Should I be using Fragments ?
Am using Android Studio and developing for Android 5.1 (Lollipop I think). The error comes when I call the onClickListener in the Adapter Class. I just can't get this to work and have tried the following Stack Overflow Answers:
This doesn't work and isn't kotlin,
nor this (again kotlin but I can convert). Can't call supportFragmentManager
This is unanswered
I have read this, watched this and read these SO posts: 1, 2, 3, 4. 5. And finally this for good measure.
Adapter Class.kt
// I've not included the imports for brevity
class MyAdapter(private val aList : ArrayList<String>, private val destination: Int) : RecyclerView.Adapter<MyAdapter.MyViewHolder>(){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.list_item,
parent, false)
return MyViewHolder(itemView)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
Log.d("Loz", "start onBindViewHolder()")
val currentItem = aList[position]
holder.buttonInRecyclerV.text = currentItem
val context = holder.buttonInRecyclerV.context
Log.d("Loz", "onBindViewHolder() before setOnClickLIstener")
// **************************************************************
// According to the error message provided by Logcat the crash is
// this line below. The 2 log messages in this function are being
// outputted too.
// **************************************************************
holder.buttonInRecyclerV.setOnClickListener {v ->
v.findNavController().navigate(destination)
// The toast message gets printed if I get rid of all the navigation
// and just run 1 fragment
Toast.makeText(context,
"${aList[position]} button pressed", Toast.LENGTH_LONG).show()
}
}
class MyViewHolder(itemView: View) :RecyclerView.ViewHolder(itemView)
{
private val binding = ListItemBinding.bind(itemView)
val buttonInRecyclerV : Button = binding.clientsNameButton
}
}
MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var clientArrayList: ArrayList<String>
private lateinit var productArrayList: ArrayList<String>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
Log.d("Loz", "before dataInit()")
dataInitialise(view)
Log.d("Loz", "after dataInit()")
replaceFragment(savedInstanceState, FirstFragment(), clientArrayList, "clientsListBundle")
Log.d("Loz", "after 1st frag Init()")
// The following may well be a load of twoddle, I was desparate when I wrote the
// code below. However if I just have:
// replaceFragment(savedInstanceState, SecondFragment(), productArrayList, "productListBundle)
// uncommented and comment out the first call of replaceFragment() the second fragment
// is created no problem...
if (FirstFragment().lifecycle.currentState == Lifecycle.State.RESUMED) {
replaceFragment(
savedInstanceState,
SecondFragment(),
productArrayList,
"productListBundle"
)
}
Log.d("Loz", "after 2nd frag Init()")
}
// This function (again) seems to work without the navigation elements
// in the app, Logs outputted.
private fun replaceFragment(savedInstanceState: Bundle?, aFragment: Fragment, aList: ArrayList<String>, TOKEN: String){
if (savedInstanceState == null) {
val fragmentManager = supportFragmentManager
val bundle = Bundle()
Log.d("Loz", "after replaceFragment()")
bundle.putStringArrayList(TOKEN, aList)
aFragment.arguments = bundle
fragmentManager.commit {
setReorderingAllowed(true)
replace(R.id.frameLayout, aFragment)
}
}
}
private fun listFromFile(view: View, filename :String) : MutableList<String> {
// Had no problems with this, and it works as expected when there's no navigation
// component to this app.
}
private fun dataInitialise(view: View) {
clientArrayList = listFromFile(view, "NEades/Client List.txt") as ArrayList<String>
productArrayList = listFromFile(view, "NEades/Product List.txt") as ArrayList<String>
}
}
activity_main.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">
<?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">
<!-- If I put this in neither fragment works, if I take it out, both -->
<!-- do independently -->
<!-- <androidx.fragment.app.FragmentContainerView-->
<!-- android:id="#+id/fragment_container_view"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="match_parent"-->
<!-- app:navGraph="#navigation/nav_graph">-->
<FrameLayout
android:id="#+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="#id/frameLayout"
app:layout_constraintBottom_toTopOf="#id/frameLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
</FrameLayout>
</androidx.fragment.app.FragmentContainerView>
<!-- </androidx.fragment.app.FragmentContainerView-->
</androidx.constraintlayout.widget.ConstraintLayout>
<FrameLayout
android:id="#+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="#id/frameLayout"
app:layout_constraintBottom_toTopOf="#id/frameLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
</FrameLayout>
</androidx.fragment.app.FragmentContainerView>
</androidx.constraintlayout.widget.ConstraintLayout>
first_fragment.xml
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:overScrollMode="always"
android:id="#+id/FirstFragment">
<FrameLayout
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="0dp"
tools:context=".FirstFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:listitem="#layout/list_item"/>
</FrameLayout>
</androidx.core.widget.NestedScrollView>
list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:paddingTop="1dp"
android:layout_marginHorizontal="2dp"
android:background="#color/white">
<androidx.appcompat.widget.AppCompatButton
android:id="#+id/clientsNameButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textColor="#color/black"
android:text="#string/default_value"
android:textSize="11sp"
android:textStyle="bold"
android:layout_marginStart="16dp"
android:layout_marginEnd="32dp"
android:layout_marginTop="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_margin="8dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="parent"
android:background="#color/underline"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Eventually, if this is possible, the goal is to refactor even more.
My question: (other than should I be using activities other than fragments ?) is how to 'call' the second fragment from any of the buttons in the first fragment (from the Adapter class ?) so that the second fragment then initialises and displays it's contents (retrieved from a text file)

Heterogeneous Views with unknow EditText number

I'm working on a dynamicViews (not sure if that's the right word for creating a view from a json file).
I'm getting the schema from a JSON file, I've stepped up the recycleView and its adapter, so far so good, each Recycleview item (must or not) contain a number of EditText whose number is unknown in advance, so based on the Json file, I have to inflate inside.
I searched a lot but the similar solution I found for Heterogene Recycleview: the idea was to use separate layout and inflate each of them according to your needs inside onCreateViewHolder but the developer who published the solution knew in advance what is the combination of all possible views and he just switch.
class Adapter_base_Display(private val listener: Display_Fragment,
activity: FragmentActivity ,
liste_display : ArrayList<DisplaySections>)
: RecyclerView.Adapter<Base_DisplayViewHolder>() {
private val activityIns = activity
private val liste_display_Recycle_adapter = liste_display
interface Base_DisplayListener {
fun onClickeddisplay(position: Int)
}
private val items = ArrayList<DisplaySections>()
fun setItems(items: ArrayList<DisplaySections>) {
this.items.clear()
this.items.addAll(items)
notifyDataSetChanged()
}
fun clear() {
val size: Int = items.size
items.clear()
notifyItemRangeRemoved(0, size)
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Base_DisplayViewHolder {
val binding: ItemDisplayBinding =
ItemDisplayBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return Base_DisplayViewHolder(
binding,
listener as Base_DisplayListener,
activityIns,
parent,
liste_display_Recycle_adapter)
}
override fun getItemCount(): Int = items.size
override fun onBindViewHolder(holder: Base_DisplayViewHolder, position: Int) =
holder.bind(items[position])
}
class Base_DisplayViewHolder(
private val itemBinding: ItemDisplayBinding,
private val listener: Adapter_base_Display.Base_DisplayListener,
private val activityIns: FragmentActivity,
private var parent: ViewGroup,
private val items: ArrayList<DisplaySections>,
) : RecyclerView.ViewHolder(itemBinding.root),
View.OnClickListener {
init {
itemBinding.root.setOnClickListener(this)
}
fun bind(item: DisplaySections) {
itemBinding.textView2.text = item.name
}
override fun onClick(v: View?) {
listener.onClickeddisplay(adapterPosition)
}
}
The EditText I want to inflate multiple time
<EditText
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/edittext_isplay"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="15dp"
android:text="TextView"
android:maxLines="3"
android:textColor="#color/black"
android:textSize="18sp" />
data class DisplaySections(
val id : Int,
val name : String,
val createdAt : String,
val updatedAt : String,
val displayTypeId : Int,
val displayCustomFields : List<DisplayCustomFields> // Contains the elements that will be displayed as EditText
The Base layout-Recycleview Item which is common for all scenarios
<LinearLayout
android:id="#+id/parent_edittext" // ALl EditText container
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="#+id/textView2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView" />
</LinearLayout>
<LinearLayout
android:id="#+id/camera_linear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="vertical"
android:visibility="visible">
<ImageView
android:id="#+id/addphoto"
android:layout_width="316dp"
android:layout_height="250dp"
android:layout_gravity="center"
android:layout_marginTop="#dimen/_20sdp"
android:src="#drawable/ajouter_photo"
app:tint="#color/clear_grey" />
<TextView
android:id="#+id/camera_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Ajouter des photos"
android:textStyle="bold" />
</LinearLayout>
<LinearLayout
android:id="#+id/plus_image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/_20sdp"
android:gravity="center"
android:orientation="vertical"
android:visibility="visible">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/my_photo_recycle"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
I going to post the solution I found.
To solve this problem, we absolutely must use a nested RecyclerView;
As I said above, I want to display a list of items with the same common layout (Imageview + Textview), but the tricky part here is the dynamic part of each item.
For recap:
Each element (may or may not) contain N-1 (EditText), it depends on what it gets from a json file.
if you want to solve this problem by creating multiple Viewholder and switch depending on which "ViewHolderType" you are wrong !, you will just create an infinite layout files, it doesn't make sense.
if you create more than one (EditText) and only change the visibility it may work, but if you get for example 100 EditText from the Json file you are not going to manually create 100 Edittext.
if you want to programmatically generate an EditText, you will affect every item in your view Recycle since you cannot create view inside OnbindViewHolder function.
the only way I found to solve this problem is to create a parent-child RecycleView whenever there is an (EditText) you send it to the child adapter and you keep your parent element safe in the parent adapter.
You can also put a condition (NULL tester) inside the Parent-OnbindViewholder whenever there is no data, you just don't call Child-adapter.
I hope this solution will be useful to anyone who has had this problem, and if you have another solution I will be very happy to test them.

Error in RecyclerViewAdapter's onBindViewHolder() Method ㅠㅠ [duplicate]

Why do I get a NullPointerException in my ViewHolder's bindItems() method?
I've highlighted the line where I get the NullPointerException. The blogpost_author ID exists, as you can see in the XML, so what's the problem here? How is findViewById<TextView>(R.id.blogpost_author) returning null?
Adapter and ViewHolder code:
class BlogPostAdapter(val blogList: ArrayList<BlogPost>) : RecyclerView.Adapter<BlogPostAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) : BlogPostAdapter.ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.blog_post_list, parent, false)
return ViewHolder(v)
}
override fun getItemCount(): Int {
return blogList.size
}
override fun onBindViewHolder(holder: BlogPostAdapter.ViewHolder, position: Int) {
holder.bindItems(blogList[position])
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bindItems(blogPost: BlogPost) {
val blogPostAuthor = itemView.findViewById<TextView>(R.id.blogpost_author) // THIS LINE - NULL POINTER EXCEPTION
val blogPostTitle = itemView.findViewById<TextView>(R.id.blogpost_title)
blogPostAuthor.text = blogPost.author
blogPostTitle.text = blogPost.title
}
}
}
Activity code:
class BlogPostListActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.blog_post_list)
// Get the RecyclerView from XML itself
val recyclerView = findViewById<RecyclerView>(R.id.recyclerview)
// Add a layout manager - What does a layout manager do?
recyclerView.layoutManager = LinearLayoutManager(this, LinearLayout.VERTICAL, false)
// Create an array list to store blogposts using the the data class blogPost
val blogPosts = ArrayList<BlogPost>()
// Add some dummy data to the list
blogPosts.add(BlogPost(123, "First Blog Post", "John"))
blogPosts.add(BlogPost(456, "Second Blog Post", "Bob"))
blogPosts.add(BlogPost(789, "Third Blog Post", "Mary"))
// Create an adapter
val adapter = BlogPostAdapter(blogPosts)
// Add the adapter to the recyclerview
recyclerView.adapter = adapter
}
}
Kotlin data class:
data class BlogPost(val id: Int, val title: String, val author: String)
XML for RecyclerView:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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="com.topzap.android.kotlinlistapptest.BlogPostListActivity">
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:layout_editor_absoluteX="8dp"
tools:layout_editor_absoluteY="8dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</android.support.constraint.ConstraintLayout>
XML for CardView layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/blogpost_author"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp"
android:text="AuthorPlaceHolder"
android:textAppearance="#style/Base.TextAppearance.AppCompat.Large"
/>
<TextView
android:id="#+id/blogpost_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp"
android:text="TitlePlaceHolder"
android:textAppearance="#style/Base.TextAppearance.AppCompat.Medium"
/>
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
You may be inflating the wrong layout within your RecyclerView.
This line within your onCreateViewHolder method:
val v = LayoutInflater.from(parent.context).inflate(R.layout.blog_post_list, parent, false)
You are inflating the blog_post_list.xml, which I'm assuming is the wrong layout file due to the fact you're also inflating that layout within your BlogPostListActivity here:
setContentView(R.layout.blog_post_list)
So when this line is called:
val blogPostAuthor = itemView.findViewById<TextView>(R.id.blogpost_author)
It is looking for the id 'blogpost_author' within R.layout.blog_post_list and as you can see there is no blogpost_author TextView within that layout so it returns null.
To sort it out, it should be straight forward and just change the layout resource that you're assigning to each ViewHolder within your onCreateViewHolder method with the correct layout for your CardView layout.
Which means the line should read something like:
val v = LayoutInflater.from(parent.context).inflate(R.layout.your_card_layout, parent, false)

RecyclerView wont display image but displays text

I am making a RecyclerView with Kotlin that will display an image and text. So far the text should be just "Textview" and the image should be one of the images that come with android. The text displays "Textview" the specified number of times but doesn't show the image at all.
I am actually following [this video](https://www.youtube.com/watch?v=jS0buQyfJfs&list=PL0dzCUj1L5JGfHj1lwxOq67zAJV3e1S9S&index=2&t=409s! exactly and run into the problem.
I have changed the cell layout of the image to wrap content and it did not help.
Adapter
class MainAdapter : RecyclerView.Adapter<CustomViewHolder>() {
val vidTitles = listOf("Monday", "Tuesday", "Wednesday", "Thursday", "Friday")
override fun getItemCount(): Int {
return vidTitles.size
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomViewHolder {
//create the view for the rows
val layoutInflater = LayoutInflater.from(parent.context)
val cellForRow = layoutInflater.inflate(R.layout.video_row, parent, false)
return CustomViewHolder(cellForRow)
}
override fun onBindViewHolder(holder: CustomViewHolder, position: Int) {
holder?.view?.videoTitleTextView?.text= vidTitles[position]
}
}
class CustomViewHolder(val view: View): RecyclerView.ViewHolder(view) {
}
RecyclerView's Cell layout
<?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="wrap_content">
<ImageView
android:id="#+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#color/colorAccent"
android:contentDescription="avatar image"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:srcCompat="#mipmap/ic_launcher" />
<TextView
android:id="#+id/videoTitleTextView"
android:layout_width="0dp"
android:layout_height="31dp"
android:text="animals"
android:textSize="20sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/imageView" />
</androidx.constraintlayout.widget.ConstraintLayout>
Main Activity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//recyclerView_main.setBackgroundColor(Color.CYAN)
recyclerView_main.layoutManager = LinearLayoutManager(this)
recyclerView_main.adapter = MainAdapter()
}
}
I expect it to show both the image and the text; It shows the text but not any image and it doesn't even leave any space for the image.
Change Your Image View as below.
<ImageView
android:id="#+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#color/colorAccent"
android:contentDescription="avatar image"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:src="#mipmap/ic_launcher" />
You are using tools:srcCompat which is wrong.
Also Change height of Constraint Layout to wrap_content. Otherwise each item in a Recyclerview is going to take full height of screen.
Tools namespace is used for Compile time behaviour. When you build the app this tools features is removed. Its helps you to preview your view at compile time.
Replace this
tools:srcCompat="#mipmap/ic_launcher"
with this:
android:src="#mipmap/ic_launcher"
Note: Here tools:srcCompat will show only in the preview of Android studio but not in the real device.
For more info check here
When I read your code right, you should see "animals" in your TextViews only :)
This is because in your cell's layout file you set the android:text and not the tools:text attribute.
Just implement your ViewHolder and bind the Views to your model.
If you want only to verify your Views are loaded, you could replace the
tools:srcCompat="#mipmap/ic_launcher"
to
app:srcCompat="#mipmap/ic_launcher"

How to show part of next/previous card RecyclerView

What is the best strategy to achieve this feature:
I Have a horizontal RecyclerView with cards.
Each card will fulfil the entire screen, but I want it to show part of the next card and previous one if it has more than one item.
I know I can achieve this by setting my card android:layout_width at the adapter to have a specific DP like 250dp instead of match_parent.
But it doesn't look like a proper solution.
This is my code:
Activity with RecyclerView:
class ListPokemon : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val items = createListPokemons()
recyclerView.adapter = PokemonAdapter(items)
recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
recyclerView.setHasFixedSize(true)
val pagerSnapHelper = PagerSnapHelper()
pagerSnapHelper.attachToRecyclerView(recyclerView)
}
private fun createListPokemons(): List<Pokemon> {
val pokemons = ArrayList<Pokemon>()
pokemons += createPokemon("Pikachu")
pokemons += createPokemon("Bulbasaur")
pokemons += createPokemon("Charmander")
pokemons += createPokemon("Squirtle")
return pokemons
}
private fun createPokemon(name: String) = Pokemon(name = name, height = 1, weight = 69, id = 1)
}
Layout of Activity:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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">
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layoutManager="android.support.v7.widget.LinearLayoutManager"/>
</android.support.constraint.ConstraintLayout>
Adapter:
class PokemonAdapter(val list: List<Pokemon>) : RecyclerView.Adapter<PokemonAdapter.PokemonVH>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PokemonAdapter.PokemonVH {
return PokemonVH(LayoutInflater.from(parent.context)
.inflate(R.layout.pokemon_item, parent, false))
}
override fun onBindViewHolder(holder: PokemonAdapter.PokemonVH, position: Int) {
holder.textViewName.text = list[position].name
}
override fun getItemCount(): Int {
return list.size
}
class PokemonVH(itemView: View) : RecyclerView.ViewHolder(itemView) {
var textViewName: TextView = itemView.findViewById(R.id.textViewName)
}
}
Layout of Adapter:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
android:layout_gravity="center_horizontal"
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="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
app:cardCornerRadius="8dp"
app:cardElevation="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:padding="36dp"
android:id="#+id/textViewName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="22sp"
tools:text="Teste String"/>
</LinearLayout>
</android.support.v7.widget.CardView>
This is my result:
I would like to show part of the next card at this situation. How can I do this?
Thanks.
What you need to do is set padding to your RecyclerView, set clipToPadding to false, use a SnapHelper with it, and you need to make sure the margins on your cards are less than or equal to the padding in the RecylerView.
So, let's say you want the distance from the cards to the sides of the screen to be 16dp and you want the distance between the cards to be 8dp. You'll have to set the margins on each card to 4dp, so the total margin is 8dp. And you have to set the padding to 12dp, given there's already a margin of 4dp on each side of the card.
It'll look a bit like this:
Your list:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView
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"
app:layoutManager="android.support.v7.widget.LinearLayoutManager"
android:clipToPadding="false"
android:orientation="horizontal"
android:paddingStart="12dp"
android:paddingEnd="12dp"/>
Your cards:
<?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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="4dp"
android:layout_marginStart="4dp"
app:cardElevation="2dp"/>
I think the padding solution is not a good for all cases, because forces the last item to have padding to the right.
Personally i use runtime width calculation of each item and i am very satisfied with this. So you can do the following:
onBindViewHolder
if (position == data.size - 1) {
holder.itemView.layoutParams = RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT)
} else {
if (width == null) {
holder.itemView.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
holder.itemView.viewTreeObserver.removeOnGlobalLayoutListener(this)
width = holder.itemView.width
params.width = width!! - partOfPage
holder.itemView.requestLayout()
}
})
} else {
params.width = width!! - partOfPage
holder.itemView.requestLayout()
}
}
The outcome is that all middle items are rendered showing a part of the next page, but the last one is rendered full width.
Change your CardView width from "match_parent" to "0dp". And add, layout_weight as "80" (or similar). Make your parent view (RecyclerView) layout_weightSum as "100".
android:layout_width="0dp"
android:layout_weight="80"

Categories

Resources