How to reference view objects inside a list? - java

I'm trying to create a method to set a listener to each of my views inside a list like:
private fun setListeners() {
val clickableViews: List<View> =
listOf(box_one_text, box_two_text, box_three_text,
box_four_text, box_five_text)
for(item in clickableViews){
item.setOnClickListener{makeColored(it)}
}
}
box_one_text, box_two_text and so on are the id of the views inside my xml file and I'm trying to set a color of it when they are clicked like:
fun makeColored(view: View) {
when (view.id) {
// Boxes using Color class colors for background
R.id.box_one_text -> view.setBackgroundColor(Color.DKGRAY)
R.id.box_two_text -> view.setBackgroundColor(Color.GRAY)
// Boxes using Android color resources for background
R.id.box_three_text -> view.setBackgroundResource(android.R.color.holo_green_light)
R.id.box_four_text -> view.setBackgroundResource(android.R.color.holo_green_dark)
R.id.box_five_text -> view.setBackgroundResource(android.R.color.holo_green_light)
else -> view.setBackgroundColor(Color.LTGRAY)
}
}
the problems is that all of the elements inside the list are all red lines or can't reference by the list

First thing first, the list type is wrong. You said that box_one_text and box_two_text are the view id. View id type is Int, so you should change the list to list of Int
val clickableViews: List<Int> =
listOf(box_one_text, box_two_text, box_three_text,
box_four_text, box_five_text)
Then, to apply a click listener to each of the id, you need to find the view using findViewById
for(item in clickableViews){
findViewById<View>(item).setOnClickListener{makeColored(it)}
}
Or if you use view binding, you can follow below code:
val clickableViews: List<View> =
listOf(binding.boxOneText, binding.boxTwoText, binding.boxThreeText,
binding.boxFourText, binding.boxFiveText)
for(item in clickableViews){
item.setOnClickListener{makeColored(it)}
}

Try this:
private fun setListeners() {
val clickableViews: List<Int> =
listOf(R.id.box_one_text, R.id.box_two_text, R.id.box_three_text,
R.id.box_four_text, R.id.box_five_text)
for(item in clickableViews){
findViewById<View>(item).setOnClickListener{makeColored(it)}
}
}

Related

How do I use a custom layout for a carousel item in Android?

I am using this library to put a carousel view in an Android app: https://github.com/ImaginativeShohag/Why-Not-Image-Carousel
I'm also trying to use the showcase type, but a prerequisite to use this type is creating a custom layout for the carousel items.
Creating the layout I understand, but the OP uses this example in Kotlin to show how the custom layout is actually used:
binding.carousel3.carouselListener = object : CarouselListener {
override fun onCreateViewHolder(
layoutInflater: LayoutInflater,
parent: ViewGroup
): ViewBinding? {
return ItemCustomFixedSizeLayout1Binding.inflate(layoutInflater, parent, false)
}
override fun onBindViewHolder(
binding: ViewBinding,
item: CarouselItem,
position: Int
) {
val currentBinding = binding as ItemCustomFixedSizeLayout1Binding
currentBinding.imageView.apply {
scaleType = imageScaleType
// carousel_default_placeholder is the default placeholder comes with
// the library.
setImage(item, R.drawable.carousel_default_placeholder)
}
}
}
val listThree = mutableListOf<CarouselItem>()
for (item in DataSet.three) {
listThree.add(
CarouselItem(
imageUrl = item.first,
caption = item.second
)
)
}
binding.carousel3.setData(listThree)
binding.customCaption.isSelected = true
binding.carousel3.onScrollListener = object : CarouselOnScrollListener {
override fun onScrollStateChanged(
recyclerView: RecyclerView,
newState: Int,
position: Int,
carouselItem: CarouselItem?
) {
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
carouselItem?.apply {
binding.customCaption.text = caption
}
}
}
override fun onScrolled(
recyclerView: RecyclerView,
dx: Int,
dy: Int,
position: Int,
carouselItem: CarouselItem?
) {
// ...
}
}
// Custom navigation
binding.btnGotoPrevious.setOnClickListener {
binding.carousel3.previous()
}
binding.btnGotoNext.setOnClickListener {
binding.carousel3.next()
}
I'm having some trouble figuring out what exactly this code is doing and how it would look in Java. Any pointers would be greatly appreciated!
A Quick Guess
It seems that the listener is providing the callback of Recycler View. If you need me to guess within a second, and I will say the custom view is a Recycler View using listener to allow users to register the Recycler View methods (which is the Adapter using in the RV)
Deep Investigation
1st question: What is the Custom View class for id=carousel3 in KotlinActivity in the sample project
Ans: org.imaginativeworld.whynotimagecarousel.ImageCarousel.
(P.S. identical between activity_kotlin.xml and activity_test.xml)
(Below is a screen cap, don't try to click the links since it will not work :))
Let's got to search ImageCarousel and we will find ImageCarousel.kt. Let's find CarouselListener in there
We can see that when CarouselListener is set, it will immediately assign to adapter?.listener (Just ignore the "?" sign if you are not familiar with Kotlin)
2nd question: What is adapter here?
Ans from the same file:
private var adapter: FiniteCarouselAdapter? = null
3rd question: What is FiniteCarouselAdapter?
Ans: Its a RecyclerView.Adapter
open class FiniteCarouselAdapter(
...
) : RecyclerView.Adapter<FiniteCarouselAdapter.MyViewHolder>() {
Last question: How is it related to FiniteCarouselAdapter#listener/CarouselListener/adapter?.listener?
When the RecyclerView#Adapter requires to call the ViewHolder method, it will call to CarouselListener methods instead.
In FiniteCarouselAdapter:
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
...
// Init listeners
listener?.onBindViewHolder(
holder.binding,
item,
realItemPosition
)
As CarouselListener is an interface, the method implementation will be defined in the KotlinActivity instead.
In KotlinActivity:
The above code in your question : )

How to make a multiple recyclerViews which contains posts recyclerview and stories recyclerView similar to facebook app?

I'm trying to make something like below:
On that screenshot of facebook lite app:
At part marked "1": is a vertical recyclerview which contains posts.
At part marked "2": is a horizontal recyclerview which contains the stories.
At part marked "3": is the same recyclerview as at part marked "1" which contains posts.
I have already made the recyclerview for posts and It works well. Now I want to know how should I make the recyclerview for stories or friendship suggestion and make the two recyclerviews appear like on Facebook app ?
How could I have recyclerviews similar to the one in facebook app ?
Facebook show multiples recyclerViews One vertical where it shows the posts some others horizontal where it
shows stories or sometimes friendship suggestion.
Do you undestand me ?
Please tell me if I should explain more my issue.
Thanks.
you can define different View Holders for a Recycler List
the horizontal lists are just a view holder with another recycler inside them but with horizontal orientation in the main list. you can choose which view holder you want to use in OnCreatViewHolder
You should implement RecyclerView with multi-view type which gives you the various layout on a single RecyclerView adapter.
By doing this you need a model class which should have a field for define the different type.
Please have a look below sample code
Sample Model class
data class Sample(
var a: String = ""
...
var type: String = ""
)
Field type in model class above will define a unique view-type of adapter
Sample Adapter Class
class SampleAdapter(
private val context: Context,
private val items: ArrayList<Sample>
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
companion object {
private const val HEADER = 1
private const val STORY = 2
private const val FEED = 3
}
override fun getItemViewType(position: Int): Int {
return when(items[position].type) {
Constant.HEADER -> HEADER
Constant.STORY -> STORY
else -> FEED
}
}
override fun getItemCount(): Int = items.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when(viewType) {
HEADER -> HeaderViewHolder(LayoutInflater.from(context).inflate(R.layout_header, parent, false))
STORY -> StoryViewHolder(LayoutInflater.from(context).inflate(R.layout_story, parent, false))
else -> FeedViewHolder(LayoutInflater.from(context).inflate(R.layout_feed, parent, false))
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when(holder) {
is HeaderViewHolder -> holder.onBind()
is StoryViewHolder -> holder.onBind()
is FeedViewHolder -> holder.onBind()
}
}
inner class HeaderViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun onBind() {
....your business logic
}
}
inner class StoryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun onBind() {
....your business logic
}
}
inner class FeedViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun onBind() {
....your business logic
}
}
}
And when you are using the above adapter in Activity or Fragment. You should define the type of each item in ArrayList
For example: samples.add(Sample(...., type=Constant.Header))

