Сортировка 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 шт):
Есть опасения относительно вашей структуры. Но в целом у вас не должно возникать проблем. Убедитесь, что вы не создаете адаптер каждый раз при изменении данных. Приведу пример:
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 }
Контекст передавать в адаптер плохо. Утечки памяти и прочее
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) {
}
})
}
}
У меня, естественно, код не компилируется, проверить не могу, но думаю, что сразу должно заработать как надо.