diff --git a/app/build.gradle b/app/build.gradle index 54e4eac..bb0b36d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -41,4 +41,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.recyclerview:recyclerview:1.3.2' + 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..121e601 --- /dev/null +++ b/app/src/main/java/otus/gpb/recyclerview/ChatAdapter.kt @@ -0,0 +1,45 @@ +package otus.gpb.recyclerview + +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil + +class ChatAdapter : ListAdapter (DiffUtilItem()) { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChatViewHolder { + return ChatViewHolder( + LayoutInflater.from(parent.context).inflate(R.layout.chat_item, parent, false) + ) + } + + override fun onBindViewHolder(viewHolder: ChatViewHolder, position: Int) { + viewHolder.bind(currentList[position]) + } + + fun removeItem(position: Int){ + val list = currentList.toMutableList() + list.removeAt(position) + submitList(list) + notifyDataSetChanged() + } + + fun addItems(count: Int) { + val list = currentList.toMutableList() + list.addAll(ChatDataGenerator.generateChatItemsList(count)) + submitList(list) + notifyDataSetChanged() + } + + class DiffUtilItem : 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/ChatDataGenerator.kt b/app/src/main/java/otus/gpb/recyclerview/ChatDataGenerator.kt new file mode 100644 index 0000000..9f6d228 --- /dev/null +++ b/app/src/main/java/otus/gpb/recyclerview/ChatDataGenerator.kt @@ -0,0 +1,52 @@ +package otus.gpb.recyclerview + +object ChatDataGenerator { + + private var lastId = 0 + fun generateChatItemsList(count: Int): MutableList { + return MutableList(count) { + ChatItem( + id = lastId++, + username = listOf( + "Anton Karavaev", + "MyWife", + "George", + "AK-47", + "Chernomor", + "Cat", + "Donald Trump", + "Paul McCartney", + "John Lennon", + "Eddie Van Halen" + ).random(), + message = listOf( + "Hello", + "Good day", + "Здоровеньки булы...", + "How're you?", + "Шалом алейхем!", + "Хай, пипл!" + ).random(), + userImage = listOf( + R.drawable.avatar_1, + R.drawable.avatar_2, + R.drawable.avatar_3, + R.drawable.avatar_4, + R.drawable.avatar_5, + R.drawable.avatar_6, + R.drawable.avatar_7, + R.drawable.avatar_8, + R.drawable.avatar_9, + R.drawable.avatar_10 + ).random(), + date = listOf( + "14:28", + "13:45", + "17:20", + "21:57", + "18:12" + ).random() + ) + } + } +} \ 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..ed15a8b --- /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 +) \ No newline at end of file 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..58736c8 --- /dev/null +++ b/app/src/main/java/otus/gpb/recyclerview/ChatViewHolder.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 ChatViewHolder(private val view: View) : RecyclerView.ViewHolder(view) { + + private val userImage = view.findViewById(R.id.user_avatar) + private val username = view.findViewById(R.id.username) + private val message = view.findViewById(R.id.message) + private val date = view.findViewById(R.id.published_date) + + fun bind(item: ChatItem) { + userImage.setImageResource(item.userImage) + username.text = item.username + message.text = item.message + date.text = item.date + } +} diff --git a/app/src/main/java/otus/gpb/recyclerview/ItemTouchHelperCallback.kt b/app/src/main/java/otus/gpb/recyclerview/ItemTouchHelperCallback.kt new file mode 100644 index 0000000..2ebca6b --- /dev/null +++ b/app/src/main/java/otus/gpb/recyclerview/ItemTouchHelperCallback.kt @@ -0,0 +1,30 @@ +package otus.gpb.recyclerview + +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.RecyclerView + +class ItemTouchHelperCallback() : 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.bindingAdapterPosition) + } + + override fun getSwipeThreshold(viewHolder: RecyclerView.ViewHolder): Float { + return 0.50f + } + + private val RecyclerView.ViewHolder.chatAdapter: ChatAdapter + get() = bindingAdapter as ChatAdapter + +} \ 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..9df275a 100644 --- a/app/src/main/java/otus/gpb/recyclerview/MainActivity.kt +++ b/app/src/main/java/otus/gpb/recyclerview/MainActivity.kt @@ -2,11 +2,53 @@ package otus.gpb.recyclerview import androidx.appcompat.app.AppCompatActivity import android.os.Bundle +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 { ChatAdapter() } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) + + val recyclerView: RecyclerView = findViewById(R.id.recyclerView) + + val layoutManager = recyclerView.layoutManager as LinearLayoutManager + + val divider = DividerItemDecoration(this, DividerItemDecoration.VERTICAL) + divider.setDrawable( + ContextCompat.getDrawable(this, R.drawable.divider) + ?: error("Not divider drawable") + ) + + recyclerView.addItemDecoration(divider) + + ItemTouchHelper(ItemTouchHelperCallback()).attachToRecyclerView(recyclerView) + + list = ChatDataGenerator.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 totalItemCount = layoutManager.itemCount + val lastVisibleItem = layoutManager.findLastVisibleItemPosition() + + if (lastVisibleItem + 1 == totalItemCount) { + adapter.addItems(5) + } + } + }) } } \ No newline at end of file diff --git a/app/src/main/res/drawable/avatar_1.xml b/app/src/main/res/drawable/avatar_1.xml new file mode 100644 index 0000000..bd51ae0 --- /dev/null +++ b/app/src/main/res/drawable/avatar_1.xml @@ -0,0 +1,40 @@ + + + + + + + + + + diff --git a/app/src/main/res/drawable/avatar_10.xml b/app/src/main/res/drawable/avatar_10.xml new file mode 100644 index 0000000..ad50a0f --- /dev/null +++ b/app/src/main/res/drawable/avatar_10.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/avatar_2.xml b/app/src/main/res/drawable/avatar_2.xml new file mode 100644 index 0000000..5cb6f6c --- /dev/null +++ b/app/src/main/res/drawable/avatar_2.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable/avatar_3.xml b/app/src/main/res/drawable/avatar_3.xml new file mode 100644 index 0000000..8676502 --- /dev/null +++ b/app/src/main/res/drawable/avatar_3.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/avatar_4.xml b/app/src/main/res/drawable/avatar_4.xml new file mode 100644 index 0000000..9b7d1fd --- /dev/null +++ b/app/src/main/res/drawable/avatar_4.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/avatar_5.xml b/app/src/main/res/drawable/avatar_5.xml new file mode 100644 index 0000000..c012900 --- /dev/null +++ b/app/src/main/res/drawable/avatar_5.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/avatar_6.xml b/app/src/main/res/drawable/avatar_6.xml new file mode 100644 index 0000000..8f028ba --- /dev/null +++ b/app/src/main/res/drawable/avatar_6.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable/avatar_7.xml b/app/src/main/res/drawable/avatar_7.xml new file mode 100644 index 0000000..8d97281 --- /dev/null +++ b/app/src/main/res/drawable/avatar_7.xml @@ -0,0 +1,40 @@ + + + + + + + + + + diff --git a/app/src/main/res/drawable/avatar_8.xml b/app/src/main/res/drawable/avatar_8.xml new file mode 100644 index 0000000..48ccc43 --- /dev/null +++ b/app/src/main/res/drawable/avatar_8.xml @@ -0,0 +1,40 @@ + + + + + + + + + + diff --git a/app/src/main/res/drawable/avatar_9.xml b/app/src/main/res/drawable/avatar_9.xml new file mode 100644 index 0000000..653dc6a --- /dev/null +++ b/app/src/main/res/drawable/avatar_9.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/divider.xml b/app/src/main/res/drawable/divider.xml new file mode 100644 index 0000000..cfbd58c --- /dev/null +++ b/app/src/main/res/drawable/divider.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/menu.xml b/app/src/main/res/drawable/menu.xml new file mode 100644 index 0000000..41c43ac --- /dev/null +++ b/app/src/main/res/drawable/menu.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/pencil.xml b/app/src/main/res/drawable/pencil.xml new file mode 100644 index 0000000..37e153a --- /dev/null +++ b/app/src/main/res/drawable/pencil.xml @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/pinned.xml b/app/src/main/res/drawable/pinned.xml new file mode 100644 index 0000000..5cddb8f --- /dev/null +++ b/app/src/main/res/drawable/pinned.xml @@ -0,0 +1,17 @@ + + + + + \ 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..e8c6024 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,13 +1,37 @@ + + + + android:layout_height="0dp" + android:orientation="vertical" + app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" + app:layout_constraintTop_toBottomOf="@+id/main_activity_toolbar" + 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..d6997bc --- /dev/null +++ b/app/src/main/res/layout/chat_item.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + \ 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..7347adb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,3 +1,3 @@ - RecyclerView + Telegram \ 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..e565258 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,12 +1,12 @@ -