Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ android {
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
viewBinding = true
}
dataBinding {
enabled = true
}
}

dependencies {
Expand All @@ -38,6 +44,11 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.5.1'
implementation 'com.google.android.material:material:1.7.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.databinding:databinding-runtime:8.10.0'

implementation "androidx.paging:paging-runtime:3.2.0"
implementation 'it.xabaras.android:recyclerview-swipedecorator:1.4'

testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
Expand Down
62 changes: 62 additions & 0 deletions app/src/main/java/otus/gpb/recyclerview/ChatAdapter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package otus.gpb.recyclerview

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.ListAdapter
import otus.gpb.recyclerview.databinding.ChatItemBinding

class ChatAdapter: ListAdapter<ChatItem, ChatItemViewHolder>(ChatItemDiffCallback()) {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChatItemViewHolder {
val binding = ChatItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ChatItemViewHolder(binding)
}

override fun onBindViewHolder(holder: ChatItemViewHolder, position: Int) {
val chatItem = getItem(position)
val binding = holder.binding
binding.textViewAuthor.setText(chatItem.author)
binding.textViewTittle.setText(chatItem.title)
binding.textViewMessage.setText(chatItem.messageLast)
binding.textViewTime.setText(chatItem.date)
when(chatItem.checkMark){
CheckMark.SENT_CHECKED.name -> {
binding.imageViewCheck.visibility = View.GONE
binding.imageViewTwoCheck.visibility = View.VISIBLE
}
CheckMark.SENT_UNCHECKED.name -> {
binding.imageViewCheck.visibility = View.VISIBLE
binding.imageViewTwoCheck.visibility = View.GONE
}
else -> {
binding.imageViewCheck.visibility = View.GONE
binding.imageViewTwoCheck.visibility = View.GONE
}
}
if (chatItem.isMentioned){
binding.imageViewAt.visibility = View.VISIBLE
} else {
binding.imageViewAt.visibility = View.GONE
}
if (chatItem.isPinned){
binding.imageViewPin.visibility = View.VISIBLE
} else {
binding.imageViewPin.visibility = View.GONE
}
if (chatItem.soundIsOn){
binding.imageViewVolume.visibility = View.GONE
} else {
binding.imageViewVolume.visibility = View.VISIBLE
}
binding.imageViewAvatar.setImageResource(chatItem.image)
}

fun submitAntSortList(list: List<ChatItem>){
val sortedList = list.sortedByDescending {
it.id
it.isPinned
}
submitList(sortedList)
}
}
15 changes: 15 additions & 0 deletions app/src/main/java/otus/gpb/recyclerview/ChatItem.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package otus.gpb.recyclerview


data class ChatItem(
val id: Int,
val checkMark: String,
val isPinned: Boolean,
val soundIsOn: Boolean,
val isMentioned: Boolean,
val date: String,
val title: String,
val author: String,
val messageLast: String,
val image: Int,
)
13 changes: 13 additions & 0 deletions app/src/main/java/otus/gpb/recyclerview/ChatItemDiffCallback.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package otus.gpb.recyclerview

import androidx.recyclerview.widget.DiffUtil

class ChatItemDiffCallback: DiffUtil.ItemCallback<ChatItem>() {
override fun areItemsTheSame(oldItem: ChatItem, newItem: ChatItem): Boolean {
return oldItem.id == newItem.id
}

override fun areContentsTheSame(oldItem: ChatItem, newItem: ChatItem): Boolean {
return oldItem == newItem
}
}
6 changes: 6 additions & 0 deletions app/src/main/java/otus/gpb/recyclerview/ChatItemViewHolder.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package otus.gpb.recyclerview

import androidx.recyclerview.widget.RecyclerView.ViewHolder
import otus.gpb.recyclerview.databinding.ChatItemBinding

class ChatItemViewHolder(val binding: ChatItemBinding): ViewHolder(binding.root)
5 changes: 5 additions & 0 deletions app/src/main/java/otus/gpb/recyclerview/CheckMark.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package otus.gpb.recyclerview

enum class CheckMark {
SENT_CHECKED, SENT_UNCHECKED, NOTHING_WAS_SENT
}
52 changes: 52 additions & 0 deletions app/src/main/java/otus/gpb/recyclerview/CustomDecorator.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package otus.gpb.recyclerview

import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Rect
import android.util.TypedValue
import android.view.View
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ItemDecoration

class CustomDecorator(context: Context): ItemDecoration() {


private val bounds = Rect()
private val paint = Paint().apply {
color = ContextCompat.getColor(context, R.color.gray_light)
strokeWidth = 1f
}
val leftMarginInPx = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
78f,
context.resources.displayMetrics
).toInt()

override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
super.onDraw(c, parent, state)

val childCount = parent.childCount
for (i in 0 until childCount) {
val child = parent.getChildAt(i)
parent.getDecoratedBoundsWithMargins(child, bounds)
val positionCurrent = parent.getChildAdapterPosition(child)
if (positionCurrent != RecyclerView.NO_POSITION) {
val lastElementPosition = parent.adapter?.itemCount?.minus(1)
if (positionCurrent != lastElementPosition) {
c.drawLine(
leftMarginInPx.toFloat(),
bounds.bottom.toFloat(),
bounds.right.toFloat(),
bounds.bottom.toFloat(),
paint,
)
}
}
}
}
}



186 changes: 184 additions & 2 deletions app/src/main/java/otus/gpb/recyclerview/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,194 @@
package otus.gpb.recyclerview

import androidx.appcompat.app.AppCompatActivity
import android.graphics.Canvas
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import it.xabaras.android.recyclerview.swipedecorator.RecyclerViewSwipeDecorator
import otus.gpb.recyclerview.databinding.ActivityMainBinding
import kotlin.random.Random


class MainActivity : AppCompatActivity() {

private lateinit var adapter: ChatAdapter
private var currentPage = 1
private val pageSize = 30
var id = 1
var previousTotalItemCount = 0
private var isLoading = false
private var chatList: MutableList<ChatItem> = mutableListOf()
private val archiveList: MutableList<ChatItem> = mutableListOf()
private val binding: ActivityMainBinding by lazy {
ActivityMainBinding.inflate(layoutInflater)
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setContentView(binding.root)
initRecyclerView()
setFirstChats()
generateRandomChats(pageSize)
setScrolling()
}


private fun setScrolling(){
binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {

override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val layoutManager = recyclerView.layoutManager as LinearLayoutManager
val totalItemCount = layoutManager.itemCount
val lastVisibleItem = layoutManager.findLastCompletelyVisibleItemPosition()
if (isLoading && totalItemCount > previousTotalItemCount) {
isLoading = false
previousTotalItemCount = totalItemCount
}
if (!isLoading && lastVisibleItem >= totalItemCount - 5) {
Log.d("MainActivity", currentPage.toString())
loadNextPage()
}
}
})
}
private fun removeItem(position: Int){
val itemToDelete = adapter.currentList[position]
val newList = chatList.apply {
remove(itemToDelete)
}
adapter.submitAntSortList(newList)
}

private fun setFirstChats(){
chatList = generateRandomChats(currentPage).toMutableList()
adapter.submitAntSortList(chatList)
}

private fun addInArchiveList(position: Int){
val item = adapter.currentList[position]
archiveList.add(item)
}

private fun loadNextPage() {
isLoading = true
val newChats = generateRandomChats(pageSize)
chatList.addAll(newChats)
adapter.submitAntSortList(chatList.toList())
currentPage++
}

private fun generateRandomChats(size: Int): List<ChatItem>{
val listRandomChats = mutableListOf<ChatItem>()
for (i in 1..size) {
listRandomChats.add(
ChatItem(
id++,
getRandomCheckMark(),
true,
Random.nextBoolean(),
Random.nextBoolean(),
"12:10",
"Just design",
"Nicolay",
"I want pizza",
R.drawable.food_steak
)
)
}
return listRandomChats
}