Android kotlin - RecyclerView app crash after deleting items in specific order

This is the adapter:
class ContactsAdapter(val context: Context, private val users: MutableList<Contacts>, val itemClick: (Contacts) -> Unit) : RecyclerView.Adapter<ContactsAdapter.ViewHolder>(){
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.remove.setOnClickListener {
val builder = AlertDialog.Builder(context)
builder.setMessage(R.string.delete_contact)
builder.setPositiveButton(R.string.yes){_, _ ->
users.removeAt(position)
notifyItemRemoved(position)
}
builder.setNegativeButton(R.string.no){_,_ ->
}
val dialog: AlertDialog = builder.create()
dialog.show()
}
}
override fun getItemCount() = users.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.contacts, parent, false)
return ViewHolder(view, itemClick)
}
class ViewHolder(itemView: View?, val itemClick: (Contacts) -> Unit) : RecyclerView.ViewHolder(itemView!!){
val remove = itemView!!.removecontact!!
}
}
I got 2 items for testing, when I delete the second then first one it's fine, but when first then second one then the app crashes and the error is:
java.lang.IndexOutOfBoundsException: Index: 1, Size: 1
at java.util.ArrayList.remove(ArrayList.java:503)
at com.xxx.xxx.classes.ContactsAdapter$onBindViewHolder$2$1.onClick(ContactsAdapter.kt:57)
at com.android.internal.app.AlertController$ButtonHandler.handleMessage(AlertController.java:177)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6944)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)
What could be the problem?
Thanks in advance
As you can see, it is an IndexOutOfBoundsException, because you are attempting to access the index 1 in an array of size 1. This is mostly because you are directly using the position argument from onBindViewHolder from inside the AlertDialog's setPositiveButton call.
Instead use the holder.getAdapterPosition method to get the latest position. This should prevent the crash.
Edit #1: What I mean is to replace the position usages with holder.getAdapterPosition(). Your onBindViewHolder should look like this after the edits:
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.remove.setOnClickListener {
val builder = AlertDialog.Builder(context)
builder.setMessage(R.string.delete_contact)
builder.setPositiveButton(R.string.yes){_, _ ->
users.removeAt(holder.getAdapterPosition())
notifyItemRemoved(holder.getAdapterPosition())
}
builder.setNegativeButton(R.string.no){_,_ ->
}
val dialog: AlertDialog = builder.create()
dialog.show()
}
}
I got 2 items for testing, when I delete the second then first one it's fine, but when first then second one then the app crashes.
My guess is you do not refresh of the indezes of the list Items in your view
When you remove your fist item everything is fine you removed Index 0 from a list of 2
But your second crashes because you try to remove Index 1 (the second item) of a list that only has one element left.
your "notifyItemRemoved(position)" has to reassign the indezes of all the items that are left after removing one
Firstly, set the click listener outside the bindview holder as it is the bad practice. Just set it on onCreateViewHolder. Then, you will come to know that the listener gets called for the first time as soon as the adapter is set. For that you need to stop it from getting called by simply declaring a bool variable and assigning it to false in the adapter class like this.
private var islistenerCalledFirst: Boolean = false;
Then in the OnCreateViewHolder set the listener. Note, you can get position of the item by calling viewHolder.getAdapterPosition().
holder.remove.setOnClickListener {
if(islistenerCalledFirst){
... //your logic
}
islistenerCalledFirst = true
}
Hope this works for you.

