diff --git a/app/build.gradle b/app/build.gradle index 54e4eac..3302ed5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -30,6 +30,12 @@ android { kotlinOptions { jvmTarget = '1.8' } + buildFeatures { + viewBinding = true + } + dataBinding { + enabled = true + } } dependencies { @@ -41,4 +47,6 @@ dependencies { testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + implementation 'androidx.databinding:databinding-runtime:8.10.0' + implementation 'it.xabaras.android:recyclerview-swipedecorator:1.4' } \ No newline at end of file diff --git a/app/src/main/java/otus/gpb/recyclerview/ChatAdapter.kt b/app/src/main/java/otus/gpb/recyclerview/ChatAdapter.kt new file mode 100644 index 0000000..a370857 --- /dev/null +++ b/app/src/main/java/otus/gpb/recyclerview/ChatAdapter.kt @@ -0,0 +1,51 @@ +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 +import android.view.View.INVISIBLE +import android.view.View.VISIBLE + +class ChatAdapter: ListAdapter(ChatDiffCallback()) { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChatViewHolder { + val binding = ChatItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return ChatViewHolder(binding) + } + + override fun onBindViewHolder(holder: ChatViewHolder, position: Int) { + val chatItem = getItem(position) + val binding = holder.binding + binding.textName.setText(chatItem.name) + binding.textStatus.setText(chatItem.status) + binding.message.setText(chatItem.message) + binding.time.setText(chatItem.time) + + binding.iconVerified.visibility = if (chatItem.verified) VISIBLE else INVISIBLE + binding.iconScam.visibility = if (chatItem.scam) VISIBLE else INVISIBLE + binding.iconMute.visibility = if (chatItem.mute) VISIBLE else INVISIBLE + binding.counter.visibility = if (chatItem.counter > 0) VISIBLE else INVISIBLE + binding.counter.text = chatItem.counter.toString() + binding.time.text = chatItem.time + binding.delivered.visibility = if (chatItem.delivered && !chatItem.read) VISIBLE else INVISIBLE + + val id: Int? = if (chatItem.read) R.drawable.doublecheck + else if (chatItem.delivered && !chatItem.read) R.drawable.check + else if (chatItem.delivered && chatItem.read) R.drawable.doublecheck + else null + + if (id != null) { + binding.delivered.setImageResource(id) + binding.delivered.visibility = VISIBLE + } + else{ + binding.delivered.visibility = INVISIBLE + } + } + + fun submitNewList(list: List){ + submitList(list) + } +} diff --git a/app/src/main/java/otus/gpb/recyclerview/ChatDiffCallback.kt b/app/src/main/java/otus/gpb/recyclerview/ChatDiffCallback.kt new file mode 100644 index 0000000..2721371 --- /dev/null +++ b/app/src/main/java/otus/gpb/recyclerview/ChatDiffCallback.kt @@ -0,0 +1,13 @@ +package otus.gpb.recyclerview + +import androidx.recyclerview.widget.DiffUtil + +class ChatDiffCallback: DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: ChatItem, newItem: ChatItem): Boolean { + return oldItem.id == newItem.id + } + + override fun areContentsTheSame(oldItem: ChatItem, newItem: ChatItem): Boolean { + return oldItem == newItem + } +} diff --git a/app/src/main/java/otus/gpb/recyclerview/ChatItem.kt b/app/src/main/java/otus/gpb/recyclerview/ChatItem.kt new file mode 100644 index 0000000..0d30d6a --- /dev/null +++ b/app/src/main/java/otus/gpb/recyclerview/ChatItem.kt @@ -0,0 +1,15 @@ +package otus.gpb.recyclerview + +data class ChatItem( + val id: Int, + val name: String, + val status: String, + val message: String, + val counter: Int, + val verified: Boolean, + val mute: Boolean, + val scam: Boolean, + val delivered: Boolean, + val read: Boolean, + val time: String, +) diff --git a/app/src/main/java/otus/gpb/recyclerview/ChatViewHolder.kt b/app/src/main/java/otus/gpb/recyclerview/ChatViewHolder.kt new file mode 100644 index 0000000..23c42b3 --- /dev/null +++ b/app/src/main/java/otus/gpb/recyclerview/ChatViewHolder.kt @@ -0,0 +1,6 @@ +package otus.gpb.recyclerview + +import androidx.recyclerview.widget.RecyclerView.ViewHolder +import otus.gpb.recyclerview.databinding.ChatItemBinding + +class ChatViewHolder(val binding: ChatItemBinding): ViewHolder(binding.root) diff --git a/app/src/main/java/otus/gpb/recyclerview/MainActivity.kt b/app/src/main/java/otus/gpb/recyclerview/MainActivity.kt index e2cdca7..05dc641 100644 --- a/app/src/main/java/otus/gpb/recyclerview/MainActivity.kt +++ b/app/src/main/java/otus/gpb/recyclerview/MainActivity.kt @@ -2,11 +2,131 @@ package otus.gpb.recyclerview import androidx.appcompat.app.AppCompatActivity import android.os.Bundle +import otus.gpb.recyclerview.databinding.ActivityMainBinding +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.RecyclerView +import it.xabaras.android.recyclerview.swipedecorator.RecyclerViewSwipeDecorator +import android.graphics.Canvas +import androidx.recyclerview.widget.LinearLayoutManager +import kotlin.random.Random class MainActivity : AppCompatActivity() { + private val binding: ActivityMainBinding by lazy { + ActivityMainBinding.inflate(layoutInflater) + } + private var chatList: MutableList = mutableListOf() + private lateinit var adapter: ChatAdapter + var lastId = 0 + var previousItemsCount = 0 + private var pageLoad = false override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) + setContentView(binding.root) + adapter = ChatAdapter() + binding.recyclerView.adapter = adapter + + 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 -> { + removeItem(position) + } + 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 + ).create().decorate() + super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive) + } + }) + itemTouchHelper.attachToRecyclerView(binding.recyclerView) + createItems(20) + setScrollListener() + } + + private fun setScrollListener(){ + 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 itemsCount = layoutManager.itemCount + val lastVisibleItem = layoutManager.findLastCompletelyVisibleItemPosition() + if (pageLoad && itemsCount > previousItemsCount) { + pageLoad = false + previousItemsCount = itemsCount + } + if (!pageLoad && lastVisibleItem >= itemsCount - 5) { + getNextPage() + } + } + }) + } + + + private fun getNextPage() { + pageLoad = true + createItems(20) + } + + private fun removeItem(position: Int){ + val item = adapter.currentList[position] + chatList.apply { + remove(item) + } + adapter.notifyItemRemoved(position) + } + + private fun createItems(size: Int){ + for (i in 1..size) { + chatList.add( + ChatItem( + lastId++, + "Name " + "$lastId", + "status " + "$lastId", + "Message " + "$lastId", + Random.nextInt(10), + Random.nextBoolean(), + Random.nextBoolean(), + Random.nextBoolean(), + Random.nextBoolean(), + Random.nextBoolean(), + "12:10" + ) + ) + } + adapter.submitList(chatList) + adapter.notifyDataSetChanged() } } \ No newline at end of file diff --git a/app/src/main/res/drawable/avatar.png b/app/src/main/res/drawable/avatar.png new file mode 100644 index 0000000..c96145f Binary files /dev/null and b/app/src/main/res/drawable/avatar.png differ diff --git a/app/src/main/res/drawable/check.xml b/app/src/main/res/drawable/check.xml new file mode 100644 index 0000000..e8b21b0 --- /dev/null +++ b/app/src/main/res/drawable/check.xml @@ -0,0 +1,2 @@ + + diff --git a/app/src/main/res/drawable/check1.xml b/app/src/main/res/drawable/check1.xml new file mode 100644 index 0000000..48b7d4d --- /dev/null +++ b/app/src/main/res/drawable/check1.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable/circle.xml b/app/src/main/res/drawable/circle.xml new file mode 100644 index 0000000..27cde7b --- /dev/null +++ b/app/src/main/res/drawable/circle.xml @@ -0,0 +1,2 @@ + + diff --git a/app/src/main/res/drawable/doublecheck.xml b/app/src/main/res/drawable/doublecheck.xml new file mode 100644 index 0000000..c71c8ba --- /dev/null +++ b/app/src/main/res/drawable/doublecheck.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable/mute.xml b/app/src/main/res/drawable/mute.xml new file mode 100644 index 0000000..dc59ee4 --- /dev/null +++ b/app/src/main/res/drawable/mute.xml @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/scam.xml b/app/src/main/res/drawable/scam.xml new file mode 100644 index 0000000..b77d9ab --- /dev/null +++ b/app/src/main/res/drawable/scam.xml @@ -0,0 +1,27 @@ + + + + + + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 2d026df..14b87f2 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -3,11 +3,15 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" + android:background="@color/white" + xmlns:app="http://schemas.android.com/apk/res-auto" tools:context=".MainActivity"> + android:layout_height="match_parent" + app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" + tools:listitem="@layout/chat_item" /> \ No newline at end of file diff --git a/app/src/main/res/layout/chat_item.xml b/app/src/main/res/layout/chat_item.xml new file mode 100644 index 0000000..92efb23 --- /dev/null +++ b/app/src/main/res/layout/chat_item.xml @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +