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
3 changes: 3 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ 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 'com.github.javafaker:javafaker:1.0.2'
implementation 'com.github.bumptech.glide:glide:4.15.1'
implementation 'androidx.cardview:cardview:1.0.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
35 changes: 35 additions & 0 deletions app/src/main/java/otus/gpb/recyclerview/ChatTlgAdapter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package otus.gpb.recyclerview

import android.util.Log
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide

class ChatTlgAdapter( ):RecyclerView.Adapter<ChatTlgViewHolder>(){

private var ChatItemList = mutableListOf<ChatTlgItem>()
fun setList(list: List<ChatTlgItem>) {
ChatItemList = list.toList() as MutableList<ChatTlgItem>
notifyDataSetChanged()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

При каждом обновлении списка перерисовываются все элементы через notifyDataSetChanged. Если будем использовать ListAdapter с DiffUtil.ItemCallback, то обновит только измененные элементы и улучшит производительность.

}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChatTlgViewHolder {

val view = LayoutInflater.from(parent.context)
val binding = view.inflate( R.layout.item_tlg_chat, parent, false )
return ChatTlgViewHolder( binding )
}

override fun getItemCount(): Int {
return ChatItemList.size
}

override fun onBindViewHolder( chatViewHolder: ChatTlgViewHolder, position: Int) {
chatViewHolder.bind( ChatItemList[position] )
}
fun onItemSwipe( position:Int, direction:Int ){
ChatItemList.removeAt(position)
notifyItemRemoved(position)
}
}
50 changes: 50 additions & 0 deletions app/src/main/java/otus/gpb/recyclerview/ChatTlgService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package otus.gpb.recyclerview

import com.github.javafaker.Faker

data class ChatTlgItem(
// TODO There should be a real structure with Tg chat elements
val chatName: String, // Chat name
val messageAuthor: String, // Message author
val messageTitle: String, // Message fragment
val messageTime: String, // Message recv time
val messageCnt: Int, // Message count
val photo: String // Avatar
)

class ChatTlgService( private val isNewList: Boolean ){

private var itemsCnt:Int = 6
private var chatList = mutableListOf<ChatTlgItem>()

init {
if( isNewList ) itemsCnt = 20
val faker = Faker.instance()

chatList = (0..itemsCnt).map {
ChatTlgItem(
chatName = faker.name().fullName(),
messageAuthor = faker.name().fullName(),
messageTitle = faker.book().title(),
messageTime = faker.numerify("##:## AM"),//"11:25 AM",
messageCnt = faker.number().numberBetween(1,100),
photo = IMAGES[it % IMAGES.size],
)
}.toMutableList()
}

fun getTlgChat() : List<ChatTlgItem>{
return chatList
}

companion object {
private val IMAGES = mutableListOf(
"avatar_1",
"avatar_2",
"avatar_3",
"avatar_4",
"avatar_5",
"avatar_6"
)
}
}
35 changes: 35 additions & 0 deletions app/src/main/java/otus/gpb/recyclerview/ChatTlgSwipeDelete.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package otus.gpb.recyclerview

import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView

class TlgItemTouchHelper( val adapter: ChatTlgAdapter ) : ItemTouchHelper.Callback() {

override fun getMovementFlags(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder
): Int {
val swipeFlags = ItemTouchHelper.START
return makeMovementFlags(0 , swipeFlags)
}

override fun isItemViewSwipeEnabled(): Boolean {
return true
}

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

override fun onSwiped(
viewHolder: RecyclerView.ViewHolder,
direction: Int
) {
val itemPosition:Int = viewHolder.adapterPosition
adapter.onItemSwipe( itemPosition, direction )

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Если добавить проверку позиции перед использованием, будет безопаснее при быстрых свайпах

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

import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import org.apache.commons.lang3.ClassUtils.getPackageName

class ChatTlgViewHolder( private val view : View ) : RecyclerView.ViewHolder( view ){

private val image: ImageView by lazy { view.findViewById(R.id.imageView) }
private val chatName: TextView by lazy { view.findViewById(R.id.chatNameTextView) }
private val messAuthor: TextView by lazy { view.findViewById(R.id.authorTextView) }
private val messageText: TextView by lazy { view.findViewById(R.id.messageTextView) }
private val messageTime: TextView by lazy { view.findViewById(R.id.timeTextView) }
private val messageCnt: TextView by lazy { view.findViewById(R.id.cntMessTextView) }


fun bind( chatTlgItem: ChatTlgItem ) {

chatName.text = chatTlgItem.chatName
messAuthor.text = chatTlgItem.messageAuthor
messageText.text = chatTlgItem.messageTitle
messageTime.text = chatTlgItem.messageTime
messageCnt.text = chatTlgItem.messageCnt.toString()

image.setImageResource(R.drawable.ic_person)

val resourceName = chatTlgItem.photo
val res = view.context.getResources()
val packageName = view.context.packageName
val resourceId = res.getIdentifier(resourceName, "drawable", packageName )
if (resourceId != 0) {

image.setImageResource(resourceId)
/*
Glide.with(view.context)
.load(resourceId)
.skipMemoryCache(false)
.diskCacheStrategy(DiskCacheStrategy.ALL )
.circleCrop()
.into(image)
*/
}
}
}
59 changes: 48 additions & 11 deletions app/src/main/java/otus/gpb/recyclerview/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,49 @@
package otus.gpb.recyclerview

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
package otus.gpb.recyclerview

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

class MainActivity : AppCompatActivity() {

private var ChatTlgList:List<ChatTlgItem> = mutableListOf()
private lateinit var chatTlgAdapter:ChatTlgAdapter
private lateinit var dividerItemDecoration : DividerItemDecoration

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

ChatTlgList = ChatTlgService( true ).getTlgChat()
chatTlgAdapter = ChatTlgAdapter( )

val manager = LinearLayoutManager(this) // LayoutManager
val rv = findViewById<RecyclerView>(R.id.recyclerView)
rv.adapter = chatTlgAdapter
rv.layoutManager = manager
dividerItemDecoration = DividerItemDecoration(
rv.context,
(rv.layoutManager as LinearLayoutManager).orientation
)
rv.addItemDecoration(dividerItemDecoration)

chatTlgAdapter.setList( ChatTlgList )

var callback = TlgItemTouchHelper( chatTlgAdapter )
var itemTouchHelper = ItemTouchHelper( callback )
itemTouchHelper.attachToRecyclerView( rv )
rv.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (!recyclerView.canScrollVertically(1)) {
var chatAddList = ChatTlgService( false )
ChatTlgList = ChatTlgList + chatAddList.getTlgChat()
chatTlgAdapter.setList( ChatTlgList )

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

При достижении конца списка onScrolled может вызываться несколько раз подряд. Флаг isLoading для защиты от повторных вызовов будет хорошо смотреться

}
}
})
}
}
Binary file added app/src/main/res/drawable/avatar_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable/avatar_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable/avatar_3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable/avatar_4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable/avatar_5.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable/avatar_6.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable/avatar_7.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions app/src/main/res/drawable/ic_person.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="50dp"
android:height="50dp"
android:tint="@color/grey"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10s10,-4.48 10,-10S17.52,2 12,2zM12,6c1.93,0 3.5,1.57 3.5,3.5S13.93,13 12,13s-3.5,-1.57 -3.5,-3.5S10.07,6 12,6zM12,20c-2.03,0 -4.43,-0.82 -6.14,-2.88C7.55,15.8 9.68,15 12,15s4.45,0.8 6.14,2.12C16.43,19.18 14.03,20 12,20z" />
</vector>
Binary file added app/src/main/res/drawable/menu.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions app/src/main/res/drawable/round_shape.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#0085FD" />
<padding
android:left="10dp"
android:top="5dp"
android:right="10dp"
android:bottom="5dp" />
</shape>
49 changes: 37 additions & 12 deletions app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
@@ -1,13 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize"
android:theme="?attr/actionBarTheme"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:logo="@drawable/menu"
app:title="Telegram"
app:titleMargin="4dp"
app:titleMarginEnd="20dp"
app:titleMarginStart="50dp"
app:titleTextAppearance="@style/TextAppearance.AppCompat.Body1"
app:titleTextColor="#F6FFFFFF" />

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="55dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toolbar3" />

</androidx.constraintlayout.widget.ConstraintLayout>
Loading