Сортировка recyclerview

Имеется recyclerview, который содержит список чатов. Я могу написать любому пользователю, но если до этого чат уже был создан, то он не поднимется выше (ну как в обычных мессенджерах). Это код фрагмента с чатами:

class MessagesFragment : Fragment() {

    private var userAdapterSecond: UserAdapterSecond? = null
    private var mUsers: List<User>? = null
    private var usersChatList: List<ChatList>? = null
    lateinit var recycler_view_messages: RecyclerView
    private var firebaseUser: FirebaseUser? = null

    private var param1: String? = null
    private var param2: String? = null

    override fun onCreate(savedInstanceState: Bundle?)
    {
        super.onCreate(savedInstanceState)
        arguments?.let {
            param1 = it.getString(ARG_PARAM1)
            param2 = it.getString(ARG_PARAM2)
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        val view = inflater.inflate(R.layout.fragment_messages, container, false)


        recycler_view_messages = view.findViewById(R.id.recycler_view_messages)
        recycler_view_messages.setHasFixedSize(true)
        recycler_view_messages.layoutManager = LinearLayoutManager(context)


        firebaseUser = FirebaseAuth.getInstance().currentUser


        usersChatList = ArrayList()

        val ref = FirebaseDatabase.getInstance().reference.child("ChatList").child(firebaseUser!!.uid)
        ref!!.addValueEventListener(object : ValueEventListener{
            override fun onDataChange(p0: DataSnapshot)
            {
                (usersChatList as ArrayList).clear()

                for (dataSnapshot in p0.children)
                {
                    val chatList = dataSnapshot.getValue(ChatList::class.java)

                    (usersChatList as ArrayList).add(chatList!!)
                }
                retrieveChatList()
            }

            override fun onCancelled(p0: DatabaseError) {

            }
        })


        return view
    }
}

Вот функция retrieveChatList():

private fun retrieveChatList()
    {
        mUsers = ArrayList()

        val ref = FirebaseDatabase.getInstance().reference.child("Users")
        ref!!.addValueEventListener(object : ValueEventListener {
            override fun onDataChange(p0: DataSnapshot)
            {
                (mUsers as ArrayList).clear()

                for (dataSnapshot in p0.children)
                {
                    val user = dataSnapshot.getValue(User::class.java)

                    for (eachChatList in usersChatList!!)
                    {
                        if (user!!.getUID().equals(eachChatList.getId()))
                        {
                            (mUsers as ArrayList).add(user!!)
                        }
                    }
                }

                if (context != null)
                {
                    userAdapterSecond = UserAdapterSecond(context!!, (mUsers as ArrayList<User>), true)
                    recycler_view_messages.adapter = userAdapterSecond
                }
            }

            override fun onCancelled(p0: DatabaseError) {

            }
        })
    }

В модуле Chat я получаю время. Вот код UserAdapterSecond для получения последнего сообщение и времени:

private fun retrieveLastMessage(chatUserId: String?, lastMessageTxt: TextView, timeMessage: TextView)
    {
        lastMsg = "defaultMsg"

        val firebaseUser = FirebaseAuth.getInstance().currentUser
        val reference = FirebaseDatabase.getInstance().reference.child("Chats")

        reference.addValueEventListener(object  : ValueEventListener{
            @SuppressLint("SetTextI18n")
            override fun onDataChange(p0: DataSnapshot)
            {
                for (dataSnapshot in p0.children)
                {
                    val chat: Chat? = dataSnapshot.getValue(Chat::class.java)

                    if (firebaseUser != null && chat != null)
                    {
                        if (chat.getReceiver() == firebaseUser!!.uid  &&
                            chat.getSender() == chatUserId  ||

                            chat.getReceiver() == chatUserId  &&
                            chat.getSender() == firebaseUser!!.uid)
                        {
                            lastMsg = chat.getMessage()!!
                            timeMessage.text = chat.getTime()
                        }
                    }

                }
                when(lastMsg)
                {
                    "defaultMsg" -> lastMessageTxt.text = "No Messages"
                    "Sent you an image" -> lastMessageTxt.text = "Photo"
                    else -> lastMessageTxt.text = lastMsg
                }
                lastMsg = "defaultMsg"
            }

            override fun onCancelled(error: DatabaseError)
            {

            }
        })
    }

А конкретно: timeMessage.text = chat.getTime() Ну тут всё легко, никаких проблем нет. введите сюда описание изображения

На скрине показан сам фрагмент с чатами и временем. Можно заметить, что последний чат расположен не в самом верху, хотя написан он был сегодня (смотрите на дату и время). Сам вопрос: как мне сортировать recyclerview так, чтобы сообщения с самой последней датой и временем были в самом начале списка? Код получения списка представлен выше.

Что получилось:

val chat: Chat? = dataSnapshot.getValue(Chat::class.java)

(mUsers as ArrayList).add(user!!)
if (chat != null) {
    (mUsers as ArrayList).sortBy {chat.getTime()}
}

Ответы (2 шт):

Автор решения: Vlad Orlinskas

Есть опасения относительно вашей структуры. Но в целом у вас не должно возникать проблем. Убедитесь, что вы не создаете адаптер каждый раз при изменении данных. Приведу пример:

class ClientAdapter : RecyclerView.Adapter<Client, ClientAdapter.Holder>() {