Android dynamic list view to different screens?

List elements and tabbed-panel contents are populating from json file.
When I click A element in listview1 it has to go to tabbeddpanel(above).
When I click B element in listview1 it has to go to listview2 screen and when I click B1 element in listview2 it has to go to tabbedpanel(below).
Help me in doing this
Modify the data set passed to your listview1 so that you can add a tag here.
For example, the data set you're passing in your listview1 is an ArrayList of tis object.
public class myData {
private String content;
private int tag; // Add a tag attribute to handle them differently in the list.
}
// Here's the list passed to your `listview1`
private ArrayList<myData> myDataList = new ArrayList<myData>();
Now inside your bindView function check for the tag of the object of that position and handle the action differently like this.
if(myDataList.get(position).getTag() == 0) {
// Do this
} else {
// Do something else
}

Android Recyclerview - How to have a different column count per row, based on child size?

I need to develop a tag picker, like the one Foursquare uses for tastes, and the one Flipboard uses for "finding new topics."
I came across this library, Foursquare-CollectionPicker at github.
However, it uses a linear layout, which can reduce the performance for numerous child views when scrolling.
Hence, I need to use a recyclerview. Can anyone suggest how to replicate this with a recyclerview? My problem is that for each row in the recyclerview, the column count could be different, based on the size/number of the child views in each row (tags, in this case).
Thank you.
You could use FlexboxLayoutManager in a recycler view. All you need to do is create the layout manager as below. Don't forget to add your own recyclerAdapter to the recycler view as well of course.
FlexboxLayoutManager layoutManager = new FlexboxLayoutManager(context);
layoutManager.setFlexWrap(FlexWrap.WRAP);
recyclerView.setLayoutManager(layoutManager);
https://android-developers.googleblog.com/2017/02/build-flexible-layouts-with.html
https://blog.devcenter.co/unboxing-the-flexboxlayout-a7cfd125f023
If all you need is a recycler view that changes the number of columns (using a standard Google provided GridLayoutManager in RecyclerView), you don't need any custom code at all.
(pseudo code)
Prerequisites
You use a RecylerView with a GridLayoutManager (import androidx.recyclerview.widget.GridLayoutManager)
Your Adapter has a Type (so different viewTypes can inflate different ViewHolders).
You can initialize your grid Layout like:
private lateinit var layoutManager: GridLayoutManager
private val adapter = YourAdapter()
Activity#onCreate(...) {
super.onCreate(savedInstance)
setContentView(...)
layoutManager = GridLayoutManager(this, 2) //defaults to two columns
yourRecyclerView.layoutmanager = layoutManager //set it
yourRecyclerView.adapter = adapter
//Here goes the magic
}
What is the Magic?
Something like this:
layoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
return when (adapter.getItemViewType(position)) {
adapter.viewTypeOneColumn -> 1
adapter.viewTypeTwoColumns -> 2
else -> -1
}
}
}
This obviously assumes you have a "view type" (of any sort)
it can be as simple as:
class YourAdapter : ... {
internal val viewTypeOneColumn = 0
internal val viewTypeTwoColumns = 1
...
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when(viewType) {
viewTypeOneColumn -> ViewHolderForOneColumn(...)
viewTypeTwoColumns -> ViewHolderForTwoColumns(...)
else -> throw IllegalArgumentException("You must supply a valid type for this adapter")
}
}
override fun getItemViewType(position: Int): Int {
return getItem(position).someThingThatClassifiesThem // this obviously depend on what you use to define what each item is...
}
}
And that's all you really need.
I have once created a sample that does this for displaying "ads" inside a RecyclerView: You can check it here (it was updated a while ago but the code still works the same).
https://github.com/Gryzor/GridToShowAds
You will need to write your own layout manager, take a look at these blogs:
http://wiresareobsolete.com/2014/09/building-a-recyclerview-layoutmanager-part-1/
http://simpleandstupid.com/2015/05/01/recyclerview-and-its-custom-layoutmanager/

Categories