Home > Software engineering >  How to load image into RecyclerView Adapter of Android
How to load image into RecyclerView Adapter of Android

Time:11-14

In my application I should show image into recyclerview adapter.
I have 2 APIs.
First API send to me images ids, and I should send this id to second API for get base64 and then decode this base64 to bitmap and again show this into ImageView!
I write below codes, but per item first send API, receive base64, decode this and show into ImageView but when scroll items show me lags!

My Adapter codes:

class MyAdapter constructor(private val items: MutableList<Result>) : RecyclerView.Adapter<ViewHolder>() {

    private lateinit var binding: ItemBinding
    private lateinit var context: Context
    private val userToken by lazy { GoodPrefs.getInstance().getString(USER_TOKEN, "") }
    private var decodedBytes: ByteArray? = null

    @SuppressLint("SimpleDateFormat")
    val formatter: DateFormat = SimpleDateFormat("yyyy-MM-dd")
    lateinit var date: Date

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        binding = ItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        context = parent.context
        return ViewHolder()
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bind(items[position])
        holder.setIsRecyclable(false)
    }

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

    inner class ViewHolder : RecyclerView.ViewHolder(binding.root) {
        @SuppressLint("SetTextI18n")
        fun bind(item: Result) {
            binding.apply {
                //Title
                titleTxt.text = item.title
                //Date
                val dateStr = item.publishAt!!.split(" ")[0]
                date = formatter.parse(dateStr) as Date
                dateTxt.text = timeDiffer(date.time / 1000)
                //Like
                if (item.youLikeBefore!!) {
                    likeImg.setImageResource(R.drawable.ic_heart)
                    likeImg.setColorFilter(ContextCompat.getColor(context, R.color.mystic))
                } else {
                    likeImg.setImageResource(R.drawable.ic_heart_line)
                    likeImg.setColorFilter(ContextCompat.getColor(context, R.color.black))
                }
                if (item.likeCount!! > 0) {
                    likeTxt.isVisible = true
                    likeTxt.text = item.likeCount.toString()
                } else {
                    likeTxt.isVisible = false
                }
                //File
                if (item.file != null) {
                    when (item.file.type) {
                        IMAGE -> {
                            thumbImg.isVisible = true
                            thumbLoading.isVisible = true
                            ApiClient.getInstance().apisUseCase().getAvatarImage(userToken, item.file.id!!)
                                .applyIoScheduler()
                                .subscribe({
                                    thumbLoading.isVisible = false
                                    if (it.isSuccessful) {
                                        if (it.code() == 200) {
                                            if (it.body() != null) {
                                                decodedBytes = Base64.decode(it.body()!!.data!!.file, Base64.DEFAULT)
                                                thumbImg.load(decodedBytes) {
                                                    memoryCachePolicy(CachePolicy.ENABLED)
                                                }
                                            }
                                        }
                                    }
                                }, {
                                    thumbLoading.isVisible = false
                                })
                        }
                        VOICE -> {
                            thumbImg.load(R.drawable.img_voice)
                            thumbLoading.isVisible = false
                        }
                        VIDEO -> {
                            thumbImg.load(R.drawable.img_video)
                            thumbLoading.isVisible = false
                        }
                    }
                }
                //User
                if (item.user != null) {
                    doctorNameTxt.text = "${item.user.firstName} ${item.user.lastName}"
                }
                //Click
                likeImg.setOnClickListener {
                    onItemClickListener?.let {
                        it(item)
                    }
                }
            }
        }
    }

    private var onItemClickListener: ((Result) -> Unit)? = null

    fun setOnItemClickListener(listener: (Result) -> Unit) {
        onItemClickListener = listener
    }

    @SuppressLint("NotifyDataSetChanged")
    fun addItems(items: MutableList<Result>) {
        this.items.addAll(items)
        notifyDataSetChanged()
    }
}

First of all, I have question, this way is True?
And I want to call this API just for once time!
In my code per scroll call API and I used Coil cache for image loader but again get image from server does not call from cache!

How can I fix it?

CodePudding user response:

As per talk, I guess the image decoding happens in the UI thread.

So if this is the case, you can use Executors or Coroutines.

However, I am not a Kotlin developer so I am not familiar with Coroutines case. But it is similar to Executors so I will post the answer for Executors first.

After the byte array is prepared,

Executors case:

var uiHandler: Handler = Handler(Looper.getMainLooper())

    // Decode your byte array in another thread
    fun decodeAndLoad(file: ByteArray?) {
        val executor = Executors.newCachedThreadPool()
        executor.execute {
            val decodedBytes = Base64.decode(file, Base64.DEFAULT)
            uiHandler.post {
                //TODO: Load your the byte array to image view here
            }
        }
    }

Coroutines case: (Updated)

    fun decodeAndLoad(file: ByteArray?) {
        val job = GlobalScope.launch(Dispatchers.IO) {
            val decodedBytes = Base64.decode(file, Base64.DEFAULT)
            withContext(Dispatchers.Main){
                //TODO: Load your the byte array to image view here
            }
        }
    }

If possible, I think you shall use Glide and Picasso libraries to load the images in RV. Just input the URL and the image load (i.e., download by IO thread, load by UI thread) and cache will be done for you.

  • Related