    private val _items = mutableListOf<Client?>()

    var items: List<Client?>
        get() = _items
        set(value) {
            _items.clear()
            _items.addAll(value)
            notifyDataSetChanged()
        }
}

Как видите при обновлении данных я не буду создавать новый адаптер, я просто сделаю: adapter.items = listOf<Clients> из своей активити. Вам просто нужно будет перед поставлением данных сортировать этот список items.sortBy { it.time }

→ Ссылка
Автор решения: beeline09

Контекст передавать в адаптер плохо. Утечки памяти и прочее

retrieveLastMessage() - очень не хорошо получать данные в адаптере! Вообще вешать какую-либо логику на адаптер - плохо. Каждый класс должен делать что-то одно. Адаптер должен быть максимально тупой и служить только отображения данных в списке.

isChatCheck - просто вызываем adapter.isChatCheck = true/false и тогда все перерисуется с учетом isChatCheck. Не надо его передавать в адаптере каждый раз

(mContext as FragmentActivity).supportFragmentManager.beginTransaction().replace(R.id.fragment_container, OtherPeopleProfileFragment()).commit() - это устаревшее решение. Лучше всего использовать Jetpack Navigation.

Что происходит в retrieveLastMessage()? DateTimeFormatter требует последних версий Андроид. Все можно сделать проще с помощью SimpleDateFormat Переделал все внутри override fun onDataChange(p0: DataSnapshot)

В объявлении адаптера тоже ошибка. Там указано, что вьюхолдер он использует от UserAdapter.ViewHolder, а должен использовать свой: UserAdapterSecond.ViewHolder. Исправил.

Ну а теперь сам код))) Для начала создаем в проекте файл DateExtensions.kt и кладем в него следующее содержимое:

import java.util.*

//Екстеншн. Проверяем дату на то в этом году она или нет
fun Date.isInCurrentYear(): Boolean {
    val currDate = Calendar.getInstance(Locale.getDefault())
    val srDate = Calendar.getInstance(Locale.getDefault()).apply { time = this@isInCurrentYear }
    return srDate.get(Calendar.YEAR) == currDate.get(Calendar.YEAR)
}

//Екстеншн. Проверяем дату на то сегодня она или нет
fun Date.isToday(): Boolean {
    val currDate = Calendar.getInstance(Locale.getDefault())
    val srDate = Calendar.getInstance(Locale.getDefault()).apply { time = this@isToday }
    return srDate.get(Calendar.YEAR) == currDate.get(Calendar.YEAR) && srDate.get(Calendar.MONTH) == currDate.get(
        Calendar.MONTH
    ) && srDate.get(
        Calendar.DAY_OF_MONTH
    ) == currDate.get(Calendar.DAY_OF_MONTH)
}

