diff --git a/app/build.gradle b/app/build.gradle index 54e4eac..29b920c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -39,6 +39,8 @@ dependencies { implementation 'com.google.android.material:material:1.7.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' testImplementation 'junit:junit:4.13.2' + implementation 'androidx.recyclerview:recyclerview:1.3.2' + implementation 'it.xabaras.android:recyclerview-swipedecorator:1.4' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' } \ No newline at end of file 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..0b4e4d1 --- /dev/null +++ b/app/src/main/java/otus/gpb/recyclerview/ChatItem.kt @@ -0,0 +1,9 @@ +package otus.gpb.recyclerview + +data class ChatItem( + val id: Int, + val userImage: Int, + val date: String, + val username: String, + val message: String +) diff --git a/app/src/main/java/otus/gpb/recyclerview/ChatItemDataGenerator.kt b/app/src/main/java/otus/gpb/recyclerview/ChatItemDataGenerator.kt new file mode 100644 index 0000000..b4600b5 --- /dev/null +++ b/app/src/main/java/otus/gpb/recyclerview/ChatItemDataGenerator.kt @@ -0,0 +1,54 @@ +package otus.gpb.recyclerview + +object ChatItemDataGenerator { + + fun generateChatItemsList(count: Int): MutableList { + return MutableList(count) { + ChatItem( + id = it, + username = listOf( + "Dima Murantsev", + "Catbird", + "just design", + "R4IN80W", + "SMDDN", + "Midnight", + "Nikita Dmitriev", + "Evgenia Smud", + "MidNighT", + "bravo-social" + ).random(), + message = listOf( + "Hello", + "Good day", + "du biest mein sonnechein", + "How're you?", + "let's make it", + "in the evening, i suppose", + "a lot of fun", + "Have you seen this?", + "yep", + "don't think so", + "you won't believe" + ).random(), + userImage = listOf( + R.drawable.ic_avatar_1, + R.drawable.ic_avatar_2, + R.drawable.ic_avatar_3, + R.drawable.ic_avatar_4, + R.drawable.ic_avatar_5, + R.drawable.ic_avatar_6, + R.drawable.ic_avatar_7 + ).random(), + date = listOf( + "14:28", + "13:45", + "17:20", + "21:57", + "18:12" + ).random() + + ) + } + } +} diff --git a/app/src/main/java/otus/gpb/recyclerview/ChatListAdapter.kt b/app/src/main/java/otus/gpb/recyclerview/ChatListAdapter.kt new file mode 100644 index 0000000..bd961f1 --- /dev/null +++ b/app/src/main/java/otus/gpb/recyclerview/ChatListAdapter.kt @@ -0,0 +1,46 @@ +package otus.gpb.recyclerview + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView + +class ChatListAdapter : + ListAdapter(DiffChatItemCallback()) { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return ChatListViewHolder( + LayoutInflater.from(parent.context).inflate(R.layout.chat_item, parent, false) + ) + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + (holder as ChatListViewHolder).bind(currentList[position]) + } + + override fun getItemCount(): Int = currentList.size + + fun removeItem(fromPosition: Int) { + val list = currentList.toMutableList() + list.removeAt(fromPosition) + submitList(list) + } + + fun addItems(count: Int) { + val list = currentList.toMutableList() + list.addAll(ChatItemDataGenerator.generateChatItemsList(count)) + submitList(list) + } + + class DiffChatItemCallback : 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 + } + } +} \ No newline at end of file diff --git a/app/src/main/java/otus/gpb/recyclerview/ChatListViewHolder.kt b/app/src/main/java/otus/gpb/recyclerview/ChatListViewHolder.kt new file mode 100644 index 0000000..0ca5817 --- /dev/null +++ b/app/src/main/java/otus/gpb/recyclerview/ChatListViewHolder.kt @@ -0,0 +1,21 @@ +package otus.gpb.recyclerview + +import android.view.View +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.imageview.ShapeableImageView + +class ChatListViewHolder(private val view: View) : RecyclerView.ViewHolder(view) { + + private val userImage = view.findViewById(R.id.user_avatar_siv) + private val username = view.findViewById(R.id.username_tv) + private val message = view.findViewById(R.id.message_tv) + private val date = view.findViewById(R.id.published_date_tv) + + fun bind(item: ChatItem) { + userImage.setImageResource(item.userImage) + username.text = item.username + message.text = item.message + date.text = item.date + } +} \ No newline at end of file diff --git a/app/src/main/java/otus/gpb/recyclerview/ItemSwipeHelper.kt b/app/src/main/java/otus/gpb/recyclerview/ItemSwipeHelper.kt new file mode 100644 index 0000000..c8c38e3 --- /dev/null +++ b/app/src/main/java/otus/gpb/recyclerview/ItemSwipeHelper.kt @@ -0,0 +1,56 @@ +package otus.gpb.recyclerview + +import android.content.Context +import android.graphics.Canvas +import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.RecyclerView +import it.xabaras.android.recyclerview.swipedecorator.RecyclerViewSwipeDecorator + + +class ItemSwipeHelper(private val context: Context) : ItemTouchHelper.Callback() { + + override fun getMovementFlags( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder + ): Int = makeMovementFlags(0, ItemTouchHelper.LEFT) + + override fun onMove( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder, + target: RecyclerView.ViewHolder + ): Boolean = false + + override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { + viewHolder.chatAdapter.removeItem(viewHolder.absoluteAdapterPosition) + } + + 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 + ) + .addBackgroundColor(ContextCompat.getColor(context, R.color.pale_blue)) + .addActionIcon(R.drawable.ic_archive) + .create() + .decorate() + + super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive) + } + + private val RecyclerView.ViewHolder.chatAdapter: ChatListAdapter + get() = bindingAdapter as? ChatListAdapter ?: error("Not ChatListAdapter") +} \ No newline at end of file diff --git a/app/src/main/java/otus/gpb/recyclerview/MainActivity.kt b/app/src/main/java/otus/gpb/recyclerview/MainActivity.kt index e2cdca7..2c28707 100644 --- a/app/src/main/java/otus/gpb/recyclerview/MainActivity.kt +++ b/app/src/main/java/otus/gpb/recyclerview/MainActivity.kt @@ -1,12 +1,49 @@ package otus.gpb.recyclerview -import androidx.appcompat.app.AppCompatActivity import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.DividerItemDecoration +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView class MainActivity : AppCompatActivity() { + private lateinit var list: MutableList + private val adapter by lazy { ChatListAdapter() } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) + + val recyclerView: RecyclerView = findViewById(R.id.recyclerView) + val divider = DividerItemDecoration(this, DividerItemDecoration.VERTICAL) + divider.setDrawable( + ContextCompat.getDrawable(this, R.drawable.rv_item_divider) + ?: error("Not divider drawable") + ) + + recyclerView.addItemDecoration(divider) + ItemTouchHelper(ItemSwipeHelper(this)).attachToRecyclerView(recyclerView) + + list = ChatItemDataGenerator.generateChatItemsList(20) + recyclerView.adapter = adapter + adapter.submitList(list) + + recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { + + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + super.onScrolled(recyclerView, dx, dy) + + val lastPosition = + (recyclerView.layoutManager as LinearLayoutManager).findLastVisibleItemPosition() + val itemCount = adapter.itemCount + + if (lastPosition + 1 >= itemCount) { + adapter.addItems(5) + } + } + }) } } \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_archive.xml b/app/src/main/res/drawable/ic_archive.xml new file mode 100644 index 0000000..7438d9a --- /dev/null +++ b/app/src/main/res/drawable/ic_archive.xml @@ -0,0 +1,13 @@ + + + + diff --git a/app/src/main/res/drawable/ic_avatar_1.png b/app/src/main/res/drawable/ic_avatar_1.png new file mode 100644 index 0000000..b3451ba Binary files /dev/null and b/app/src/main/res/drawable/ic_avatar_1.png differ diff --git a/app/src/main/res/drawable/ic_avatar_2.png b/app/src/main/res/drawable/ic_avatar_2.png new file mode 100644 index 0000000..40fb0c4 Binary files /dev/null and b/app/src/main/res/drawable/ic_avatar_2.png differ diff --git a/app/src/main/res/drawable/ic_avatar_3.png b/app/src/main/res/drawable/ic_avatar_3.png new file mode 100644 index 0000000..301557d Binary files /dev/null and b/app/src/main/res/drawable/ic_avatar_3.png differ diff --git a/app/src/main/res/drawable/ic_avatar_4.png b/app/src/main/res/drawable/ic_avatar_4.png new file mode 100644 index 0000000..c8da0c7 Binary files /dev/null and b/app/src/main/res/drawable/ic_avatar_4.png differ diff --git a/app/src/main/res/drawable/ic_avatar_5.png b/app/src/main/res/drawable/ic_avatar_5.png new file mode 100644 index 0000000..5c49a88 Binary files /dev/null and b/app/src/main/res/drawable/ic_avatar_5.png differ diff --git a/app/src/main/res/drawable/ic_avatar_6.png b/app/src/main/res/drawable/ic_avatar_6.png new file mode 100644 index 0000000..378067e Binary files /dev/null and b/app/src/main/res/drawable/ic_avatar_6.png differ diff --git a/app/src/main/res/drawable/ic_avatar_7.png b/app/src/main/res/drawable/ic_avatar_7.png new file mode 100644 index 0000000..fa5818d Binary files /dev/null and b/app/src/main/res/drawable/ic_avatar_7.png differ diff --git a/app/src/main/res/drawable/ic_menu.xml b/app/src/main/res/drawable/ic_menu.xml new file mode 100644 index 0000000..963ca95 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_pencil.xml b/app/src/main/res/drawable/ic_pencil.xml new file mode 100644 index 0000000..e336f09 --- /dev/null +++ b/app/src/main/res/drawable/ic_pencil.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_pinned.xml b/app/src/main/res/drawable/ic_pinned.xml new file mode 100644 index 0000000..e270350 --- /dev/null +++ b/app/src/main/res/drawable/ic_pinned.xml @@ -0,0 +1,17 @@ + + + + + diff --git a/app/src/main/res/drawable/rv_item_divider.xml b/app/src/main/res/drawable/rv_item_divider.xml new file mode 100644 index 0000000..bfdcc05 --- /dev/null +++ b/app/src/main/res/drawable/rv_item_divider.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 2d026df..a2daf19 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,13 +1,44 @@ + + + + + + android:layout_height="0dp" + android:orientation="vertical" + app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" + app:layout_constraintTop_toBottomOf="@+id/main_activity_abl" + 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..0a00cfd --- /dev/null +++ b/app/src/main/res/layout/chat_item.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index f8c6127..e282770 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -7,4 +7,9 @@ #FF018786 #FF000000 #FFFFFFFF + #517DA2 + #66A9E0 + #416482 + #51AEE7 + #D9D9D9 \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3d78b1f..4ab5e83 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,3 +1,6 @@ RecyclerView + User avatar + Floating action button + RecyclerView \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..1504391 --- /dev/null +++ b/app/src/main/res/values/styles.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 2187cf1..8c131ef 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,14 +1,14 @@ -