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
2 changes: 2 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ 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.recyclerview:recyclerview:1.3.2'
implementation 'androidx.recyclerview:recyclerview-selection:1.1.0'
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
42 changes: 42 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,42 @@
package otus.gpb.recyclerview

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView

class ChatAdapter(
private val listener: Listener
) : RecyclerView.Adapter<ChatViewHolder>() {

private var list = listOf<ChatItem>()

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChatViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.message, parent, false)

return ChatViewHolder(view, listener)
}

override fun getItemCount(): Int = list.size

override fun onBindViewHolder(holder: ChatViewHolder, position: Int) {
val item = list.getOrNull(position)
item?.let {
holder.bind(item)

}
}

fun setData(newList: List<ChatItem>) {
list = newList
}

fun removeItem(id: Int) {

list = list.toMutableList().also {
it.removeAt(id)
}.toList()

notifyItemRangeRemoved(id, 1)
}
}
17 changes: 17 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,17 @@
package otus.gpb.recyclerview

data class ChatItem(
val id: Int,
val imageId: Int,
val name: String,
val status: String,
val message: String,
val counter: Int,
val verified: Boolean,
val voice: Boolean,
val mute: Boolean,
val scam: Boolean,
val delivered: Boolean,
val read: Boolean,
val dateTime: String,
)
62 changes: 62 additions & 0 deletions app/src/main/java/otus/gpb/recyclerview/ChatViewHolder.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package otus.gpb.recyclerview

import android.view.View
import android.view.View.INVISIBLE
import android.view.View.VISIBLE
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView

class ChatViewHolder(
private val view: View,
private val listener: Listener
) : RecyclerView.ViewHolder(view) {

private val image: ImageView by lazy { view.findViewById(R.id.imageAvatar) }
private val name: TextView by lazy { view.findViewById(R.id.textViewName) }
private val status: TextView by lazy { view.findViewById(R.id.textViewStatus) }
private val time: TextView by lazy { view.findViewById(R.id.time) }
private val message: TextView by lazy { view.findViewById(R.id.message) }
private val imageVoice: ImageView by lazy { view.findViewById(R.id.imageVOIP) }
private val iconVerif: ImageView by lazy { view.findViewById(R.id.icon_verified) }
private val iconScam: ImageView by lazy { view.findViewById(R.id.icon_scam) }
private val iconMute: ImageView by lazy { view.findViewById(R.id.icon_mute) }
private val counter: TextView by lazy { view.findViewById(R.id.counter) }
private val iconDelivered: ImageView by lazy { view.findViewById(R.id.delivered) }
private val root: ViewGroup by lazy { view.findViewById(R.id.root) }
private val delete: View by lazy { view.findViewById(R.id.delete) }

fun bind(item: ChatItem) {
name.text = item.name
status.text = item.status
message.text = item.message
imageVoice.visibility = if (item.voice) VISIBLE else INVISIBLE
iconVerif.visibility = if (item.verified) VISIBLE else INVISIBLE
iconScam.visibility = if (item.scam) VISIBLE else INVISIBLE
iconMute.visibility = if (item.mute) VISIBLE else INVISIBLE
counter.visibility = if (item.counter > 0) VISIBLE else INVISIBLE
counter.text = item.counter.toString()
time.text = item.dateTime
iconDelivered.visibility = if (item.delivered && !item.read) VISIBLE else INVISIBLE

val id: Int? = if (item.read) R.drawable.ic_doublecheck
else if (item.delivered && !item.read) R.drawable.ic_check
else if (item.delivered && item.read) R.drawable.ic_doublecheck
else null

if (id != null) {
iconDelivered.setImageResource(id)
iconDelivered.visibility = VISIBLE
}
else{
iconDelivered.visibility = INVISIBLE
}

image.setImageResource(item.imageId)

root.setOnClickListener { listener.onItemClicked(item.id) }
delete.setOnClickListener { listener.onItemActionClicked(item.id) }
}

}
83 changes: 83 additions & 0 deletions app/src/main/java/otus/gpb/recyclerview/ItemTouchHelperCallBack.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package otus.gpb.recyclerview

