[Question] Update multiple views separately by Click Listener
See original GitHub issueHello, I’m new to the library and I have an issue while implementing my example app.
In general, I have a list view of model (using @EpoxyModelClass
and @EpoxyAttribute
manually). In each model, I have a button download
and download_progress_view
(updated by progress). When clicking on download
button, then it will update progress of view in model with different values.
I have problem with this, click on download
then all model update progress instead of the clicked one.
Do you have any suggestions?
Thanks for great library. Please have a look at relevant code below:
My Model
@EpoxyModelClass(layout = R.layout.view_holder_course_detail_chapter_item)
abstract class CourseDetailChapterModel : EpoxyModelWithHolder<CourseChapterViewHolder>() {
@EpoxyAttribute
lateinit var onClickListener: () -> Unit
@EpoxyAttribute
lateinit var courseChapter: CourseChapter
@EpoxyAttribute
lateinit var course: Course
@EpoxyAttribute
lateinit var purchaseState: PurchaseState
@EpoxyAttribute
lateinit var authState: AuthState
@EpoxyAttribute
lateinit var context: Context
@EpoxyAttribute
lateinit var onDownloadClick: () -> Unit
@EpoxyAttribute
var myProgress: Float = 0F
@SuppressLint("SetTextI18n")
override fun bind(holder: CourseChapterViewHolder) {
var fileName = "${courseChapter.courseId}:${courseChapter.chapterNumber}:${courseChapter.version}.pdf"
var fileExisted = FileUtils.checkFileExisted(context, fileName)
holder.chapterDownloadProgress.color = Color.parseColor("#${course.colorPrimary}")
holder.chapterTitleTextView.text = "${courseChapter.chapterNumber}. ${courseChapter.title}"
if (courseChapter.currentlyPlaying) {
holder.chapterTitleTextView.setTextColor((Color.parseColor("#${course.colorPrimary}")))
} else {
holder.chapterTitleTextView.setTextColor(Color.WHITE)
}
holder.chapterDurationTextView.text = millisecondsToString(courseChapter.duration)
holder.chapterBackground.setOnClickListener { onClickListener.invoke() }
holder.chapterDescriptionTextView.text = courseChapter.description
if (courseChapter.isTrial || (purchaseState.purchased && authState.authed)) {
holder.chapterLockIcon.visibility = View.GONE
}
if (fileExisted) {
holder.chapterDownloadTextView.visibility = VISIBLE
holder.chapterDownloadedIcon.visibility = VISIBLE
holder.chapterDownloadTextView.text = context.getString(R.string.lesson_downloaded)
holder.chapterDownloadProgress.progress = 0f
holder.chapterDownloadTextView.setOnClickListener(null)
} else {
holder.chapterDownloadedIcon.visibility = GONE
holder.chapterDownloadTextView.visibility = VISIBLE
holder.chapterDownloadTextView.text = context.getString(R.string.lesson_download)
holder.chapterDownloadTextView.setOnClickListener { onDownloadClick.invoke() }
holder.chapterDownloadProgress.progress = myProgress
if (myProgress > 0F) {
holder.chapterDownloadProgress.visibility = VISIBLE
holder.chapterDownloadTextView.visibility = GONE
holder.chapterDownloadedIcon.visibility = GONE
if (myProgress in 99F..100F) {
holder.chapterDownloadProgress.visibility = GONE
holder.chapterDownloadTextView.text = context.getString(R.string.lesson_downloaded)
holder.chapterDownloadTextView.visibility = VISIBLE
holder.chapterDownloadedIcon.visibility = VISIBLE
}
}
}
}
override fun unbind(holder: CourseChapterViewHolder) {
super.unbind(holder)
holder.chapterLockIcon.visibility = View.VISIBLE
holder.chapterTitleTextView.setTextColor(Color.WHITE)
}
private fun millisecondsToString(millis: Long): String {
val hours = millis / 60
val minutes = millis % 60
return if (hours > 0) {
"$hours hr $minutes min"
} else {
"$minutes min"
}
}
}
class CourseChapterViewHolder : BaseEpoxyHolder() {
val chapterBackground by bind<View>(R.id.course_chapter_item_background)
val chapterTitleTextView by bind<TextView>(R.id.course_chapter_item_title_text_view)
val chapterDurationTextView by bind<TextView>(R.id.course_chapter_item_duration_text_view)
val chapterDescriptionTextView by bind<TextView>(R.id.course_chapter_description_text_view)
val chapterLockIcon by bind<ImageView>(R.id.course_chapter_item_lock_image_view)
val chapterDownloadTextView by bind<TextView>(R.id.download_title)
val chapterDownloadedIcon by bind<ImageView>(R.id.downloaded_icon)
val chapterDownloadProgress by bind<CircularProgressBar>(R.id.download_status)
}
My controller
class CourseDetailChaptersController(private val course: Course, private val context: Context) : Typed4EpoxyController<Boolean, List<CourseChapter>, Boolean, Boolean>() {
var chaptersListener: CourseDetailChaptersListener? = null
var downloadListener: CourseDetailChaptersDownloadListener? = null
private var myProgress: Float = 0F
fun setProgress(progress: Float) {
this.myProgress = progress
}
override fun buildModels(loading: Boolean, chapters: List<CourseChapter>, coursePurchased: Boolean, authenticated: Boolean) {
if (loading) {
courseDetailLoading {
id("loading")
}
return
}
if (chapters.isNotEmpty()) {
courseDetailEmptySpace {
id("empty_space")
}
for (chapter in chapters.sortedBy { it.chapterNumber }) {
courseDetailChapter {
context(context)
id(chapter.id)
courseChapter(chapter)
purchaseState(PurchaseState(coursePurchased))
course(course)
authState(AuthState(authenticated))
onClickListener{
chaptersListener?.onChapterClicked(chapter)
}
onDownloadClick {
downloadListener?.onDownloadClicked(chapter)
}
myProgress(myProgress)
}
}
}
}
}
Handle onDownloadClicked
private fun downloadFile(chapter: CourseChapter, downloadUrl: String) {
val myDirectory = File(context!!.filesDir.path + "/MyResources/")
var filePath: String
Fuel.download(downloadUrl)
.fileDestination { response, url ->
myDirectory.mkdir()
filePath = File(myDirectory.absolutePath, "${chapter.courseId}:${chapter.chapterNumber}:${chapter.version}.pdf").absolutePath
File(filePath)
}
.progress { readBytes, totalBytes ->
val progress = readBytes.toFloat() / totalBytes.toFloat() * 100
Timber.i("Bytes downloaded $readBytes / $totalBytes ($progress %)")
controller.setProgress(progress = progress)
handleDataState(chaptersViewModel?.getChapters()!!.value!!)
}
.response { result ->
Timber.i(result.toString())
if (result.toString().contains("Success")) {
activity!!.runOnUiThread {
Toast.makeText(context, "Downloaded file ${chapter.courseId}:${chapter.chapterNumber}:${chapter.version}.pdf", Toast.LENGTH_LONG).show()
}
}
}
}
Issue Analytics
- State:
- Created 4 years ago
- Comments:5 (2 by maintainers)
Top Results From Across the Web
Multiple click listeners on buttons - java - Stack Overflow
I want to know how to add multiple click events to buttons defined in XML, as previously, in Java, we implemented View.onClickListener ......
Read more >Introduction to events - Learn web development | MDN
Inside the addEventListener() function, we specify two parameters: the name of the event we want to register this handler for, and the code...
Read more >Multi-window support - Android Developers
Multi -window mode enables multiple apps to share the same screen simultaneously. Apps can be side by side or one above the other...
Read more >API Reference | Eventbrite Platform
Eventbrite has many models that refer to each other, and often you'll want to fetch related data along with the primary model you're...
Read more >Updates to a BIND dynamic zone that is shared between ...
Different views act separately, it's essentially a convenience over running separate instances of named. If there are zones with the same name in...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Oh, I see what you are saying - this is because your data structure isn’t set up properly - you are using the same progress value for all models.
Epoxy always rebuilds all models - get in the mindset of thinking of your entire data set mapping to the models, and all models are rebuilt from the data.
you need to change your data structure to completely represent your models. The MvRx library is good for that and works very nicely with epoxy, but you can also just use a data class. You shouldn’t be using the typed adapter - it’s messy when you have 4 parameters, plus you are setting data via the progress setter.
anyway, for your specific problem, you need a map of chapter to progress
and when you build models, grab the right progress for the chapter
Thank you very much for your solution. It works well now. Cheers 😃