بالتأكيد عمل كل مطور أندرويد مع قوائم باستخدام RecyclerView. كما تمكن الكثيرون من رؤية كيفية تنظيم ترقيم الصفحات في القائمة باستخدام مكتبة Paging Library من Android Architecture Components.
الأمر بسيط: قم بتعيين PositionalDataSource ، وقم بتعيين التهيئة ، وإنشاء قائمة PagedList وإطعامها كلها باستخدام المحول و DiffUtilCallback إلى RecyclerView الخاص بنا.
ولكن ماذا لو كان لدينا العديد من مصادر البيانات؟ على سبيل المثال ، نريد أن يكون لدينا ذاكرة تخزين مؤقت في الغرفة ونستقبل البيانات من الشبكة.
تبين أن الحالة مخصصة تمامًا ولا توجد معلومات كثيرة حول هذا الموضوع على الإنترنت. سأحاول إصلاحه وإظهار كيف يمكن حل مثل هذه الحالة.

إذا كنت لا تزال غير معتاد على تنفيذ ترقيم الصفحات بمصدر بيانات واحد ، فإنني أنصحك بالتعرف على ذلك قبل قراءة المقالة.
كيف سيبدو الحل بدون ترقيم الصفحات:
- الوصول إلى ذاكرة التخزين المؤقت (في حالتنا ، إنها قاعدة بيانات)
- إذا كانت ذاكرة التخزين المؤقت فارغة - أرسل طلبًا إلى الخادم
- نتلقى البيانات من الخادم
- نعرضها في ورقة
- الكتابة إلى ذاكرة التخزين المؤقت
- إذا كان هناك ذاكرة تخزين مؤقت - اعرضها في القائمة
- نحصل على أحدث البيانات من الخادم
- نعرضها في القائمة ○
- الكتابة إلى ذاكرة التخزين المؤقت

شيء ملائم مثل ترقيم الصفحات ، الذي يبسط حياة المستخدمين ، هنا يعقدنا. دعونا نحاول تخيل المشاكل التي قد تنشأ عند تنفيذ قائمة مقسمة إلى صفحات بمصادر بيانات متعددة.
الخوارزمية هي تقريبًا ما يلي:
- الحصول على بيانات ذاكرة التخزين المؤقت للصفحة الأولى
- إذا كانت ذاكرة التخزين المؤقت فارغة ، نحصل على بيانات الخادم ونعرضها في القائمة ونكتب إلى قاعدة البيانات
- إذا كان هناك ذاكرة تخزين مؤقت ، فقم بتحميلها في القائمة
- إذا وصلنا إلى نهاية قاعدة البيانات ، فإننا نطلب البيانات من الخادم ، ونعرضها
- في القائمة والكتابة إلى قاعدة البيانات
من ميزات هذا النهج ، يمكنك أن ترى أنه لعرض القائمة ، يتم أولاً مسح ذاكرة التخزين المؤقت ، وإشارة تحميل البيانات الجديدة هي نهاية ذاكرة التخزين المؤقت.

فكرت Google في الأمر وابتكرت حلاً يخرج من صندوق PagingLibrary - BoundaryCallback.
تقارير BoundaryCallback عندما "ينتهي" مصدر البيانات المحلية ويخطر المستودع لتنزيل البيانات الجديدة.