import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.toBitmap
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView


class ItemTouchHelperCallBack() : ItemTouchHelper.Callback() {

override fun getMovementFlags(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder
): Int {
return makeMovementFlags(
ItemTouchHelper.UP or ItemTouchHelper.DOWN,
ItemTouchHelper.LEFT
)
}

override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
return true
}

override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
if (direction == ItemTouchHelper.LEFT)
viewHolder.chatAdapter.removeItem(viewHolder.absoluteAdapterPosition)
}

override fun onChildDraw(
c: Canvas,
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
dX: Float,
dY: Float,
actionState: Int,
isCurrentlyActive: Boolean
) {
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
val itemView = viewHolder.itemView
var blue = Paint().apply { setColor(Color.parseColor("#3D95D4")) }
var white = Paint().apply { setColor(Color.parseColor("#FFFFFF")) }

val icon = ContextCompat.getDrawable(recyclerView.context, R.drawable.ic_archive)?.toBitmap()

if (dX <= 0) {

/* swipe left */
try {
c.drawRect(
itemView.right.toFloat() + dX, itemView.top.toFloat(),
itemView.right.toFloat(), itemView.bottom.toFloat(), blue
)

if (dX <= -200) {
if (icon != null) {
c.drawBitmap(
icon,
itemView.right.toFloat() - icon.width - icon.width / 2,
itemView.top.toFloat() + (itemView.bottom.toFloat() - itemView.top.toFloat() - icon.height) / 2,
white
)
}
}
}
catch (e: Exception) {

}
}
}
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
}

private val RecyclerView.ViewHolder.chatAdapter: ChatAdapter
get() = bindingAdapter as? ChatAdapter ?: error("Not ChatAdapter")
}
6 changes: 6 additions & 0 deletions app/src/main/java/otus/gpb/recyclerview/Listener.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package otus.gpb.recyclerview

interface Listener {
fun onItemClicked(id: Int)
fun onItemActionClicked(id: Int)
}
49 changes: 48 additions & 1 deletion app/src/main/java/otus/gpb/recyclerview/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,58 @@ package otus.gpb.recyclerview

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView

