0

I am trying to build a Slack-like chat app following a tutorial in a course I am taking online.

In the tutorial the instructor is using a ListView and the OnItemClickListener method, but I am trying to do it with recycler view, and I am stuck with the onClickListener in the adapter.

I have tried to find answers in other questions but couldn't find one that solves my problem. The closest ones were this and this

My two problems are:
1. The app's main activity has on the top of the screen a title that states what channel is currently active. I have created a singleton that holds the "current channel" and the title's text is being pulled from that singleton. I am having a hard time changing the value of that singleton on click.

  1. The main activity also has all the channels in a list view in a drawer. I am trying to close the drawer when a channel is clicked but that isn't happening either.

This is my current adapter:

class ChannelsAdapter(val context: Context, val channels: ArrayList<Channel>) :
    RecyclerView.Adapter<ChannelsAdapter.Holder>() {


    inner class Holder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val singleChannel = itemView.findViewById<TextView>(R.id.single_channel)

        val mainLayout = LayoutInflater.from(context).inflate(R.layout.activity_main, null)

        fun bindText(textVar: String, context: Context) {
            singleChannel.text = textVar
        }
    }

    override fun onBindViewHolder(holder: Holder, position: Int) {
        holder.bindText(channels[position].toString(), context)

        holder.itemView.setOnClickListener {

            ChannelName.activeChannel = channels[position]

            holder.mainLayout.drawer_layout.closeDrawer(GravityCompat.START)

        }

    }

    override fun getItemCount(): Int {
        return channels.count()
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChannelsAdapter.Holder {

        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.channel_list_layout, parent, false)
        return Holder(view)

    }

}

This is the singleton

object ChannelName {

var activeChannel : Channel? = null

}
Tsabary
  • 3,119
  • 2
  • 24
  • 66
  • 1
    I don't see any Singleton Class in this code . – ADM Feb 26 '19 at 04:38
  • You can use an `Observable` like `LiveData` for `ChannelName` and whenever you change the value inside the adapter, the `Observer` inside the activity will get notified – Mosius Feb 26 '19 at 04:41
  • @ADM the singleton is in a separate file, I've edited the question to show it. If there are other missing parts for my question please let me know and I'll add them – Tsabary Feb 26 '19 at 04:45
  • @Mosius I've never used one before. I'll do some reading thanks – Tsabary Feb 26 '19 at 04:47

2 Answers2

1

You can rewrite the setter for activeChanell variable and call a listener that has been added before to notify your Activity:

object ChannelName {
    private val listeners = ArrayList<(Channel?) -> Unit>()

    fun addChannelNameChangedListener(listener: (Channel?) -> Unit) {
        listeners.add(listener)
    }

    fun removeChannelNameChangedListener(listener: (Channel?) -> Unit) {
        listeners.remove(listener)
    }

    var activeChannel: Channel? = null
        set(value) {
            field = value
            listeners.forEach { it.invoke(value) }
        }
}

And inside the Activity add a listener like this:

ChannelName.addChannelNameChangedListener { 
    // Do your operation
}

The alternative solution is to use Observable utils like LiveData, so you shouldn't worry about the Android life cycle any more:

object ChannelName {
    val activeChannel: MutableLiveData<ChannelName> = MutableLiveData()
}

To change the value inside your adapter simply call:

ChannelName.activeChannel.value = channels[position]

And inside your activity Observe to the variable by calling:

ChannelName.activeChannel.observe(this, Observer { 
    // Do your operation
})
Mosius
  • 1,602
  • 23
  • 32
  • Lifa saver! Spent all day trying to figure this out. I still can't say that I fully understand what is going on here, but now I have a working code I can try to dissect. Thanks again. – Tsabary Feb 26 '19 at 09:50
0
class ChannelsAdapter(val context: Context, val channels: ArrayList<Channel>) :
        RecyclerView.Adapter<ChannelsAdapter.Holder>() {

    private var itemClickListener: OnItemClickListener? = null

    fun setItemClickListener(itemClickListener: OnItemClickListener) {
        this.itemClickListener = itemClickListener
    }

    interface OnItemClickListener {
        fun onItemClick(position: Int)
    }

    inner class Holder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener {

        val singleChannel = itemView.findViewById<TextView>(R.id.single_channel)

        val mainLayout = LayoutInflater.from(context).inflate(R.layout.activity_main, null)

        fun bindText(textVar: String, context: Context) {
            singleChannel.text = textVar
        }

        override fun onClick(v: View?) {
            val position = adapterPosition
            itemClickListener?.let {
                if (position != RecyclerView.NO_POSITION) {
                    it.onItemClick(position)
                }
            }
        }
    }

    override fun onBindViewHolder(holder: Holder, position: Int) {
        holder.bindText(channels[position].toString(), context)
    }

    override fun getItemCount(): Int {
        return channels.count()
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChannelsAdapter.Holder {
        val view = LayoutInflater.from(parent.context)
                .inflate(R.layout.channel_list_layout, parent, false)
        return Holder(view)
    }
}

This way you can setItemClickListener to the adapter in your activity and get callback from your recyclerView. You should not set listener in onBind() method since it will be called more than your items' count.

underoid
  • 429
  • 4
  • 11