I tried to control recycler view item visibility when i click button but it does not work
I'm using databinding in xml
this is error message
java.lang.IndexOutOfBoundsException: Index: 5, Size: 4
In my code, recycler view item has a constraintLayout and button
And constraintLayout has recycler view
I want to show constraintLayout of item that has clicked button and hide other item's constraintLayout
The way that i tried to resolve this problem is to use previous position
When button is clicked, hide previous position's item and show current position's item
the code below is what i tried
this is clickListener in activity code
answerAdapter.onItemClickListener = object : QnaDetailAdapter.OnItemClickListener {
override fun onClick(
view: View,
position: Int,
holder: QnaDetailAdapter.AnswerHolder
) {
if (prePosition != -1)
binding.recyclerViewAnswer[prePosition].comment_holder.visibility = View.GONE
if (binding.recyclerViewAnswer[position].comment_holder.visibility == View.GONE) {
binding.recyclerViewAnswer[position].comment_holder.visibility = View.VISIBLE
prePosition = position
} else {
binding.recyclerViewAnswer[position-1].comment_holder.visibility = View.GONE
prePosition = -1
}
}
}
And this is adapter's onBindViewHodler
override fun onBindViewHolder(holder: AnswerHolder, position: Int) {
if (onItemClickListener != null) {
holder.btnComment.setOnClickListener { v ->
onItemClickListener?.onClick(v, position, holder)
}
}
holder.layout.recycler_view_comment.layoutManager = LinearLayoutManager(context)
holder.layout.recycler_view_comment.setHasFixedSize(true)
holder.layout.recycler_view_comment.adapter = adapter
val item = answerList[position]
holder.bind(item)
}
And this is AnswerHodler class
class AnswerHolder(private val binding: QnaDetailItemBinding) :
RecyclerView.ViewHolder(binding.root) {
val btnComment: Button = binding.btnComment
val layout: ConstraintLayout = binding.commentHolder
fun bind(item: Answer) {
binding.item = item
}
}
binding.comment is constraintLayout i touched on
thank for your help
Rather thaan usingposition, use holder.getAdapterPosition()
Related
I have RecyclerViewwith TextView items and want to replace the last item to ImageView. The numbers must be text, that's why I can't make all them ImageView
It mmust be like this:
I add TextView and ImageView to recycler row item and make the visibility of ImageView gone. Here is my ViewHolder
override fun onBindViewHolder(holder: TestView, position: Int) {
holder.testName?.text = listOfTests?.get(position)?.testName
holder.txtNumberIcon?.text = listOfTests?.get(position)?.txtNumberIcon
if (listOfTests?.get(listOfTests!!.size.minus(1))?.txtNumberIcon == dont know what condition must be here) {
holder.txtNumberIcon?.visibility = View.GONE
holder.imageStarIcon?.visibility = View.VISIBLE
Glide
.with(context?.applicationContext!!)
.load(listOfTests?.get(listOfTests!!.size.minus(1))?.testNumberIcon)
.into(holder.imageStarIcon!!)
}
What condition I must write to replace last item from textview to imageview?
Let me see if I understand it right.
If the listOfTests has 10 elements you want 9 cells from the RecyclerView to be TextView's and 1 ImageView.
If so:
override fun onBindViewHolder(holder: TestView, position: Int) {
holder.testName?.text = listOfTests?.get(position)?.testName
holder.txtNumberIcon?.text = listOfTests?.get(position)?.txtNumberIcon
if (position == listOfTests.size - 1) { /* position starts from 0 */
// I would suggest to load the image only for the element that will be displayed
Glide.with(context?.applicationContext!!)
.load(listOfTests?.get(position)?.testNumberIcon)
.into(holder.imageStarIcon!!)
holder.txtNumberIcon?.visibility = View.GONE
holder.imageStarIcon?.visibility = View.VISIBLE
}
else {
holder.txtNumberIcon?.visibility = View.VISIBLE
holder.imageStarIcon?.visibility = View.GONE
}
}
I have an issue, I want to have the recycler item row color alternatively. ie, If 1st row is in white color, 2nd row is in grey color and 3rd row is again white and 4th is again grey and so on. I tried one code, but it didn't work. Kindly help.
This is my code,
class ItemAdapter() : RecyclerView.Adapter<ItemAdapter.DateViewHolder>() {
private var ItemList: MutableList<Items>? = ArrayList()
private lateinit var ItemViewModel: ItemRowListBinding
private lateinit var listener: OnItemClickListener
init {
this.ItemList = arrayListOf()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DateViewHolder {
ItemViewModel = DataBindingUtil.inflate(
LayoutInflater.from(parent.context), R.layout.item_row_list,
parent, false
)
return DateViewHolder(ItemViewModel)
}
override fun onBindViewHolder(holder: DateViewHolder, position: Int) {
if(position % 2 == 0) {
holder.itemView.setBackgroundResource(R.color.White);
} else {
holder.itemView.setBackgroundResource(R.color.Grey);
}
holder.bindItemDetail(ItemList!![position])
}
override fun getItemCount(): Int {
return ItemList!!.size
}
fun setItemList(scanList: MutableList<Items>) {
this.ItemList = scanList
notifyDataSetChanged()
}
inner class DateViewHolder(private var itemRowBinding: ItemRowListBinding) :
RecyclerView.ViewHolder(itemRowBinding.root) {
fun bindItemDetail(ItemResponse: Items) {
if (itemRowBinding.ItemDetailViewModel == null) {
itemRowBinding.ItemDetailViewModel =
ItemDetailViewModel(
ItemResponse,
itemView.context
)
} else {
itemRowBinding.ItemDetailViewModel!!.setDetail(ItemResponse)
itemRowBinding.executePendingBindings()
}
itemRowBinding.root.Detail.setOnClickListener {
notifyDataSetChanged()
}
itemRowBinding.root.itemLookup.setOnClickListener {
Log.v(
"Clicked_ADAPTER",
"Clicked itemLookup adapter :: position -> $adapterPosition"
)
}
}
}
Any help would be deeply appreciated.
If you read setBackgroundDrawable's documentation, it expects a Drawable resource ID, or 0 to clear the background. A Color is not a Drawable. So you probably want to use this:
holder.itemView.setBackgroundColor(ContextCompat.getColor(context,R.color.white))
If you want to set the drawable you can use setBackgroundResource
Here you are trying to give color in setBackgroundResouce so it is not giving the result
You can set background color using setBackgroundColor
if(position % 2 == 0) {
holder.itemView.setBackgroundColor(ContextCompat.getColor(context,R.color.White));
} else {
holder.itemView.setBackgroundColor(ContextCompat.getColor(context,R.color.Grey));
}
Try this :
It worked for me.
if(position % 2 == 0){
holder.itemView.setBackgroundColor(#A6A2A2);
}else{
holder.itemview.setBackgroundColor(#FFFFFF);
}
How does one properly send data to child adapter in a fragment?
I'm basically trying to implement an Instagram like comments-section, e.g. a bunch of comments that can each have more comments (replies).
To do that, I use one main recyclerView + main adapter, which instances are retained in my fragment, and within the main adapter I bind the children comments (recyclerView + adapter).
Adding comments to the main adapter is easy since the object is always available in the fragment, so I just call mainAdapter.addComments(newComments):
MainAdapter
fun addComments(newComments: List<Comment>){
comments.addAll( 0, newComments) //loading comments or previous comments go to the beginning
notifyItemRangeInserted(0, newComments.size)
}
But how to call addComments of one particular nested-rV? I read I should not save the adapter instances and only use positions.
I'm trying to do that in my Fragment as follows:
val item = rVComments.findViewHolderForItemId(mAdapter.itemId)!!.itemView
val adapt = item.rVReplies.adapter as ChildCommentsAdapter
adapt.addComment(it.data.comment)
But that doesn't work very well: since we have only RecyclerViews, that particular ViewHolder is often already recycled if the user scrolled after posting or fetching items, which leads to a NullPointerException.
Hence the initial question: how does one properly interact with nested recyclerviews and their adapter? If the answer is via Interface, please provide an example as I've tried it without success since I shouldn't save adapter objects.
You can achieve that using a single multi-view type adapter by placing the comments
as part of the parent item, with that, you add the child items below the parent item and call notifyItemRangeInserted.
That way you don't have to deal with most of the recycling issues.
When you want to update a comment you just update the comment inside the parent item and call notifyItemChanged.
If you want I created a library that can generate that code for you in compile time.
It supports the exact case you wanted and much more.
Using #Gil Goldzweig's suggestion, here is what I did: in case of an Instagram like comments' system with replies, I did use a nested recyclerView system. It just makes it easier to add and remove items. However, as for the question How does one properly send data to child adapter in a fragment? You don't. It gets super messy. From my fragment, I sent the data to my mainAdapter, which in turn sent the data to the relevant childAdapter. The key to make it smooth is using notifyItemRangeInserted when adding a comment to the mainAdapter and then notifyItemChanged when adding replies to a comment. The second event will allow sending data to the child adapter using the payload. Here's the code in case other people are interested:
Fragment
class CommentsFragment : androidx.fragment.app.Fragment(), Injectable,
SendCommentButton.OnSendClickListener, CommentsAdapter.Listener {
#Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
private val viewModel by lazy {
ViewModelProviders.of(requireActivity(), viewModelFactory).get(CommentsViewModel::class.java)
}
private val searchViewModel by lazy {
ViewModelProviders.of(requireActivity(), viewModelFactory).get(SearchViewModel::class.java)
}
private val mAdapter = CommentsAdapter(this)
private var contentid: Int = 0 //store the contentid to process further posts or requests for more comments
private var isLoadingMoreComments: Boolean = false //used to check if we should fetch more comments
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_comments, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
//hide the action bar
activity?.findViewById<BottomNavigationView>(R.id.bottomNavView)?.visibility = View.GONE
contentid = arguments!!.getInt("contentid") //argument is mandatory, since comment is only available on content
ivBackArrow.setOnClickListener{ activity!!.onBackPressed() }
viewModel.initComments(contentid) //fetch comments
val layoutManager = LinearLayoutManager(this.context)
layoutManager.stackFromEnd = true
rVComments.layoutManager = layoutManager
mAdapter.setHasStableIds(true)
rVComments.adapter = mAdapter
setupObserver() //observe initial comments response
setupSendCommentButton()
post_comment_text.setSearchViewModel(searchViewModel)
setupScrollListener(layoutManager) //scroll listener to load more comments
iVCancelReplyTo.setOnClickListener{
//reset ReplyTo function
resetReplyLayout()
}
}
private fun loadMoreComments(){
viewModel.fetchMoreComments(contentid, mAdapter.itemCount)
setupObserver()
}
/*
1.check if not already loading
2.check scroll position 0
3.check total visible items != total recycle items
4.check itemcount to make sure we can still make request
*/
private fun setupScrollListener(layoutManager: LinearLayoutManager){
rVComments.addOnScrollListener(object: RecyclerView.OnScrollListener(){
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val visibleItemCount = rVComments.childCount
val totalItemCount = layoutManager.itemCount
val pos = layoutManager.findFirstCompletelyVisibleItemPosition()
if(!isLoadingMoreComments && pos==0 && visibleItemCount!=totalItemCount && mAdapter.itemCount%10==0){
//fetch more comments
isLoadingMoreComments = true
loadMoreComments()
}
}
})
}
private fun setupSendCommentButton() {
btnSendComment.setOnSendClickListener(this)
}
override fun onSendClickListener(v: View?) {
if(isInputValid(post_comment_text.text.toString())) {
val isReply = mAdapter.commentid!=null
viewModel.postComment(post_comment_text.text.toString(), mAdapter.commentid?: contentid, isReply) //get reply ID, otherwise contentID
observePost()
post_comment_text.setText("")
btnSendComment.setCurrentState(SendCommentButton.STATE_DONE)
}
}
override fun postCommentAsReply(username: String) {
//main adapter method to post a reply
val replyText = "${getString(R.string.replyingTo)} $username"
tVReplyTo.text = replyText
layoutReplyTo.visibility=View.VISIBLE
post_comment_text.requestFocus()
}
override fun fetchReplies(commentid: Int, commentsCount: Int) {
//main adapter method to fetch replies
if(!isLoadingMoreComments){ //load one series at a time
isLoadingMoreComments = true
viewModel.fetchReplies(commentid, commentsCount)
viewModel.replies.observe(this, Observer<Resource<List<Comment>>> {
if (it?.data != null) when (it.status) {
Resource.Status.LOADING -> {
//showProgressBar(true)
}
Resource.Status.ERROR -> {
//showProgressBar(false)
isLoadingMoreComments = false
}
Resource.Status.SUCCESS -> {
isLoadingMoreComments = false
mAdapter.addReplies(mAdapter.replyCommentPosition!!, it.data)
rVComments.scrollToPosition(mAdapter.replyCommentPosition!!)
}
}
})
}
}
private fun isInputValid(text: String): Boolean = text.isNotEmpty()
private fun observePost(){
viewModel.postComment.observe(this, Observer<Resource<PostCommentResponse>> {
if (it?.data != null) when (it.status) {
Resource.Status.LOADING -> {
//showProgressBar(true)
}
Resource.Status.ERROR -> {
//showProgressBar(false)
}
Resource.Status.SUCCESS -> {
if(it.data.asReply){
//dispatch comment to child adapter via main adapter
mAdapter.addReply(mAdapter.replyCommentPosition!!, it.data.comment)
rVComments.scrollToPosition(mAdapter.replyCommentPosition!!)
}else{
mAdapter.addComment(it.data.comment)
}
resetReplyLayout()
//showProgressBar(false)
}
}
})
}
private fun setupObserver(){
viewModel.comments.observe(this, Observer<Resource<List<Comment>>> {
if (it?.data != null) when (it.status) {
Resource.Status.LOADING -> {
//showProgressBar(true)
}
Resource.Status.ERROR -> {
isLoadingMoreComments = false
//showProgressBar(false)
}
Resource.Status.SUCCESS -> {
mAdapter.addComments(it.data)
isLoadingMoreComments = false
//showProgressBar(false)
}
}
})
}
private fun resetReplyLayout(){
layoutReplyTo.visibility=View.GONE
mAdapter.replyCommentPosition = null
mAdapter.commentid = null
}
override fun onStop() {
super.onStop()
activity?.findViewById<BottomNavigationView>(R.id.bottomNavView)?.visibility = View.VISIBLE
}
}
MainAdapter
class CommentsAdapter(private val listener: Listener) : RecyclerView.Adapter<CommentsAdapter.ViewHolder>(), ChildCommentsAdapter.ChildListener {
//method from child adapter
override fun postChildReply(replyid: Int, username: String, position: Int) {
commentid = replyid
replyCommentPosition = position
listener.postCommentAsReply(username)
}
interface Listener {
fun postCommentAsReply(username: String)
fun fetchReplies(commentid: Int, commentsCount: Int=0)
}
class ViewHolder(val view: View) : RecyclerView.ViewHolder(view)
private var comments = mutableListOf<Comment>()
private var repliesVisibility = mutableListOf<Boolean>() //used to store visibility state for replies
var replyCommentPosition: Int? = null //store the main comment's position
var commentid: Int? = null //used to indicate which comment is replied to
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_comment, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val comment = comments[position]
with(holder.view) {
//reset visibilities (rebinding purpose)
rVReplies.visibility = View.GONE
iVMoreReplies.visibility = View.GONE
tVReplies.visibility = View.GONE
content.loadUserPhoto(comment.avatarThumbnailURL)
text.setCaptionText(comment.username!!, comment.comment)
tvTimestamp.setTimeStamp(comment.timestamp!!)
val child = ChildCommentsAdapter(
//we pass parent commentid and position to child to be able to pass it again on click
this#CommentsAdapter, comments[holder.adapterPosition].id!!, holder.adapterPosition
)
val layoutManager = LinearLayoutManager(this.context)
rVReplies.layoutManager = layoutManager
rVReplies.adapter = child
//initial visibility block when binding the viewHolder
val txtMore = this.resources.getString(R.string.show_more_replies)
if(comment.repliesCount>0) {
tVReplies.visibility = View.VISIBLE
if (repliesVisibility[position]) {
//replies are to be shown directly
rVReplies.visibility = View.VISIBLE
child.addComments(comment.replies!!)
tVReplies.text = resources.getString(R.string.hide_replies)
if (comment.repliesCount > comment.replies!!.size) {
//show the load more replies arrow if we can fetch more replies
iVMoreReplies.visibility = View.VISIBLE
}
} else {
//replies all hidden
val txt = txtMore + " (${comment.repliesCount})"
tVReplies.text = txt
}
}
//second visibility block when toggling with the show more/hide textView
tVReplies.setOnClickListener{
//toggle child recyclerView visibility and change textView text
if(holder.view.rVReplies.visibility == View.GONE){
//show stuff
if(comment.replies!!.isEmpty()){
Timber.d(holder.adapterPosition.toString())
//fetch replies if none were fetched yet
replyCommentPosition = holder.adapterPosition
listener.fetchReplies(comments[holder.adapterPosition].id!!)
}else{
//load comments into adapter if not already
if(comment.replies!!.size>child.comments.size){child.addComments(comment.replies!!)}
}
repliesVisibility[position] = true
holder.view.rVReplies.visibility = View.VISIBLE
holder.view.tVReplies.text = holder.view.resources.getString(R.string.hide_replies)
if (comment.repliesCount > comment.replies!!.size && comment.replies!!.isNotEmpty()) {
//show the load more replies arrow if we can fetch more replies
iVMoreReplies.visibility = View.VISIBLE
}
}else{
//hide replies and change text
repliesVisibility[position] = false
holder.view.rVReplies.visibility = View.GONE
holder.view.iVMoreReplies.visibility = View.GONE
val txt = txtMore + " (${comment.repliesCount})"
holder.view.tVReplies.text = txt
}
}
tvReply.setOnClickListener{
replyCommentPosition = holder.adapterPosition
commentid = comments[holder.adapterPosition].id!!
listener.postCommentAsReply(comments[holder.adapterPosition].username!!)
}
iVMoreReplies.setOnClickListener{
replyCommentPosition = holder.adapterPosition
listener.fetchReplies(comments[holder.adapterPosition].id!!, layoutManager.itemCount) //pass amount of replies too
}
}
}
#Suppress("UNCHECKED_CAST")
override fun onBindViewHolder(holder: ViewHolder, position: Int, payloads: MutableList<Any>) {
if(payloads.isNotEmpty()){
//add reply to child adapter
with(holder.view){
Timber.d(payloads.toString())
val adapter = rVReplies.adapter as ChildCommentsAdapter
if(payloads[0] is Comment){
adapter.addComment(payloads[0] as Comment)
}else{
//will be of type List<Comment>
adapter.addComments(payloads[0] as List<Comment>)
val comment = comments[position]
if (comment.repliesCount > comment.replies!!.size) {
//show the load more replies arrow if we can fetch more replies
iVMoreReplies.visibility = View.VISIBLE
}else{
iVMoreReplies.visibility = View.GONE
}
}
}
}else{
super.onBindViewHolder(holder,position, payloads) //delegate to normal binding process
}
}
override fun getItemCount(): Int = comments.size
//add multiple replies to child adapter at pos 0
fun addReplies(position: Int, newComments: List<Comment>){
comments[position].replies!!.addAll(0, newComments)
notifyItemChanged(position, newComments)
}
//add a single reply to child adapter at last position
fun addReply(position: Int, newComment: Comment){
comments[position].replies!!.add(newComment)
comments[position].repliesCount += 1 //update replies count in case viewHolder gets rebinded
notifyItemChanged(position, newComment)
}
//add a new comment to main adapter at last position
fun addComment(comment: Comment){
comments.add(comment) //new comment just made goes to the end
repliesVisibility.add(false)
notifyItemInserted(itemCount-1)
}
//add multiple new comments to main adapter at pos 0
fun addComments(newComments: List<Comment>){
comments.addAll( 0, newComments) //loading comments or previous comments go to the beginning
repliesVisibility.addAll(0, List(newComments.size) { false })
notifyItemRangeInserted(0, newComments.size)
}
}
The childAdapter is very basic and has nearly 0 logic.
I didn't add any data to my RecyclerView but it shows a empty box (the one I styled in the layouts for my data) anyways. It crashes with this errormessage
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
Here is my customAdapter:
class CustomAdapterExercise(var exerciseList: ArrayList<Exercise>, val addList: ArrayList<textAdd>) : RecyclerView.Adapter<CustomAdapterExercise.ViewHolder>() {
val typeAdd = 0
val typeExercise = 1
override fun getItemViewType(position: Int): Int {
if (position == exerciseList.size + 1) {
return typeAdd
}
else{
return typeExercise
}
}
//this method is returning the view for each item in the list
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomAdapterExercise.ViewHolder {
if (viewType == typeExercise) {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.exercise_layout, parent, false)
return ViewHolder(itemView)
} else {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.add_layout, parent, false)
return ViewHolder(itemView)
}
}
//this method is binding the data on the list
override fun onBindViewHolder(holder: CustomAdapterExercise.ViewHolder, position: Int) {
if (holder.itemViewType == typeAdd) {
holder.bindAdd(addList[0])
}
else{
if(position != exerciseList.size){
holder.bindItems(exerciseList[position])
}
}
}
//this method is giving the size of the list
override fun getItemCount(): Int {
return exerciseList.size + 2
}
//the class is hodling the list view
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bindItems(Exercise: Exercise) {
var exerciseAmount = itemView.findViewById<TextView>(R.id.exerciseAmount)
if(exerciseAmount != null){
exerciseAmount.text = Exercise.exAmount
}
}
fun bindAdd(textAdd: textAdd){
val addText = itemView.findViewById<TextView>(R.id.addText)
if(addText != null){
addText.text = textAdd.textAdd
}
}
}
}
Even if I add some data it still produces a empty box there and I don't get why.
I wonder how can I stop it from producing a empty box always?
These are issues with calculating the index in RecyclerView:
In getItemCount it should be + 1, instead of + 2, as it only needs to add one additional item for add button.
In getItemViewType position at the end of the list if list length, rather than list lenght +1. This is because position is 0-indexed. So, for example, if you have 5 items, positions 0-4 will be your exercise items, and then position 5 (position == exerciseList.size) will be an add item.
Adding logs in getItemViewType for position and generated view type is helpful for debugging, as it shows which positions are calculated incorrectly very quickly.
I have RecyclerView inside another RecyclerView. When item of inner RecyclerView is clicked, I'm showing PopupWindow like below.
When user would click button inside popup, item of outer RecyclerView should update data. The problem is that, after update outer item notifyItemChanged(position), PopupWindow displays in left, top corner of fragment, instead of below item of inner RecyclerView.
override fun onBindViewHolder(holder : MessageItemHolder, messagePosition : Int)
{
val item = messages[messagePosition]
//Display some data
if(item.isRateViewOpened)
showRatePopup(holder, messagePosition, item)
holder.tvMessage.setOnLongClickListener {
showRatePopup(holder, messagePosition, item)
true
}
}
private fun showRatePopup(holder : MessageItemHolder, messagePosition : Int, message : Message)
{
messages[messagePosition].isRateViewOpened = true
val rateView = layoutInflater.inflate(R.layout.popup_rate, null)
setRateIcons(message.status, rateView)
rateView.ibLike.setOnClickListener { displayRateStatus(RateStatus.LIKE, message, messagePosition, rateView) }
rateView.ibDislike.setOnClickListener { displayRateStatus(RateStatus.DISLIKE, message, messagePosition, rateView) }
val popupWindow = PopupWindow(rateView, FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT, true)
popupWindow.setBackgroundDrawable(ColorDrawable())
popupWindow.isOutsideTouchable = true
val location = IntArray(2)
holder.tvMessage.getLocationOnScreen(location)
popupWindow.showAsDropDown(holder.tvMessage, 0, 0, Gravity.NO_GRAVITY)
popupWindow.setOnDismissListener {
messages[messagePosition].isRateViewOpened = false
}
}