class MainActivity : AppCompatActivity() {
class MainActivity : AppCompatActivity(), Listener {

private val adapter: ChatAdapter by lazy { ChatAdapter(this) }
private var list: List<ChatItem> = emptyList()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
recyclerView.adapter = adapter

list = generateList()
adapter.setData(list)

ItemTouchHelper(ItemTouchHelperCallBack()).attachToRecyclerView(recyclerView)


}

private fun generateList() = run {
val list = mutableListOf<ChatItem>()
repeat(10) {
val chatItem = ChatItem(
id = it,
name = "Ivan $it",
status = "stat",
message = "This is message",
dateTime = "Fri",
mute = (it % 2 == 1),
counter = it,
verified = (it % 2 == 1),
voice = (it % 2 == 0),
scam = (it % 4 == 0),
delivered = true,
read = (it % 2 == 1),
imageId = R.drawable.avatar,
)
list.add(chatItem)
}
list.toList()
}

override fun onItemClicked(id: Int) {
Toast.makeText(this, "onItemClicked $id", Toast.LENGTH_SHORT).show()
}

override fun onItemActionClicked(id: Int) {
adapter.removeItem(id)
}
}
Binary file added app/src/main/res/drawable/avatar.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions app/src/main/res/drawable/divider.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<size android:height="3dp"/>
<solid android:color="#A60505"/>
</shape>
9 changes: 9 additions & 0 deletions app/src/main/res/drawable/ic_archive.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="48dp"
android:height="48dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@color/white"
android:pathData="M5.12,5L5.93,4H17.93L18.87,5M12,17.5L6.5,12H10V10H14V12H17.5L12,17.5M20.54,5.23L19.15,3.55C18.88,3.21 18.47,3 18,3H6C5.53,3 5.12,3.21 4.84,3.55L3.46,5.23C3.17,5.57 3,6 3,6.5V19A2,2 0,0 0,5 21H19A2,2 0,0 0,21 19V6.5C21,6 20.83,5.57 20.54,5.23Z"/>
</vector>
15 changes: 15 additions & 0 deletions app/src/main/res/drawable/ic_at.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12,12m-12,0a12,12 0,1 1,24 0a12,12 0,1 1,-24 0"
android:fillColor="#3D95D4"/>
<path
android:pathData="M14.937,11.667C14.937,13.105 13.771,14.271 12.333,14.271C10.895,14.271 9.73,13.105 9.73,11.667C9.73,10.229 10.895,9.063 12.333,9.063C13.771,9.063 14.937,10.229 14.937,11.667ZM14.937,11.667C14.937,13.105 15.277,14.384 16.635,14.384C17.994,14.384 18.333,12.572 18.333,11.667C18.333,8.353 15.647,5.667 12.333,5.667C9.02,5.667 6.333,8.353 6.333,11.667C6.333,14.98 9.02,17.667 12.333,17.667H14.597"
android:strokeWidth="1.5"
android:fillColor="#00000000"
android:strokeColor="#ffffff"
android:strokeLineCap="round"/>
</vector>
13 changes: 13 additions & 0 deletions app/src/main/res/drawable/ic_check.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="18dp"
android:height="18dp"
android:viewportWidth="18"
android:viewportHeight="18">
<path
android:pathData="M3.176,9.842L6.37,12.706L14.823,5.294"
android:strokeLineJoin="round"
android:strokeWidth="1.5"
android:fillColor="#00000000"
android:strokeColor="#48A938"
android:strokeLineCap="round"/>
</vector>
1 change: 1 addition & 0 deletions app/src/main/res/drawable/ic_circle.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<!-- drawable/circle.xml --><vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24"><path android:fillColor="#000000" android:pathData="M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z" /></vector>
Binary file added app/src/main/res/drawable/ic_counter.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions app/src/main/res/drawable/ic_doublecheck.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="18dp"
android:height="18dp"
android:viewportWidth="18"
android:viewportHeight="18">
<path
android:pathData="M1.059,9.776L4.014,12.533L11.77,5.294M16.941,5.466L9.185,12.706L8.261,11.844"
android:strokeLineJoin="round"
android:strokeWidth="1.5"
android:fillColor="#00000000"
android:strokeColor="#48A938"
android:strokeLineCap="round"/>
</vector>
13 changes: 13 additions & 0 deletions app/src/main/res/drawable/ic_mute.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="18dp"
android:height="19dp"
android:viewportWidth="18"
android:viewportHeight="19">
<path
android:pathData="M4.235,7.842C4.235,7.545 4.485,7.305 4.791,7.305H5.745L11.462,13.141V14.573C11.462,15.305 10.906,15.58 10.35,15.043L6.702,11.97H4.791C4.485,11.97 4.235,11.728 4.235,11.432V7.842Z"
android:fillColor="#BDC1C4"
android:fillType="evenOdd"/>
<path
android:pathData="M4.898,3.678C4.796,3.696 4.702,3.741 4.625,3.808C4.548,3.875 4.492,3.961 4.463,4.057C4.434,4.153 4.434,4.255 4.462,4.351C4.489,4.447 4.544,4.534 4.62,4.602L13.836,13.507C13.885,13.566 13.946,13.614 14.016,13.648C14.085,13.682 14.161,13.702 14.239,13.705C14.317,13.709 14.394,13.697 14.467,13.67C14.54,13.643 14.606,13.602 14.661,13.549C14.716,13.495 14.759,13.432 14.787,13.361C14.815,13.291 14.827,13.216 14.823,13.141C14.819,13.066 14.798,12.992 14.763,12.925C14.727,12.858 14.678,12.799 14.617,12.752L12.402,10.611V4.751C12.402,3.712 11.33,3.723 10.547,4.365L8.026,6.383L5.402,3.846C5.345,3.787 5.274,3.741 5.196,3.712C5.118,3.683 5.034,3.672 4.95,3.678C4.933,3.678 4.915,3.678 4.898,3.678Z"
android:fillColor="#BDC1C4"/>
</vector>
Loading