يحتوي موقع Android Dev الرسمي على رابط إلى مستودع يحتوي على مثال لمشروع يستخدم قائمة ترقيم صفحات مع مصدرين للبيانات: Network (Retrofit 2) + Database (Room). من أجل فهم أفضل لكيفية عمل هذا النظام ، دعنا نحاول تحليل هذا المثال وتبسيطه قليلاً.
لنبدأ بطبقة البيانات. إنشاء اثنين من مصدر البيانات.
واجهة RedditApi.ktimport com.memebattle.pagingwithrepository.domain.model.RedditPost import retrofit2.Call import retrofit2.http.GET import retrofit2.http.Path import retrofit2.http.Query interface RedditApi { @GET("/r/{subreddit}/hot.json") fun getTop( @Path("subreddit") subreddit: String, @Query("limit") limit: Int): Call<ListingResponse>
تصف هذه الواجهة طلبات واجهة برمجة تطبيقات Reddit وفئات النماذج (ListingResponse و ListingData و RedditChildrenResponse) التي سيتم طي ردود واجهة برمجة التطبيقات عليها.
وعلى الفور جعل نموذجا للتحديث والغرفة
RedditPost.kt import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.Index import androidx.room.PrimaryKey import com.google.gson.annotations.SerializedName @Entity(tableName = "posts", indices = [Index(value = ["subreddit"], unique = false)]) data class RedditPost( @PrimaryKey @SerializedName("name") val name: String, @SerializedName("title") val title: String, @SerializedName("score") val score: Int, @SerializedName("author") val author: String, @SerializedName("subreddit")
فئة RedditDb.kt التي ترث RoomDatabase.
Redditdb.kt import androidx.room.Database import androidx.room.RoomDatabase import com.memebattle.pagingwithrepository.domain.model.RedditPost @Database( entities = [RedditPost::class], version = 1, exportSchema = false ) abstract class RedditDb : RoomDatabase() { abstract fun posts(): RedditPostDao }
تذكر أن إنشاء فئة RoomDatabase في كل مرة لتنفيذ استعلام إلى قاعدة البيانات أمر مكلف للغاية ، لذلك في حالة حقيقية ، قم بإنشائه مرة واحدة طوال عمر التطبيق!
وفئة داو مع استعلامات قاعدة البيانات RedditPostDao.kt
RedditPostDao.kt import androidx.paging.DataSource import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query import com.memebattle.pagingwithrepository.domain.model.RedditPost @Dao interface RedditPostDao { @Insert(onConflict = OnConflictStrategy.REPLACE) fun insert(posts : List<RedditPost>) @Query("SELECT * FROM posts WHERE subreddit = :subreddit ORDER BY indexInResponse ASC") fun postsBySubreddit(subreddit : String) : DataSource.Factory<Int, RedditPost> @Query("DELETE FROM posts WHERE subreddit = :subreddit") fun deleteBySubreddit(subreddit: String) @Query("SELECT MAX(indexInResponse) + 1 FROM posts WHERE subreddit = :subreddit") fun getNextIndexInSubreddit(subreddit: String) : Int }
ربما لاحظت أن طريقة postBySubreddit لاسترداد ما بعد تعود
مصدر البيانات. يعد ذلك ضروريًا لإنشاء قائمة PagedList الخاصة بنا باستخدام
LivePagedListBuilder في موضوع الخلفية. يمكنك قراءة المزيد عن هذا في
الدرس .
عظيم ، طبقة البيانات جاهزة. ننتقل إلى طبقة منطق الأعمال ، لتطبيق نمط المستودع ، من المعتاد إنشاء واجهة مستودع بشكل منفصل عن تنفيذه. لذلك ، سنقوم بإنشاء الواجهة RedditPostRepository.kt
RedditPostRepository.kt interface RedditPostRepository { fun postsOfSubreddit(subReddit: String, pageSize: Int): Listing<RedditPost> }
وعلى الفور السؤال - أي نوع من الإدراج؟ هذه هي فئة التاريخ المطلوبة لعرض القائمة.
Listing.kt import androidx.lifecycle.LiveData import androidx.paging.PagedList import com.memebattle.pagingwithrepository.domain.repository.network.NetworkState data class Listing<T>( // the LiveData of paged lists for the UI to observe val pagedList: LiveData<PagedList<T>>, // represents the network request status to show to the user val networkState: LiveData<NetworkState>, // represents the refresh status to show to the user. Separate from networkState, this // value is importantly only when refresh is requested. val refreshState: LiveData<NetworkState>, // refreshes the whole data and fetches it from scratch. val refresh: () -> Unit, // retries any failed requests. val retry: () -> Unit)
إنشاء تطبيق لمستودع MainRepository.kt
MainRepository.kt import android.content.Context import androidx.annotation.MainThread import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.Transformations import com.android.example.paging.pagingwithnetwork.reddit.api.RedditApi import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory import androidx.room.Room import com.android.example.paging.pagingwithnetwork.reddit.db.RedditDb import com.android.example.paging.pagingwithnetwork.reddit.db.RedditPostDao import com.memebattle.pagingwithrepository.domain.model.RedditPost import retrofit2.Call import retrofit2.Callback import retrofit2.Response import java.util.concurrent.Executors import androidx.paging.LivePagedListBuilder import com.memebattle.pagingwithrepository.domain.repository.core.Listing import com.memebattle.pagingwithrepository.domain.repository.boundary.SubredditBoundaryCallback import com.memebattle.pagingwithrepository.domain.repository.network.NetworkState import com.memebattle.pagingwithrepository.domain.repository.core.RedditPostRepository class MainRepository(context: Context) : RedditPostRepository { private var retrofit: Retrofit = Retrofit.Builder() .baseUrl("https://www.reddit.com/")
دعونا نرى ما يحدث في مستودعنا.
إنشاء مثيلات من واجهات مصدر البيانات والوصول إلى البيانات. لقاعدة البيانات:
RoomDatabase و Dao ، للشبكة: واجهة تحديثية و api.
بعد ذلك ، ننفذ طريقة التخزين المطلوبة
fun postsOfSubreddit(subReddit: String, pageSize: Int): Listing<RedditPost>
الذي ينشئ ترقيم الصفحات:
- إنشاء SubRedditBoundaryCallback الذي يرث PagedList.BoundaryCallback <>
- نحن نستخدم المُنشئ مع المعلمات ونمرر كل ما هو مطلوب لكي يعمل BoundaryCallback
- قم بإنشاء مشغل RefreshTe لإعلام المستودع بالحاجة إلى تحديث البيانات
- إنشاء وإرجاع كائن قائمة
في كائن القائمة:
- livePagedList
- networkState - حالة الشبكة
- إعادة المحاولة - رد الاتصال لاسترداد البيانات من الخادم
- تحديث - مشغل لتحديث البيانات
- updState - حالة عملية التحديث
ننفذ طريقة مساعدة
private fun insertResultIntoDb(subredditName: String, body: RedditApi.ListingResponse?)
لتسجيل استجابة الشبكة في قاعدة البيانات. سيتم استخدامه عندما تحتاج إلى تحديث القائمة أو كتابة جزء جديد من البيانات.
ننفذ طريقة مساعدة
private fun refresh(subredditName: String): LiveData<NetworkState>
لمشغل تحديث البيانات. كل شيء بسيط للغاية هنا: نحصل على البيانات من الخادم وننظف قاعدة البيانات ونكتب بيانات جديدة إلى قاعدة البيانات.
اكتشفنا المستودع. الآن دعونا نلقي نظرة فاحصة على SubredditBoundaryCallback.
SubredditBoundaryCallback.kt import androidx.paging.PagedList import androidx.annotation.MainThread import com.android.example.paging.pagingwithnetwork.reddit.api.RedditApi import com.memebattle.pagingwithrepository.domain.model.RedditPost import retrofit2.Call import retrofit2.Callback import retrofit2.Response import java.util.concurrent.Executor import com.memebattle.pagingwithrepository.domain.util.PagingRequestHelper import com.memebattle.pagingwithrepository.domain.repository.network.createStatusLiveData class SubredditBoundaryCallback( private val subredditName: String, private val webservice: RedditApi, private val handleResponse: (String, RedditApi.ListingResponse?) -> Unit, private val ioExecutor: Executor, private val networkPageSize: Int) : PagedList.BoundaryCallback<RedditPost>() { val helper = PagingRequestHelper(ioExecutor) val networkState = helper.createStatusLiveData() @MainThread override fun onZeroItemsLoaded() { helper.runIfNotRunning(PagingRequestHelper.RequestType.INITIAL) { webservice.getTop( subreddit = subredditName, limit = networkPageSize) .enqueue(createWebserviceCallback(it)) } } @MainThread override fun onItemAtEndLoaded(itemAtEnd: RedditPost) { helper.runIfNotRunning(PagingRequestHelper.RequestType.AFTER) { webservice.getTopAfter( subreddit = subredditName, after = itemAtEnd.name, limit = networkPageSize) .enqueue(createWebserviceCallback(it)) } } private fun insertItemsIntoDb( response: Response<RedditApi.ListingResponse>, it: PagingRequestHelper.Request.Callback) { ioExecutor.execute { handleResponse(subredditName, response.body()) it.recordSuccess() } } override fun onItemAtFrontLoaded(itemAtFront: RedditPost) {
هناك عدة طرق مطلوبة في الفصل ترث BoundaryCallback:
override fun onZeroItemsLoaded()
يتم استدعاء الطريقة عندما تكون قاعدة البيانات فارغة ، وهنا يجب علينا تلبية طلب إلى الخادم للحصول على الصفحة الأولى.
override fun onItemAtEndLoaded(itemAtEnd: RedditPost)
يتم استدعاء الطريقة عندما يصل "المكرر" إلى "قاع" قاعدة البيانات ، وهنا يجب علينا الاستعلام عن الخادم للحصول على الصفحة التالية ، لتمرير المفتاح الذي سيخرج به الخادم البيانات مباشرة بعد السجل الأخير من المتجر المحلي.
override fun onItemAtFrontLoaded(itemAtFront: RedditPost)
يتم استدعاء الطريقة عندما يصل "المكرر" إلى العنصر الأول من متجرنا. لتنفيذ قضيتنا ، يمكننا تجاهل تنفيذ هذه الطريقة.
أضف رد اتصال لاستلام البيانات ونقلها بشكل أكبر
fun createWebserviceCallback(it: PagingRequestHelper.Request.Callback) : Callback<RedditApi.ListingResponse>
نضيف طريقة تسجيل البيانات المستلمة في قاعدة البيانات
insertItemsIntoDb( response: Response<RedditApi.ListingResponse>, it: PagingRequestHelper.Request.Callback)
ما هو مساعد PagingRequestHelper؟ هذه فئة صحية قدمتها لنا Google بلطف وتقدمها لوضعها في المكتبة ، ولكننا فقط ننسخها في حزمة الطبقة المنطقية.
PagingRequestHelper.kt package com.memebattle.pagingwithrepository.domain.util; import java.util.Arrays; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; import androidx.annotation.AnyThread; import androidx.annotation.GuardedBy; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.paging.DataSource;
PagingRequestHelperExt.kt import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import com.memebattle.pagingwithrepository.domain.util.PagingRequestHelper private fun getErrorMessage(report: PagingRequestHelper.StatusReport): String { return PagingRequestHelper.RequestType.values().mapNotNull { report.getErrorFor(it)?.message }.first() } fun PagingRequestHelper.createStatusLiveData(): LiveData<NetworkState> { val liveData = MutableLiveData<NetworkState>() addListener { report -> when { report.hasRunning() -> liveData.postValue(NetworkState.LOADING) report.hasError() -> liveData.postValue( NetworkState.error(getErrorMessage(report))) else -> liveData.postValue(NetworkState.LOADED) } } return liveData }
, .
MVVM Google ViewModel LiveData.
MainActivity.kt import android.os.Bundle import android.view.KeyEvent import android.view.inputmethod.EditorInfo import androidx.lifecycle.Observer import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProviders import androidx.paging.PagedList import com.memebattle.pagingwithrepository.R import com.memebattle.pagingwithrepository.domain.model.RedditPost import com.memebattle.pagingwithrepository.domain.repository.MainRepository import com.memebattle.pagingwithrepository.domain.repository.network.NetworkState import com.memebattle.pagingwithrepository.presentation.recycler.PostsAdapter import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() { companion object { const val KEY_SUBREDDIT = "subreddit" const val DEFAULT_SUBREDDIT = "androiddev" } lateinit var model: MainViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) model = getViewModel() initAdapter() initSwipeToRefresh() initSearch() val subreddit = savedInstanceState?.getString(KEY_SUBREDDIT) ?: DEFAULT_SUBREDDIT model.showSubReddit(subreddit) } private fun getViewModel(): MainViewModel { return ViewModelProviders.of(this, object : ViewModelProvider.Factory { override fun <T : ViewModel?> create(modelClass: Class<T>): T { val repo = MainRepository(this@MainActivity) @Suppress("UNCHECKED_CAST") return MainViewModel(repo) as T } })[MainViewModel::class.java] } private fun initAdapter() { val adapter = PostsAdapter { model.retry() } list.adapter = adapter model.posts.observe(this, Observer<PagedList<RedditPost>> { adapter.submitList(it) }) model.networkState.observe(this, Observer { adapter.setNetworkState(it) }) } private fun initSwipeToRefresh() { model.refreshState.observe(this, Observer { swipe_refresh.isRefreshing = it == NetworkState.LOADING }) swipe_refresh.setOnRefreshListener { model.refresh() } } override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putString(KEY_SUBREDDIT, model.currentSubreddit()) } private fun initSearch() { input.setOnEditorActionListener { _, actionId, _ -> if (actionId == EditorInfo.IME_ACTION_GO) { updatedSubredditFromInput() true } else { false } } input.setOnKeyListener { _, keyCode, event -> if (event.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER) { updatedSubredditFromInput() true } else { false } } } private fun updatedSubredditFromInput() { input.text.trim().toString().let { if (it.isNotEmpty()) { if (model.showSubReddit(it)) { list.scrollToPosition(0) (list.adapter as? PostsAdapter)?.submitList(null) } } } } }
onCreate ViewModel, , .
LiveData ViewModel, .
MainViewModel.kt import androidx.lifecycle.MutableLiveData import androidx.lifecycle.Transformations import androidx.lifecycle.ViewModel import com.memebattle.pagingwithrepository.domain.repository.core.RedditPostRepository class MainViewModel(private val repository: RedditPostRepository) : ViewModel() { private val subredditName = MutableLiveData<String>() private val repoResult = Transformations.map(subredditName) { repository.postsOfSubreddit(it, 10) } val posts = Transformations.switchMap(repoResult) { it.pagedList }!! val networkState = Transformations.switchMap(repoResult) { it.networkState }!! val refreshState = Transformations.switchMap(repoResult) { it.refreshState }!! fun refresh() { repoResult.value?.refresh?.invoke() } fun showSubReddit(subreddit: String): Boolean { if (subredditName.value == subreddit) { return false } subredditName.value = subreddit return true } fun retry() { val listing = repoResult?.value listing?.retry?.invoke() } fun currentSubreddit(): String? = subredditName.value }
, : retry refesh.
PagedListAdapter. .
PostAdapter.kt import androidx.paging.PagedListAdapter import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView import android.view.ViewGroup import com.memebattle.pagingwithrepository.R import com.memebattle.pagingwithrepository.domain.model.RedditPost import com.memebattle.pagingwithrepository.domain.repository.network.NetworkState import com.memebattle.pagingwithrepository.presentation.recycler.viewholder.NetworkStateItemViewHolder import com.memebattle.pagingwithrepository.presentation.recycler.viewholder.RedditPostViewHolder class PostsAdapter( private val retryCallback: () -> Unit) : PagedListAdapter<RedditPost, RecyclerView.ViewHolder>(POST_COMPARATOR) { private var networkState: NetworkState? = null override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { when (getItemViewType(position)) { R.layout.reddit_post_item -> (holder as RedditPostViewHolder).bind(getItem(position)) R.layout.network_state_item -> (holder as NetworkStateItemViewHolder).bindTo( networkState) } } override fun onBindViewHolder( holder: RecyclerView.ViewHolder, position: Int, payloads: MutableList<Any>) { if (payloads.isNotEmpty()) { val item = getItem(position) (holder as RedditPostViewHolder).updateScore(item) } else { onBindViewHolder(holder, position) } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { return when (viewType) { R.layout.reddit_post_item -> RedditPostViewHolder.create(parent) R.layout.network_state_item -> NetworkStateItemViewHolder.create(parent, retryCallback) else -> throw IllegalArgumentException("unknown view type $viewType") } } private fun hasExtraRow() = networkState != null && networkState != NetworkState.LOADED override fun getItemViewType(position: Int): Int { return if (hasExtraRow() && position == itemCount - 1) { R.layout.network_state_item } else { R.layout.reddit_post_item } } override fun getItemCount(): Int { return super.getItemCount() + if (hasExtraRow()) 1 else 0 } fun setNetworkState(newNetworkState: NetworkState?) { val previousState = this.networkState val hadExtraRow = hasExtraRow() this.networkState = newNetworkState val hasExtraRow = hasExtraRow() if (hadExtraRow != hasExtraRow) { if (hadExtraRow) { notifyItemRemoved(super.getItemCount()) } else { notifyItemInserted(super.getItemCount()) } } else if (hasExtraRow && previousState != newNetworkState) { notifyItemChanged(itemCount - 1) } } companion object { private val PAYLOAD_SCORE = Any() val POST_COMPARATOR = object : DiffUtil.ItemCallback<RedditPost>() { override fun areContentsTheSame(oldItem: RedditPost, newItem: RedditPost): Boolean = oldItem == newItem override fun areItemsTheSame(oldItem: RedditPost, newItem: RedditPost): Boolean = oldItem.name == newItem.name override fun getChangePayload(oldItem: RedditPost, newItem: RedditPost): Any? { return if (sameExceptScore(oldItem, newItem)) { PAYLOAD_SCORE } else { null } } } private fun sameExceptScore(oldItem: RedditPost, newItem: RedditPost): Boolean {
ViewHolder .
RedditPostViewHolder.kt import android.content.Intent import android.net.Uri import androidx.recyclerview.widget.RecyclerView import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ImageView import android.widget.TextView import com.memebattle.pagingwithrepository.R import com.memebattle.pagingwithrepository.domain.model.RedditPost class RedditPostViewHolder(view: View) : RecyclerView.ViewHolder(view) { private val title: TextView = view.findViewById(R.id.title) private val subtitle: TextView = view.findViewById(R.id.subtitle) private val score: TextView = view.findViewById(R.id.score) private var post : RedditPost? = null init { view.setOnClickListener { post?.url?.let { url -> val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) view.context.startActivity(intent) } } } fun bind(post: RedditPost?) { this.post = post title.text = post?.title ?: "loading" subtitle.text = itemView.context.resources.getString(R.string.post_subtitle, post?.author ?: "unknown") score.text = "${post?.score ?: 0}" } companion object { fun create(parent: ViewGroup): RedditPostViewHolder { val view = LayoutInflater.from(parent.context) .inflate(R.layout.reddit_post_item, parent, false) return RedditPostViewHolder(view) } } fun updateScore(item: RedditPost?) { post = item score.text = "${item?.score ?: 0}" } }
NetworkStateItemViewHolder.kt import androidx.recyclerview.widget.RecyclerView import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Button import android.widget.ProgressBar import android.widget.TextView import com.memebattle.pagingwithrepository.R import com.memebattle.pagingwithrepository.domain.repository.network.NetworkState import com.memebattle.pagingwithrepository.domain.repository.network.Status class NetworkStateItemViewHolder(view: View, private val retryCallback: () -> Unit) : RecyclerView.ViewHolder(view) { private val progressBar = view.findViewById<ProgressBar>(R.id.progress_bar) private val retry = view.findViewById<Button>(R.id.retry_button) private val errorMsg = view.findViewById<TextView>(R.id.error_msg) init { retry.setOnClickListener { retryCallback() } } fun bindTo(networkState: NetworkState?) { progressBar.visibility = toVisibility(networkState?.status == Status.RUNNING) retry.visibility = toVisibility(networkState?.status == Status.FAILED) errorMsg.visibility = toVisibility(networkState?.msg != null) errorMsg.text = networkState?.msg } companion object { fun create(parent: ViewGroup, retryCallback: () -> Unit): NetworkStateItemViewHolder { val view = LayoutInflater.from(parent.context) .inflate(R.layout.network_state_item, parent, false) return NetworkStateItemViewHolder(view, retryCallback) } fun toVisibility(constraint : Boolean): Int { return if (constraint) { View.VISIBLE } else { View.GONE } } } }
, , Reddit androiddev. , .

, !
, Google.
هذا كل شيء. “” , .
!