Вот переделанный MessagesFragment:

class MessagesFragment : Fragment() {

    private var userAdapterSecond: UserAdapterSecond = UserAdapterSecond( isFragment = true)
    private var mUsers: MutableList<User> = mutableListOf()
    private var usersChatList: List<ChatList>? = null
    lateinit var recycler_view_messages: RecyclerView
    private var firebaseUser: FirebaseUser? = null

    private var param1: String? = null
    private var param2: String? = null

    override fun onCreate(savedInstanceState: Bundle?)
    {
        super.onCreate(savedInstanceState)
        arguments?.let {
            param1 = it.getString(ARG_PARAM1)
            param2 = it.getString(ARG_PARAM2)
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        val view = inflater.inflate(R.layout.fragment_messages, container, false)


        recycler_view_messages = view.findViewById(R.id.recycler_view_messages)
        recycler_view_messages.setHasFixedSize(true)
        recycler_view_messages.layoutManager = LinearLayoutManager(context)


        firebaseUser = FirebaseAuth.getInstance().currentUser


        usersChatList = ArrayList()

        val ref = FirebaseDatabase.getInstance().reference.child("ChatList").child(firebaseUser!!.uid)
        ref!!.addValueEventListener(object : ValueEventListener{
            override fun onDataChange(p0: DataSnapshot)
            {
                (usersChatList as ArrayList).clear()

                for (dataSnapshot in p0.children)
                {
                    val chatList = dataSnapshot.getValue(ChatList::class.java)

                    (usersChatList as ArrayList).add(chatList!!)
                }
                retrieveChatList()
            }

            override fun onCancelled(p0: DatabaseError) {

            }
        })


        return view
    }

    private fun retrieveChatList()
    {


        val ref = FirebaseDatabase.getInstance().reference.child("Users")
        ref!!.addValueEventListener(object : ValueEventListener {
            override fun onDataChange(p0: DataSnapshot)
            {
                mUsers.clear()

                for (dataSnapshot in p0.children)
                {
                    val user = dataSnapshot.getValue(User::class.java)

                    for (eachChatList in usersChatList!!)
                    {
                        if (user!!.getUID().equals(eachChatList.getId()))
                        {
                            mUsers.add(user!!)
                        }
                    }
                }

                userAdapterSecond.isChatCheck = true
                userAdapterSecond.setData(mUsers)

            }

            override fun onCancelled(p0: DatabaseError) {

            }
        })
    }
}

Ну и сам адаптер:

class UserAdapterSecond(
    private var isFragment: Boolean = false
) : RecyclerView.Adapter<UserAdapterSecond.ViewHolder>() {

    private var mUser: MutableList<User> = mutableListOf()

    fun setData(items: List<User>) {
        mUser.clear()
        mUser.addAll(items)
        notifyDataSetChanged()
    }

    var isChatCheck: Boolean = true
        set(value) {
            field = value
            notifyDataSetChanged()
        }


    private var firebaseUser: FirebaseUser? = FirebaseAuth.getInstance().currentUser


    var lastMsg: String = ""

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserAdapterSecond.ViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.messages_item_layout, parent, false)
        return UserAdapterSecond.ViewHolder(view)

    }

    override fun getItemCount(): Int {
        return mUser.size
    }

    override fun onBindViewHolder(holder: UserAdapterSecond.ViewHolder, position: Int) {
        val user = mUser[position]

        val mContext = holder.timeMessage.context

        holder.userNameTextView.text = user.getUsername()
        holder.userFullnameTextView.text = user.getFullname()

        if (user.getDeleted() != "true") {
            Glide.with(mContext.applicationContext).load(user.getImage())
                .into(holder.userProfileImage)
        } else {
            Glide.with(mContext.applicationContext).load(R.drawable.profile_deleted_image)
                .into(holder.userProfileImage)
        }


        if (isChatCheck) {
            retrieveLastMessage(user.getUID(), holder.lastMessageTxt, holder.timeMessage)
        } else {
            holder.lastMessageTxt.visibility = View.GONE
            holder.timeMessage.visibility = View.GONE
        }





        holder.linearUser.setOnClickListener {
            val intent = Intent(mContext, MessagesChatActivity::class.java)
            intent.putExtra("userId", user.getUID())
            mContext.startActivity(intent)
        }


        holder.userProfileImage.setOnClickListener(View.OnClickListener {
            val pref = mContext.getSharedPreferences("PREFS", Context.MODE_PRIVATE).edit()
            pref.putString("profileId", user.getUID())
            pref.apply()

            (mContext as FragmentActivity).supportFragmentManager.beginTransaction()
                .replace(R.id.fragment_container, OtherPeopleProfileFragment()).commit()
        })


    }

    class ViewHolder(@NonNull itemView: View) : RecyclerView.ViewHolder(itemView) {
        var userProfileImage: CircleImageView =
            itemView.findViewById(R.id.user_profile_image_search)
        var userFullnameTextView: TextView = itemView.findViewById(R.id.user_full_name_search)
        var userNameTextView: TextView = itemView.findViewById(R.id.user_name_search)
        var followButton: ImageButton = itemView.findViewById(R.id.follow_btn_search)
        var followingButton: ImageButton = itemView.findViewById(R.id.following_btn_search)
        var userMessages: ImageButton = itemView.findViewById(R.id.user_messages)
        var linearUser: LinearLayout = itemView.findViewById(R.id.linear_user)
        var lastMessageTxt: TextView = itemView.findViewById(R.id.message_last)
        var onlineImageView: CircleImageView = itemView.findViewById(R.id.image_online)
        var mp: ImageView = itemView.findViewById(R.id.mp)
        var checkMark: ImageView = itemView.findViewById(R.id.check_mark)
        var heart: ImageView = itemView.findViewById(R.id.heart)
        var game: ImageView = itemView.findViewById(R.id.game)
        var timeMessage: TextView = itemView.findViewById(R.id.message_time)
    }


    private fun retrieveLastMessage(
        chatUserId: String?,
        lastMessageTxt: TextView,
        timeMessage: TextView
    ) {
        lastMsg = "defaultMsg"

        val firebaseUser = FirebaseAuth.getInstance().currentUser
        val reference = FirebaseDatabase.getInstance().reference.child("Chats")

        reference.addValueEventListener(object : ValueEventListener {

            @SuppressLint("SetTextI18n")

            override fun onDataChange(p0: DataSnapshot) {
                for (dataSnapshot in p0.children) {
                    val chat: Chat? = dataSnapshot.getValue(Chat::class.java)

                    if (firebaseUser != null && chat != null) {
                        if (chat.getReceiver() == firebaseUser!!.uid &&
                            chat.getSender() == chatUserId ||

                            chat.getReceiver() == chatUserId &&
                            chat.getSender() == firebaseUser!!.uid
                        ) {
                            lastMsg = chat.getMessage()!!

                            val formatter =
                                SimpleDateFormat("hh:mm a dd-MM-yyyy", Locale.getDefault())

                            if (chat.getTime()!!.isToday()) {
                                formatter.applyPattern("HH:mm")
                            } else if (!chat.getTime()!!.isToday() && chat.getTime()!!
                                    .isInCurrentYear()
                            ) {
                                formatter.applyPattern("d MMM")
                            } else {
                                formatter.applyPattern("d MMM yyyy")
                            }

                            timeMessage.text = formatter.format(chat.getTime()!!)
                        }
                    }

                }
                when (lastMsg) {
                    "defaultMsg" -> lastMessageTxt.text = "No Messages"
                    "Sent you an image" -> {
                        lastMessageTxt.text = "Photo"
                        lastMessageTxt.setTextColor(Color.parseColor("#0080FF"))
                    }

                    else -> lastMessageTxt.text = lastMsg
                }
                lastMsg = "defaultMsg"
            }

            override fun onCancelled(error: DatabaseError) {

            }
        })
    }
}

У меня, естественно, код не компилируется, проверить не могу, но думаю, что сразу должно заработать как надо.

→ Ссылка