I'll give you some hint of my app , maybe help with my issue .
I have an Api an with retrofit I'll get this data . after that insert all data in roomdatabase .
In my main screen with a recyclerview I display all this product (data) .
Near each of this product I have a button to send product to the cart fragment .also have a value(textview) to save each button is pressed to show how many items gone to cart fragment .
I use a upsert(transaction) for insert or update the product to cart fragment .
and after that use a obvserver livedata to display the amount value of products that is send to cart .
First i need A way to implement the observe live data in my main screen instead of adapter / and also is my upsert query is correct ? sometimes when click on products button the other products value changes .
Here is my code :
Dao :
// this is for Maintable
#Query("SELECT * FROM main")
fun getalldata(): LiveData<List<Roomtable>>
// cart Table
#Query("SELECT * FROM Cart")
fun getAllFromCart(): LiveData<List<CartTable>>
#Query("SELECT * FROM Cart WHERE id = :int")
fun GetAllFromCart(int: Int): LiveData<List<CartTable>>
#Insert(onConflict = OnConflictStrategy.IGNORE)
fun insertToCart(model: CartTable): Long
#Query("UPDATE cart SET amount = amount+1 WHERE id = :int")
fun updateCart(int: Int)
#Transaction
fun upsert(model: CartTable) {
val id = insertToCart(model)
if (id == -1L) {
model.id?.let {
updateCart(it)
}
MainAdapter :
class RecyclerAdapterMain(
private val product: List<Roomtable>,
val context: Context,
private val viewlifecyclerOwner: LifecycleOwner
) :
RecyclerView.Adapter<RecyclerAdapterMain.ViewHolder>() {
val viewModel: ViewModelRoom by lazy {
ViewModelProvider.AndroidViewModelFactory(Application()).create(ViewModelRoom::class.java)
}
inner class ViewHolder(itemview: View) :
RecyclerView.ViewHolder(itemview) {
val title: TextView = itemview.product_txt
val price: TextView = itemview.price_product
val imageproduct: ImageView = itemview.product_image
val btn_add_product: Button = itemview.btn_add_product
var amount_value: TextView = itemview.amount_value
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val layoutview =
LayoutInflater.from(parent.context).inflate(R.layout.product_items, parent, false)
return ViewHolder(layoutview)
}
override fun getItemCount(): Int = product.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
var products = product[position]
holder.title.text = products.title
holder.price.text = products.price
Picasso.get().load(products.image).into(holder.imageproduct)
holder.btn_add_product.setOnClickListener {
products.amount++
viewModel.upsert(
CartTable(
holder.adapterPosition,
products.title,
products.price,
products.image,
products.amount
)
)
viewModel.GetallFromCart(holder.adapterPosition).observe(viewlifecyclerOwner, Observer {
if (it != null) {
for (item in it) {
holder.amount_value.text = item.amount.toString()
}
}
})
}
Main Activity :
class HomeActivity : AppCompatActivity(){
val viewModel: ViewModelRoom by lazy {
ViewModelProvider(this).get(ViewModelRoom::class.java)
}
#RequiresApi(Build.VERSION_CODES.M)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.home_activity)
loadProduct()
#RequiresApi(Build.VERSION_CODES.M)
private fun loadProduct() {
swipeRefreshMain.isRefreshing = true
if (hasNetworkAvilable(applicationContext)) {
viewModel.setup()
viewModel.products.observe(this, Observer {
loadrecycler(it)
})
} else {
Toast.makeText(
applicationContext,
"برای بروز رسانی محصولات اینترنت خود را روشن کنید",
Toast.LENGTH_LONG
).show()
viewModel.getalldata().observe(this, Observer {
if (!it.isNullOrEmpty()) {
loadrecycler(it)
} else {
val builder = AlertDialog.Builder(this)
.setView(R.layout.customalertdialog)
.setPositiveButton("Ok", null)
.create()
.show()
val constraint = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
val workmanager: WorkManager = WorkManager.getInstance(this)
val workRequest = OneTimeWorkRequest.Builder(UploadWorkerClass::class.java)
.setConstraints(constraint)
.build()
workmanager.enqueue(workRequest)
}
})
}
}
fun loadrecycler(product: List<Roomtable>) {
val swipeRefreshLayout: SwipeRefreshLayout = findViewById(R.id.swipeRefreshMain)
val recycler: RecyclerView = findViewById(R.id.recycler_main)
recycler.apply {
layoutManager = GridLayoutManager(this#HomeActivity, 2)
adapter = RecyclerAdapterMain(
product, this#HomeActivity, this#HomeActivity )
Handler(Looper.getMainLooper()).postDelayed({
swipeRefreshLayout.isRefreshing = false
}, randomInRange(1, 3) * 1000.toLong())
}
}
}
Related
I have an API which give me the list of doctors. On it's last page only 1 item is there and other items are null like this:
After this i have used paging library for pagination
my pagingSource code: `
class DocPagingSource(val docRepository: DocRepository): PagingSource<Int, Data>() {
override fun getRefreshKey(state: PagingState<Int, Data>): Int? {
return state.anchorPosition?.let {
state.closestPageToPosition(it)?.prevKey?.plus(1)
?: state.closestPageToPosition(it)?.nextKey?.minus(1)
}
}
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Data> {
return try {
val currentPage = params.key?: 1
val city: String = ""
val response = docRepository.getDoctors(city, currentPage)
val page = Math.ceil(response.body()!!.total.toDouble()/5).toInt()
val data = response.body()!!.data
val responseData = mutableListOf<Data>()
responseData.addAll(data)
LoadResult.Page(
data = responseData,
prevKey = if(currentPage==1) null else -1,
nextKey = if (currentPage== page) null else currentPage.plus(1)
)
}catch (e: HttpException){
LoadResult.Error(e)
}catch (e: Exception){
LoadResult.Error(e)
}
}
`
My paging Adapter Code:
class DocAdapter(val context: Context): PagingDataAdapter<Data, DocAdapter.DocViewHolder>(DiffUtil()) {
private lateinit var binding: ItemDoctorsBinding
inner class DocViewHolder : RecyclerView.ViewHolder(binding.root) {
fun bind(item: Data?) {
binding.apply {
txtDocCity.text = item?.city
txtDocName.text = item?.docName
txtDocFees.text = item?.docConsultationFee
txtDocYOE.text = item?.docYoE
txtDocSpecialisation.text = item?.docSpecialisation
Glide.with(context)
.load(item?.docProfileImgUrl)
.fitCenter()
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(docPhoto)
}
}
}
override fun onBindViewHolder(holder: DocViewHolder, position: Int) {
val item = getItem(position)
if (item!=null){
holder.bind(getItem(position)!!)
holder.setIsRecyclable(false)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DocViewHolder {
val inflater = LayoutInflater.from(context)
binding = ItemDoctorsBinding.inflate(inflater, parent, false)
return DocViewHolder()
}
class DiffUtil: androidx.recyclerview.widget.DiffUtil.ItemCallback<Data>(){
override fun areItemsTheSame(oldItem: Data, newItem: Data): Boolean {
return oldItem.docId == newItem.docId
}
override fun areContentsTheSame(oldItem: Data, newItem: Data): Boolean {
return oldItem==newItem
}
}}
what I am getting after reaching my 16th item in doctor list on last page it should show entry till 16th item but after that it also shows again like this:
Also if i dont use holder.setIsRecyclable(false) in pagingAdapter then this android icon not shown but then list is populated with previous doctors:
on the top DR. Sixteen is shown like this:
and in between it again shows like this:
My doctorViewModel Class:
class DocViewModel(val repository: DocRepository): ViewModel() {
val loading = MutableLiveData<Boolean>()
val docList = Pager(PagingConfig(5, maxSize = 100)){
DocPagingSource(repository)
}.flow.cachedIn(viewModelScope)}
My main Activity:
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var docViewModel: DocViewModel
private lateinit var docAdapter: DocAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val docListRepository = DocRepository()
val docFactory = DocViewModelFactory(docListRepository)
docViewModel = ViewModelProvider(this, docFactory).get(DocViewModel::class.java)
docAdapter = DocAdapter(this)
lifecycleScope.launchWhenCreated {
docViewModel.docList.collect{
docAdapter.submitData(it)
}
}
binding.docRecyclerView.apply {
layoutManager = LinearLayoutManager(this#MainActivity)
adapter = docAdapter
setHasFixedSize(true)
}
}}
I have solved this error by applying a condition in my paging source code
I have a RecyclerView where an item can be edited via a DialogFragment, so when an item is clicked a Dialog is shown, then I can change some properties of that item, the issue is that RecyclerView is not updated with the updated properties and I have to force a notifyItemChanged when the Dialog is closed.
When an item in RecyclerView is clicked I set a MutableLiveData in my ViewModel so then it can be manipulated in the Dialog.
My ViewModel looks like this:
#HiltViewModel
class DocumentProductsViewModel #Inject constructor(private val repository: DocumentProductsRepository) :
ViewModel() {
val barcode = MutableLiveData<String>()
private val _selectedProduct = MutableLiveData<DocumentProduct>()
val selectedProduct: LiveData<DocumentProduct> = _selectedProduct
private val _selectedDocumentId = MutableLiveData<Long>()
val selectedDocumentId: LiveData<Long> = _selectedDocumentId
val products: LiveData<List<DocumentProduct>> = _selectedDocumentId.switchMap { documentId ->
repository.getDocumentProducts(documentId).asLiveData()
}
fun insert(documentProduct: DocumentProduct) = viewModelScope.launch {
repository.insert(documentProduct)
}
fun setProductQuantity(quantity: Float) {
_selectedProduct.value = _selectedProduct.value.also {
it?.timestamp = System.currentTimeMillis()
it?.quantity = quantity
}
update()
}
fun start(documentId: Long?) = viewModelScope.launch{
if (documentId == null) {
_selectedDocumentId.value = repository.getHeaderByType("Etichette")?.id
}
documentId?.let { documentId ->
_selectedDocumentId.value = documentId
}
}
fun select(product: DocumentProduct) {
_selectedProduct.value = product
}
fun delete() = viewModelScope.launch {
_selectedProduct.value?.let { repository.delete(it) }
}
private fun update() = viewModelScope.launch {
_selectedProduct.value?.let { repository.update(it) }
}
}
And in my fragment I'm subscribed to products as this:
private fun initRecyclerView() {
binding.rvProducts.adapter = adapter
viewModel.products.observe(viewLifecycleOwner) { products ->
val productsCount = products.count()
binding.tvProductsCount.text =
resources.getQuantityString(R.plurals.articoli, productsCount, productsCount)
// TODO: create amount string and set it with resources
binding.tvProductsAmount.text = productsCount.toEuro()
adapter.submitList(products)
binding.rvProducts.smoothScrollToPosition(adapter.itemCount - 1)
}
initSwipe(adapter)
}
When setProductQuantity is called the RecyclerView remains unchanged until notify is called while delete works fine without the necessity of calling any notify on RecyclerView.
UPDATE:
The item position is actually changed in RecyclerView as it's sorted by it's last changed timestamp BUT not the quantity field.
Here is my Adapter:
class DocumentProductsListAdapter : ListAdapter<DocumentProduct, DocumentProductsListAdapter.ViewHolder>(ProductDiffCallback) {
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
val view: View = LayoutInflater.from(viewGroup.context)
.inflate(R.layout.layout_item, viewGroup, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val product = getItem(position)
holder.bind(product)
}
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val barcode: TextView = itemView.findViewById(R.id.barcode)
val quantity: TextView = itemView.findViewById(R.id.quantity)
val description: TextView = itemView.findViewById(R.id.description)
val unitOfMeasure: TextView = itemView.findViewById(R.id.unitOfMeasure)
fun bind(product: DocumentProduct) {
barcode.text = product.barcode
quantity.text = product.quantity.formatForQta().replace(".", ",")
if (product.labelType != null && product.labelType != "") {
unitOfMeasure.text = product.labelType
} else {
unitOfMeasure.text = product.unitOfMeasure?.lowercase(Locale.ITALIAN)
}
description.text = product.description ?: "-"
}
}
}
object ProductDiffCallback : DiffUtil.ItemCallback<DocumentProduct>() {
override fun areItemsTheSame(oldItem: DocumentProduct, newItem: DocumentProduct): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: DocumentProduct, newItem: DocumentProduct): Boolean {
return oldItem == newItem
}
}
data class DocumentProduct(
#PrimaryKey(autoGenerate = true)
var id: Long,
var barcode: String,
#Json(name = "desc")
var description: String?,
#ColumnInfo(defaultValue = "PZ")
#Json(name = "um")
var unitOfMeasure: String?,
#Json(name = "qta")
var quantity: Float,
#Json(name = "id_testata")
var documentId: Long,
#Json(name = "tipo_frontalino")
var labelType: String?,
var timestamp: Long?
) {
constructor(barcode: String, documentId: Long, labelType: String?) : this(
0,
barcode,
null,
"PZ",
1f,
documentId,
labelType,
null
)
override fun equals(other: Any?): Boolean {
return super.equals(other)
}
override fun hashCode(): Int {
return super.hashCode()
}
}
You have the implementations of areContentsTheSame() and areItemsTheSame() swapped.
areContentsTheSame() is asking if everything in the two items being compared is the same. Therefore, if the class has a proper equals()/hashcode() for all properties used by the ViewHolder, you can use oldItem == newItem. If you use a data class with all relevant properties in the primary constructor, then you don't need to manually override equals()/hashcode().
areItemsTheSame() is asking if the two items represent the same conceptual row item, with possible differences in their details. So it should be oldItem.id == newItem.id.
The problem with your data class is that you are overriding equals()/hashcode() without providing any implementation at all. This is effectively disabling the proper implementations that are provided by the data modifier by calling through to the super implementation in the Any class. You should not override them at all when you use data class.
I just want to say sorry to my English skill
I've studied the Android Studio and Kotlin these days.
but I'd got a problem on RecyclerViewer and Adapter, for Intent method
work flow chart
this image, this is what i want to do
so i coded the three classes
ShoppingAppActivity.kt, MyAdapter.kt, CartActivity.kt
At ShoppingAppActivity, If I click the itemId ( in the Red box texts)
I make it move to other context(CartActivity)
ShoppingAppActivity working
if i clicked the red box then
cartStatus
go to cart Activity
it worked but already I said, I just want to send only send itemID
covert to String (i will use toString())
SO i tried to use Intent method in ShoppingAppActivity.kt
///PROBLEM PART
adapter?.setOnItemClickListener{
val nextIntent = Intent(this, CartActivity::class.java)
//nextIntent.putExtra("itemID", )
startActivity(nextIntent)
}
///PROBLEM PART
like this but the problem is I don't know what am i have to put the parameter in
nextIntent.putExtra("itemID", )
what should i do?
I think, I should fix MyAdaptor.kt or ShopingAppActivity.kt for this problem.
But in my knowledge, this is my limit. :-(
below
Full Codes of ShoppingAppActivity.kt, MyAdapter.kt, CartActivity.kt
ShoppingAppActivity.kt
class ShoppingAppActivity : AppCompatActivity() {
lateinit var binding: ActivityShoppingAppBinding
private var adapter: MyAdapter? = null
private val db : FirebaseFirestore = Firebase.firestore
private val itemsCollectionRef = db.collection("items")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityShoppingAppBinding.inflate(layoutInflater)
setContentView(binding.root)
updateList()
binding.recyclerViewItems.layoutManager = LinearLayoutManager(this)
adapter = MyAdapter(this, emptyList())
binding.recyclerViewItems.adapter = adapter
///PROBLEM PART
adapter?.setOnItemClickListener{
val nextIntent = Intent(this, CartActivity::class.java)
//nextIntent.putExtra("itemID", )
startActivity(nextIntent)
}
///PROBLEM PART
}
private fun updateList() {
itemsCollectionRef.get().addOnSuccessListener {
val items = mutableListOf<Item>()
for (doc in it) {
items.add(Item(doc))
}
adapter?.updateList(items)
}
}
}
MyAdapter.kt
data class Item(val id: String, val name: String, val price: Int, val cart: Boolean) {
constructor(doc: QueryDocumentSnapshot) :
this(doc.id, doc["name"].toString(), doc["price"].toString().toIntOrNull() ?: 0, doc["cart"].toString().toBoolean() ?: false)
constructor(key: String, map: Map<*, *>) :
this(key, map["name"].toString(), map["price"].toString().toIntOrNull() ?: 0, map["cart"].toString().toBoolean() ?: false)
}
class MyViewHolder(val binding: ItemBinding) : RecyclerView.ViewHolder(binding.root)
class MyAdapter(private val context: Context, private var items: List<Item>)
: RecyclerView.Adapter<MyViewHolder>() {
fun interface OnItemClickListener {
fun onItemClick(student_id: String)
}
private var itemClickListener: OnItemClickListener? = null
fun setOnItemClickListener(listener: OnItemClickListener) {
itemClickListener = listener
}
fun updateList(newList: List<Item>) {
items = newList
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding: ItemBinding = ItemBinding.inflate(inflater, parent, false)
return MyViewHolder(binding)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val item = items[position]
val itemID : String
holder.binding.textID.text = item.id
holder.binding.textName.text = item.name
if(item.cart)
{
holder.binding.textCart.text = "in Cart"
}
else
{
holder.binding.textCart.text = ""
}
holder.binding.textID.setOnClickListener {
AlertDialog.Builder(context).setMessage("You clicked ${item.id}.").show()
itemClickListener?.onItemClick(item.id)
}
holder.binding.textName.setOnClickListener {
//AlertDialog.Builder(context).setMessage("You clicked ${student.name}.").show()
itemClickListener?.onItemClick(item.id)
}
//return item.id.toString()
}
override fun getItemCount() = items.size
}
CartActivity.kt
class CartActivity : AppCompatActivity() {
lateinit var binding: ActivityCartBinding
private val db: FirebaseFirestore = Firebase.firestore
private val itemsCollectionRef = db.collection("items")
private var adapter: MyAdapter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityCartBinding.inflate(layoutInflater)
setContentView(binding.root)
updateList()
//binding.recyclerViewItems.layoutManager = LinearLayoutManager(this)
//adapter = MyAdapter(this, emptyList())
//binding.recyclerViewItems.adapter = adapter
binding.changeCartStatus.setOnClickListener{
//change the button's text if the itemID is corrected
//if(){
// binding.changeCartStatus.text = ""
//}
}
}
private fun updateList() {
itemsCollectionRef.get().addOnSuccessListener {
val items = mutableListOf<Item>()
for (doc in it) {
items.add(Item(doc))
}
adapter?.updateList(items)
}
}
}
You just need to implement listener to your activity
class ShoppingAppActivity : AppCompatActivity() ,MyAdapter.OnItemClickListener {
In oncreate add below line after adapter
adapter?.setOnItemClickListener(this)
Then override its method
override fun onItemClick(id: String){
val nextIntent = Intent(this, CartActivity::class.java)
nextIntent.putExtra("itemID",id )
startActivity(nextIntent)
}
I'm trying to make my Android App (I'm only experienced in iOS).
I created a RecyclerView that gets the data from a web. I tried everything to implement endless scrolling to load more items, but when I call the function to get the items, the entire RecyclerView loads again and no attach the new results on the bottom.
This is my code:
ConversationUser.kt
data class ConversationUser(
val message_nickname: String,
val message_image_thumb: String,
val message_large_thumb: String,
val message_modified: String,
val message_status: String,
val message_unread: Int,
val conv_id: String,
val message_dest: String) {
}
ConversacionesActivity.kt
class ConversacionesActivity : AppCompatActivity() {
// MARK: Variables
var user_token = ""
var user_id = ""
override fun onCreate(savedInstanceState: Bundle?) {
// User Defaults
val sharedPreferences = getSharedPreferences("Preferences", Context.MODE_PRIVATE)
user_token = sharedPreferences.getString("user_token", "")!!
user_id = sharedPreferences.getString("user_id", "")!!
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_conversaciones)
recyclerConv.addItemDecoration(DividerItemDecoration(this, LinearLayoutManager.VERTICAL))
getConversationsData()
recyclerConv.setLoadingListener(object : LoadingListener {
override fun onRefresh() {
//refresh data here
}
override fun onLoadMore() {
// load more data here
getConversationsData()
}
})
}
fun getConversationsData() {
val httpAsync = "https://mywebsite.com/conversations/${user_token}"
.httpPost()
.responseString { request, response, result ->
when (result) {
is Result.Failure -> {
val ex = result.getException()
println(ex)
}
is Result.Success -> {
val data = result.get()
runOnUiThread {
val conversaciones = processJson(data)
show(conversaciones)
return#runOnUiThread
}
}
}
}
httpAsync.join()
}
fun processJson(json: String): List<ConversationUser> {
val gson: Gson = GsonBuilder().create()
val conversaciones: List<ConversationUser> = gson.fromJson(
json,
Array<ConversationUser>::class.java
).toList()
return conversaciones
}
fun show(conversaciones: List<ConversationUser>) {
recyclerConv.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
recyclerConv.adapter = AdaptadorConv(conversaciones, this, user_token, user_id)
}
AdaptadorConv.kt
class AdaptadorConv(
val conversaciones: List<ConversationUser> = ArrayList(),
val context: Context,
val user_token: String,
val user_id: String) : RecyclerView.Adapter<AdaptadorConv.ConvViewHolder>() {
override fun onBindViewHolder(holder: ConvViewHolder, position: Int) {
holder.convName.text = conversaciones[position].message_nickname
holder.convTime.text = conversaciones[position].message_modified
}
override fun getItemCount(): Int {
return conversaciones.size - 1
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ConvViewHolder {
val view: View = LayoutInflater.from(parent.context).inflate(
R.layout.conversaciones,
parent,
false
)
return ConvViewHolder(view)
}
class ConvViewHolder(vista: View): RecyclerView.ViewHolder(vista) {
val convImg: ImageView = itemView.findViewById(R.id.convImg)
val convStatus: ImageView = itemView.findViewById(R.id.convStatus)
val convName: TextView = itemView.findViewById(R.id.convName)
val convUnread: TextView = itemView.findViewById(R.id.convUnread)
val convTime: TextView = itemView.findViewById(R.id.convTime)
}
Thanks for any help or hint.
Please check your show () method, you are creating new Adapter every time with the new dataset. You have to append the new items to the adapter's list and adapter should be set to list once. Helpful tutorial can be found at here.
Here is my first time to apply MVVM concept to my Android Application. I follow the steps at the referenced article
https://medium.com/swlh/realtime-firestore-pagination-on-android-with-mvvm-b5e30cea437
And I am managed to load data successfully. When it comes to implementing the onclick event at the row of my RecyclerView List, it comes out that there has no onlick response .
Would you please suggest the better method to implement the onCLick method given that I have applied PageListAdapter?
When I study the PageListAdapter documentation on Android, it seems no clue for me to implement the onclick method.
class MovieViewModel(movieRepository: MovieRepository) : ViewModel() {
private val viewModelJob = SupervisorJob()
private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
var selected: MutableLiveData<RealtimeMovie>? = null
private val config = PagedList.Config.Builder()
.setEnablePlaceholders(false)
.setPrefetchDistance(10)
.setPageSize(20)
.build()
val records: LiveData<PagedList<RealtimeMovie>> =
LivePagedListBuilder<String, RealtimeMovie>(
MovieDataSource.Factory(movieRepository, uiScope),
config
).build()
override fun onCleared() {
super.onCleared()
viewModelJob.cancel()
}
}
Here is my adapter:
class MovieAdapter : PagedListAdapter<RealtimeMovie, MovieAdapter.MovieViewHolder>(
object : DiffUtil.ItemCallback<RealtimeMovie>() {
override fun areItemsTheSame(old: RealtimeMovie, new: RealtimeMovie): Boolean =
old.id == new.id
override fun areContentsTheSame(old: RealtimeMovie, new: RealtimeMovie): Boolean =
old == new
}
) {
private lateinit var onItemClick: (movie: RealtimeMovie) -> Unit
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MovieViewHolder {
val view = LayoutInflater.from(parent.context).inflate(
R.layout.list_movie,
parent,
false
)
return MovieViewHolder(view)
}
infix fun setOnItemClick(onClick: (movie: RealtimeMovie) -> Unit) {
this.onItemClick = onClick
}
override fun onBindViewHolder(holder: MovieViewHolder, position: Int) {
val record = getItem(position)
holder.bind(record)
holder.itemView.setOnClickListener { onItemClick(record!!) }
}
override fun onViewRecycled(holder: MovieViewHolder) {
super.onViewRecycled(holder)
holder.apply {
txtRecordName.text = ""
crdRecord.isEnabled = true
crdRecord.setCardBackgroundColor(
ContextCompat.getColor(
view.context,
android.R.color.white
)
)
viewHolderDisposables.clear()
}
}
inner class MovieViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
val viewHolderDisposables = CompositeDisposable()
val crdRecord by lazy { view.findViewById<MaterialCardView>(R.id.crd_record) }
val txtRecordName by lazy { view.findViewById<TextView>(R.id.txt_record_name) }
fun bind(RealtimeMovie: RealtimeMovie?) {
RealtimeMovie?.let {
it.record
.subscribeBy(
onNext = {
txtRecordName.text = it.title
},
onError = {
// Handle error here
// Record maybe deleted
}
)
.addTo(viewHolderDisposables)
}
}
}
}
Here is my fragment :
..
viewModel = ViewModelProviders.of(this, factory).get(MovieViewModel::class.java)
viewModel.records.observe(this, Observer {
swpRecords.isRefreshing = false
recordsAdapter.submitList(it)
recordsAdapter.setOnItemClick {
print("movie : ${it.id}" )
print("movie : ${it.record}" )
}
})