我有一个 RecyclerView 与 Paging 一起实现,以从 Room Database 加载列表。当大小很小时,该列表可以正常工作。当大小达到 50 - 60 左右时,列表仍然可以正常工作,但是当我切换到另一个片段然后返回列表时,它会阻塞 UI 大约 1.5 - 2 秒,这在用户体验中非常沉闷(参见下面的 GIF ):
我的代码如下:
道
@Query("SELECT * FROM account_table WHERE userID = :userID")
fun getAll(userID: String): DataSource.Factory<Int, Account>
存储库
class AccountRepository private constructor(application: Application) {
private val database =
LockyDatabase.getDatabase(
application
)
private val accountDao = database.accountDao()
companion object {
@Volatile
private var instance: AccountRepository? = null
fun getInstance(application: Application) =
instance ?: synchronized(this) {
instance ?: AccountRepository(application).also { instance = it }
}
}
fun getAll(userID: String) = accountDao.getAll(userID)
}
适配器
class CredentialsPagingAdapter(
private val clickListener: ClickListener,
private val optionsClickListener: OptionsClickListener?,
private val isSimplified: Boolean
) : PagedListAdapter<Credentials, CredentialsViewHolder>(
diffCallback
) {
companion object {
private val diffCallback = object : DiffUtil.ItemCallback<Credentials>() {
override fun areItemsTheSame(oldItem: Credentials, newItem: Credentials): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: Credentials, newItem: Credentials): Boolean {
return oldItem.equals(newItem)
}
}
}
override fun onBindViewHolder(holder: CredentialsViewHolder, position: Int) {
holder.bind(
clickListener,
optionsClickListener,
getItem(position),
isSimplified
)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CredentialsViewHolder {
return CredentialsViewHolder.from(
parent
)
}
}
视图模型
val accounts = Transformations.switchMap(_sort) {
when (true) {
it.name -> _accounts.sortByEntryName
it.username -> _accounts.sortByUsername
it.email -> _accounts.sortByEmail
it.website -> _accounts.sortByWebsite
it.authType -> _accounts.sortByAuthenticationType
else -> _accounts
}.toLiveData(pageSize = resources.getInteger(R.integer.size_paging_list_default))
}
分段
private fun subscribeAccounts() {
val adapter = CredentialsPagingAdapter(
/* The click listener to handle account on clicks */
ClickListener {
navigateTo(
AccountFragmentDirections.actionFragmentAccountToFragmentViewAccount(
it as Account
)
)
},
/* The click listener to handle popup menu for each accounts */
OptionsClickListener { view, credential ->
view.apply {
isEnabled = false
}
createPopupMenu(view, credential as Account)
},
false
)
binding.RecyclerViewAccount.apply {
/*
* State that layout size will not change for better performance
*/
setHasFixedSize(true)
/* Bind the layout manager */
layoutManager = LinearLayoutManager(requireContext())
/* Bind the adapter */
this.adapter = adapter
}
viewModel.accounts.observe(viewLifecycleOwner, Observer {
if (it != null) {
/*
* If accounts is not null
* Load recyclerview and
* Update the ui
*/
lifecycleScope.launch {
adapter.submitList(it as PagedList<Credentials>)
}
updateUI(it.size)
}
})
}
主要活动布局
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<androidx.drawerlayout.widget.DrawerLayout
android:id="@+id/Drawer_Main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.main.main.MainActivity">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/Layout_Coordinator_Main"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/Toolbar_Main"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@drawable/custom_rounded_background_toolbar"
android:clipChildren="false"
android:outlineAmbientShadowColor="@color/colorShadowColor"
android:outlineSpotShadowColor="@color/colorShadowColor"
android:paddingStart="8dp"
android:paddingEnd="8dp"
app:contentInsetStartWithNavigation="0dp"
tools:targetApi="p">
...
</com.google.android.material.appbar.MaterialToolbar>
<androidx.core.widget.NestedScrollView
android:id="@+id/Nested_Scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="?attr/actionBarSize"
android:fillViewport="true">
<fragment
android:id="@+id/Navigation_Host"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/navigation_drawer_main"
tools:ignore="FragmentTagUsage" />
</androidx.core.widget.NestedScrollView>
<com.google.android.material.floatingactionbutton.FloatingActionButton ... />
<com.google.android.material.floatingactionbutton.FloatingActionButton ... />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.google.android.material.navigation.NavigationView
android:id="@+id/Navigation_View"
style="@style/Locky.Widget.Custom.NavigationView"
android:layout_width="280dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:clipToPadding="false"
android:paddingStart="0dp"
android:paddingEnd="16dp"
app:headerLayout="@layout/drawer_header"
app:itemTextAppearance="@style/Locky.Text.Body.Drawer"
app:menu="@menu/menu_drawer_main" />
</androidx.drawerlayout.widget.DrawerLayout>
</layout>
片段帐户布局:
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<import type="android.view.View" />
<import type="com.th3pl4gu3.locky_offline.repository.Loading.List" />
<variable
name="ViewModel"
type="com.th3pl4gu3.locky_offline.ui.main.main.account.AccountViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/Layout_Fragment_Account"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorOnSurface">
<!--
Recyclerview
-->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/RecyclerView_Account"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="@{ViewModel.loadingStatus==List.LIST ? View.VISIBLE : View.GONE}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="gone" />
<!--
Empty Views and group
-->
<androidx.constraintlayout.widget.Group
android:id="@+id/Empty_View"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="@{ViewModel.loadingStatus==List.EMPTY_VIEW ? View.VISIBLE : View.GONE}"
app:constraint_referenced_ids="Empty_View_Illustration,Empty_View_Title,Empty_View_Subtitle" />
<ImageView
android:id="@+id/Empty_View_Illustration" ... />
<TextView
android:id="@+id/Empty_View_Title" ... />
<TextView
android:id="@+id/Empty_View_Subtitle" ... />
<!--
Progress Bar
-->
<include
android:id="@+id/Progress_Bar"
layout="@layout/custom_view_list_loading"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="@{ViewModel.loadingStatus==List.LOADING ? View.VISIBLE : View.GONE}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Recyclerview 列表布局
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<import type="android.view.View" />
<variable
name="IsSimplifiedVersion"
type="Boolean" />
<variable
name="Credential"
type="com.th3pl4gu3.locky_offline.core.main.credentials.Credentials" />
<variable
name="ClickListener"
type="com.th3pl4gu3.locky_offline.ui.main.main.ClickListener" />
<variable
name="OptionsClickListener"
type="com.th3pl4gu3.locky_offline.ui.main.main.OptionsClickListener" />
</data>
<com.google.android.material.card.MaterialCardView
style="@style/Locky.ListCardView"
credentialCardConfiguration="@{Credential}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_marginEnd="8dp"
android:clickable="true"
android:focusable="true"
android:onClick="@{() -> ClickListener.onClick(Credential)}">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/Credential_Logo"
configureLogo="@{Credential}"
android:layout_width="56dp"
android:layout_height="56dp"
android:layout_marginEnd="16dp"
android:scaleType="centerCrop"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/Barrier_Logo"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/ic_locky_with_background_circle" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/Barrier_Logo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="end"
app:constraint_referenced_ids="Credential_Logo" />
<TextView
android:id="@+id/Credential_Entry_Name"
style="@style/Locky.Text.Title6.List"
listTitleMessageCardEligibility="@{Credential}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:ems="11"
android:singleLine="true"
android:text="@{Credential.entryName}"
app:layout_constraintBottom_toTopOf="@+id/Credential_First_Subtitle"
app:layout_constraintStart_toEndOf="@id/Barrier_Logo"
app:layout_constraintTop_toTopOf="@+id/Credential_Logo"
app:layout_constraintVertical_chainStyle="spread_inside"
tools:text="This is an entry name and it can be very very very long" />
<TextView
android:id="@+id/Credential_First_Subtitle"
style="@style/Locky.Text.Subtitle.List.Primary"
setCredentialSubtitle="@{Credential}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:ems="13"
android:singleLine="true"
app:layout_constraintBottom_toTopOf="@+id/Credential_Second_Subtitle"
app:layout_constraintStart_toStartOf="@+id/Credential_Entry_Name"
app:layout_constraintTop_toBottomOf="@+id/Credential_Entry_Name"
app:layout_constraintVertical_chainStyle="spread"
tools:text="This is the very first subtitle and this can be very long too" />
<TextView
android:id="@+id/Credential_Second_Subtitle"
style="@style/Locky.Text.Subtitle.List.Secondary"
setCredentialOtherSubtitle="@{Credential}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:ems="14"
android:singleLine="true"
android:textColor="@color/colorAccent"
app:layout_constraintBottom_toBottomOf="@+id/Credential_Logo"
app:layout_constraintStart_toStartOf="@+id/Credential_Entry_Name"
app:layout_constraintTop_toBottomOf="@+id/Credential_First_Subtitle"
app:layout_constraintVertical_chainStyle="spread"
tools:text="This is the second subtitle and this can be very long too" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/Barrier_More_Options"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="start"
app:constraint_referenced_ids="Credential_More_Options" />
<ImageButton
android:id="@+id/Credential_More_Options"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:background="@drawable/custom_states_background_button_image"
android:onClick="@{(view) -> OptionsClickListener.onClick(view, Credential)}"
android:scaleType="centerCrop"
android:src="@drawable/ic_more_options"
android:visibility="@{IsSimplifiedVersion ? View.GONE : View.VISIBLE}"
app:layout_constraintBottom_toBottomOf="@+id/Credential_Logo"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/Barrier_More_Options"
app:layout_constraintTop_toTopOf="@+id/Credential_Logo" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
</layout>
我的分页版本是 2.1.2
有人可以帮我解决这个问题。我尝试了几天寻找修复,但没有任何效果。
我很感激帮助。