private fun getRandomCheckMark(): String {
val values = CheckMark.values().toList()
val randomNumber = Random.nextInt(3)
return values[randomNumber].name
}

private fun initRecyclerView() {
adapter = ChatAdapter()
binding.recyclerView.adapter = adapter
binding.recyclerView.addItemDecoration(CustomDecorator(this))
val itemTouchHelper = ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(
0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
return false
}

override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val position = viewHolder.adapterPosition
when(direction){
ItemTouchHelper.LEFT -> {
addInArchiveList(position)
removeItem(position)
Log.d("MainActivity", archiveList.toString())
}
ItemTouchHelper.RIGHT -> {
removeItem(position)
}
}
}

override fun onChildDraw(
c: Canvas,
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
dX: Float,
dY: Float,
actionState: Int,
isCurrentlyActive: Boolean
) {
RecyclerViewSwipeDecorator.Builder(
c,
recyclerView,
viewHolder,
dX,
dY,
actionState,
isCurrentlyActive
)
.addSwipeRightBackgroundColor(
ContextCompat.getColor(
this@MainActivity,
R.color.red
)
)
.addSwipeRightActionIcon(R.drawable.trash_can_outline)
.addSwipeRightLabel("Delete")
.setSwipeRightLabelColor(
ContextCompat.getColor(
this@MainActivity,
R.color.white
)
)
.addSwipeLeftBackgroundColor(
ContextCompat.getColor(
this@MainActivity,
R.color.blue
)
)
.addSwipeLeftActionIcon(R.drawable.package_down)
.addSwipeLeftLabel("Archive")
.setSwipeLeftLabelColor(
ContextCompat.getColor(
this@MainActivity,
R.color.white
)
)
.create()
.decorate()
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
}
})
itemTouchHelper.attachToRecyclerView(binding.recyclerView)
}
}
9 changes: 9 additions & 0 deletions app/src/main/res/drawable/at.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@color/white"
android:pathData="M12,15C12.81,15 13.5,14.7 14.11,14.11C14.7,13.5 15,12.81 15,12C15,11.19 14.7,10.5 14.11,9.89C13.5,9.3 12.81,9 12,9C11.19,9 10.5,9.3 9.89,9.89C9.3,10.5 9,11.19 9,12C9,12.81 9.3,13.5 9.89,14.11C10.5,14.7 11.19,15 12,15M12,2C14.75,2 17.1,3 19.05,4.95C21,6.9 22,9.25 22,12V13.45C22,14.45 21.65,15.3 21,16C20.3,16.67 19.5,17 18.5,17C17.3,17 16.31,16.5 15.56,15.5C14.56,16.5 13.38,17 12,17C10.63,17 9.45,16.5 8.46,15.54C7.5,14.55 7,13.38 7,12C7,10.63 7.5,9.45 8.46,8.46C9.45,7.5 10.63,7 12,7C13.38,7 14.55,7.5 15.54,8.46C16.5,9.45 17,10.63 17,12V13.45C17,13.86 17.16,14.22 17.46,14.53C17.76,14.84 18.11,15 18.5,15C18.92,15 19.27,14.84 19.57,14.53C19.87,14.22 20,13.86 20,13.45V12C20,9.81 19.23,7.93 17.65,6.35C16.07,4.77 14.19,4 12,4C9.81,4 7.93,4.77 6.35,6.35C4.77,7.93 4,9.81 4,12C4,14.19 4.77,16.07 6.35,17.65C7.93,19.23 9.81,20 12,20H17V22H12C9.25,22 6.9,21 4.95,19.05C3,17.1 2,14.75 2,12C2,9.25 3,6.9 4.95,4.95C6.9,3 9.25,2 12,2Z"/>
</vector>
Loading