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.