Can't seem to make android state saving work - java

I'm currently working on a little project to practice my Android developping skills. Right now I have 2 activities set up, the "MainActivity" and the "EnlargeImageActivity".
EDIT: I'm working in kotlin
The Main activity contains a bunch of textviews and an imageview to display information about a boxer. The boxers are all hardcoded in an array and I retrieve them by using an integer called "counter". The counter is set to 0 at the very start.
private var counter: Int = 0
Here's the catch, to move throught the list of boxers I've added 2 buttons: a "next" and a "previous" button. When clicked these buttons increase/decrease the counter by 1 and update the textviews/imageview.
Finally, when clicking an image I want to see it enlarged in a new activity. I made this work by getting the image from my boxers-array at the index of counter.
The problem occurs when I move back from my EnlargedImageActivity to my MainActivity, the MainActivity completely resets to counter = 0, so the first element of my array is displayed instead of the element corresponding to the image I just enlarged.
Here's the code for all that:
MainActivity:
private lateinit var boxers: Array<Boxer>
private lateinit var imageView: ImageView
private lateinit var nameView: TextView
private lateinit var boxerIdView: TextView
private lateinit var birthDayView: TextView
private lateinit var weightClassView: TextView
private lateinit var winsView: TextView
private lateinit var lossesView: TextView
private lateinit var drawsView: TextView
private var counter: Int = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initialiseViews()
addEventHandlers()
}
private fun initialiseViews() {
imageView = findViewById(R.id.imageView)
nameView = findViewById(R.id.name)
boxerIdView = findViewById(R.id.boxerId)
birthDayView = findViewById(R.id.birthday)
weightClassView = findViewById(R.id.weightClass)
winsView = findViewById(R.id.wins)
lossesView = findViewById(R.id.losses)
drawsView = findViewById(R.id.draws)
boxers = getBoxers()
imageView.setImageDrawable(getDrawable(boxers[counter].image))
val fullName = boxers[counter].first_name + " " + boxers[counter].last_name
nameView.setText(fullName)
boxerIdView.setText(String.format("ID #%04d", boxers[counter].id))
val birthday = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(boxers[counter].birth_day.time)
birthDayView.setText(birthday)
weightClassView.setText(boxers[counter].weightclass)
winsView.setText(boxers[counter].wins.toString())
lossesView.setText(boxers[counter].losses.toString())
drawsView.setText(boxers[counter].draws.toString())
}
private fun addEventHandlers() {
val buttonNext: Button = findViewById(R.id.buttonNext)
buttonNext.setOnClickListener {
if (counter >= 14) {
counter = 0
} else {
counter += 1
}
imageView.setImageDrawable(getDrawable(boxers[counter].image))
val fullName = boxers[counter].first_name + " " + boxers[counter].last_name
nameView.text = fullName
boxerIdView.setText(String.format("ID #%04d", boxers[counter].id))
val birthday = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(boxers[counter].birth_day.time)
birthDayView.setText(birthday)
weightClassView.setText(boxers[counter].weightclass)
winsView.setText(boxers[counter].wins.toString())
lossesView.setText(boxers[counter].losses.toString())
drawsView.setText(boxers[counter].draws.toString())
}
val buttonPrev: Button = findViewById(R.id.buttonPrev)
buttonPrev.setOnClickListener {
if (counter <= 0) {
counter = boxers.lastIndex
} else {
counter -= 1
}
imageView.setImageDrawable(getDrawable(boxers[counter].image))
val fullName = boxers[counter].first_name + " " + boxers[counter].last_name
nameView.text = fullName
boxerIdView.setText(String.format("ID #%04d", boxers[counter].id))
val birthday = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(boxers[counter].birth_day.time)
birthDayView.setText(birthday)
weightClassView.setText(boxers[counter].weightclass)
winsView.setText(boxers[counter].wins.toString())
lossesView.setText(boxers[counter].losses.toString())
drawsView.setText(boxers[counter].draws.toString())
}
}
fun enlargeImage(view: View) {
val imageToEnlarge = boxers[counter].image
val intent = Intent(this, EnlargeImageActivity::class.java).apply {
putExtra(EXTRA_ENLIMAGE, imageToEnlarge)
}
startActivity(intent)
}
EnlargedImageActivity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_enlarge_image)
val image = intent.getIntExtra(EXTRA_ENLIMAGE, 0)
val imageView = findViewById<ImageView>(R.id.enlargedImage).apply {
setImageDrawable(getDrawable(image))
}
}
I tried to make it work by overriding the onStop(), onSaveInstance and onRestoreInstance() -functions to save my counter using a constant but I can't get it to work properly. I keep just ending up the first element of my array.

Related

How to pass a list of objects from adapter to activity via broadcast receiver in kotlin

I have a situation where I need to pass a list from adapter to activity, then pass that same list from activity to another adapter. So, I am passing a list from adapter to activity like this, and it works.
if(filteredList.isNotEmpty()){
var json = Gson().toJson(filteredList)
intentPassList.putExtra("filteredList", json)
LocalBroadcastManager.getInstance(holder.context).sendBroadcast(intentPassList)
}
Next, I am receiving my list successfully like this in my activity class
private val receiverFilteredList = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
var recList = intent.getStringExtra("filteredList")
if (recList != null){
var token: TypeToken<ArrayList<Model?>?> = object : TypeToken<ArrayList<Model?>?>() {}
myFilteredList = Gson().fromJson(recList, token.type) // list is received correctly
}
}
}
Now I need to pass this received list to another adapter (recycler view) and my idea was to do this in onCreate in my activity
private var myFilteredList: ArrayList<Model> = arrayListOf()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
LocalBroadcastManager.getInstance(this).registerReceiver(receiverFilteredList, IntentFilter("filteredList"))
if (myFilteredList?.isNotEmpty() == true){
adapter = NewAdapter(myFilteredList!!)
myRecycler?.layoutManager = manager
myRecycler?.adapter = adapter
} else {
adapterInfo = NewAdapter(completeList)
myRecycler?.layoutManager = manager
myRecycler?.adapter = adapter
}
}
}
but it always sends completeList to adapter, because myFilteredList is null. How can i correctly pass received list in receiver to myFilteredList in onCreate?
NewAdapterClass
class NewAdapter(var itemList: ArrayList<Model>) :
RecyclerView.Adapter<NewAdapter.ViewHolder>() {
private lateinit var getContext: Context
class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
var x: TextView = view.txtX
val y: TextView = view.txtY
val z: TextView = view.txtZ
val localPreferances = PreferencesManager()
val context: Context = view.context
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
getContext = parent.context
val layoutView = LayoutInflater.from(parent.context).inflate(R.layout.listview_item_list_layout, parent, false)
return ViewHolder(layoutView)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
var receivedList = itemList[position]
if (receivedList?.dataA != null) {
holder.y?.text = "${receivedList?.dataA}"
} else holder.y?.text = ""
if (receivedList?.dataB != null) {
holder.x?.text = "${receivedList?.dataB}"
} else holder.x?.text = ""
if (receivedList?.dataC != null) {
holder.z?.text = "${receivedList?.dataC}"
} else holder.z?.text = ""
}
override fun getItemCount() = itemList.size
}
You need to inform Adapter about changes to it's data source which is List in your case, also myFilteredList = Gson().fromJson(recList, token.type) will create new instance of ArrayList and assign it to myFilteredList, but the initial instance provided to your adapter was different
private val receiverFilteredList = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
var recList = intent.getStringExtra("filteredList")
if (recList != null){
var token: TypeToken<ArrayList<Model?>?> = object : TypeToken<ArrayList<Model?>?>() {}
myFilteredList.clear() //clear existing data
myFilteredList.addAll(Gson().fromJson(recList, token.type)) // add new data
adapter.notifyDataSetChanged() //reset the list
}
}
}
I had to change approach, broadcast receiver wasn't working for me. I sent the list (Gson().toJson) from adapter via Intent.putExtra, and in the activity I received it like this
recList= intent.getStringExtra("filteredList")
if (recList!= null){
var token: TypeToken<ArrayList<Model?>?> = object : TypeToken<ArrayList<Model?>?>() {}
myFilteredList?.clear()
myFilteredList= Gson().fromJson(recList, token.type)
}
Now everything is working fine.

Why does my Recyclerview add new items at random positions?

After much reading on how to add an item to the top (or bottom), I realized that list.add(position, item) along with notifyDataSetChanged() is the correct way to add an item at the top (index 0). However, in my code, even after doing that, my RecyclerView adds positions at random places. Below is my CommentsActivity code:
class CommentsActivity : AppCompatActivity() {
private var clickedPostUid = ""
private var currentUserUid = ""
private lateinit var binding_commentsActivity: ActivityCommentsBinding
private val currentUserUID = FirebaseAuth.getInstance().currentUser?.uid!!
private val dbRef = FirebaseDatabase.getInstance().reference
private val uidRef = currentUserUID.let { dbRef.child("Users").child(it) }
private var commentAdapterView: CommentsAdapter? = null
private var commentsList: MutableList<CommentsModel>? = null
var commentsActivityRecyclerView: RecyclerView? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding_commentsActivity = ActivityCommentsBinding.inflate(layoutInflater)
clickedPostUid = intent.getStringExtra("keyIdentifierPostUid")!!
currentUserUid = intent.getStringExtra("keyIdentifierCurrentUserUid")!!
commentsActivityRecyclerView =
binding_commentsActivity.commentsActivityRecyclerView
val layoutManager = LinearLayoutManager(this)
layoutManager.reverseLayout = true
layoutManager.stackFromEnd = true
commentsActivityRecyclerView!!.layoutManager = layoutManager
commentsList = ArrayList()
commentAdapterView = CommentsAdapter(this, commentsList as ArrayList<CommentsModel>)
commentsActivityRecyclerView!!.adapter = commentAdapterView
commentsActivityRecyclerView!!.setHasFixedSize(true)
LoadAndDisplayComments()
And this is my LoadAndDisplayComments()
private fun LoadAndDisplayComments() {
val commentsRef = dbRef.child("Comments").child(clickedPostUid)
commentsRef.addValueEventListener(object : ValueEventListener {
override fun onCancelled(p0: DatabaseError) {
TODO("Not yet implemented")
}
override fun onDataChange(p0: DataSnapshot) {
if (p0.exists())
{
commentsList!!.clear()
for (snapshot in p0.children)
{
val getCommentDetails = snapshot.getValue(CommentsModel::class.java)
if (getCommentDetails != null) {
commentsList!!.add(0,getCommentDetails)
}
}
commentAdapterView!!.notifyDataSetChanged()
}
else{
Toast.makeText(this#CommentsActivity, "No comments" , Toast.LENGTH_SHORT).show()
}
}
})
}
This is my comments adapter
class CommentsAdapter(
private var commentsContext : Context,
private var commentsList: MutableList<CommentsModel>
) : RecyclerView.Adapter<CommentsAdapter.CommentsViewHolder>()
{
inner class CommentsViewHolder(#NonNull itemView: View) : RecyclerView.ViewHolder(itemView) {
val showComment : TextView = itemView.comments_recview_item_comment
val showImage : CircleImageView = itemView.comments_recview_item_profile_image
val showUsername : TextView = itemView.comments_recview_item_username
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CommentsViewHolder {
val itemView = LayoutInflater.from(commentsContext)
.inflate(R.layout.comment_recyclerview_item, parent, false)
return CommentsViewHolder(itemView)
}
override fun getItemCount(): Int {
return commentsList!!.size
}
override fun onBindViewHolder(holder: CommentsViewHolder, position: Int) {
val currentUser = FirebaseAuth.getInstance().currentUser!!
val comment = commentsList!![position]
holder.showComment.text = comment.comment
}
I need that a new comment to be displayed at the top or bottom of the recyclerview. I know how to scroll to 0 index using .SmoothScroll. Would be happy to post the XML if needed. Thank you.
Turns out the error was not in the RecyclerView. It was the way I was storing the values in the database. I was creating a unique Id for my values in Firebase that was causing the error. Instead I used .push to generate unique keys and it worked fine.
Wrong approach (or just not what I wanted):
val uniqueCommentId = UUID.randomUUID().toString()
.replace("-","").toUpperCase(Locale.ROOT)
val postCommentsRef = FirebaseDatabase.getInstance().reference
.child("Comments")
.child(postId).child(uniqueCommentId )
postCommentRef.setValue()
Better approach:
val postCommentsRef = FirebaseDatabase.getInstance().reference
.child("Comments")
.child(postId)
val commentMap = HashMap<String, Any>()
commentMap["userUid"] = currentUserUid
commentMap["postId"] = postId
commentMap["comment"] = comment
postCommentsRef.push().setValue(commentMap)
Worked fine for me after this.

How to enable next item in RecyclerView?

I want the next item in the RecyclerView can be clicked. For example, if item 1 has score > 10, then the item 2 can be clicked, but the item 3,4,5,6... cannot be clicked. If item 2 has scrore > 10,then the item 3 can be clicked,but the item 4,5,6,7... cannot be clicked. If item 3 has score > 10, then the item 4 can be clicked, but the item 5,6,7,8... cannot be clicked.
class RecyclerView
class MathCategoryDetailActivity : AppCompatActivity() {
private var progressBar: ProgressBar? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_math_category_detail)
progressBar = findViewById(R.id.progressBar)
recycler_math_category_detail.layoutManager = GridLayoutManager(this, 2)
recycler_math_category_detail.setHasFixedSize(true)
val adapter = MathCategoryDetailAdapter(
this,
DBHelperOther.getInstance(this).allCategoryDetail(Common.selectedCategory!!.id)
)
Common.selectedCategory!!.id
recycler_math_category_detail.addItemDecoration(
SplacesItemDecoration(
4
)
)
recycler_math_category_detail.adapter = adapter
}
}
Adapter of RecyclerView
class MathCategorySpecifyDetailAdapter(
internal var context: Context,
internal var categoryDetailList: List<MathCategorySpecifyDetail>) :
RecyclerView.Adapter<MathCategorySpecifyDetailAdapter.MyViewHolder1>() {
private var dialog: Dialog? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder1 {
val itemView =
LayoutInflater.from(context).inflate(R.layout.activity_math_category_specify_detail_adapter, parent, false)
return MyViewHolder1(itemView)
}
override fun getItemCount(): Int {
return categoryDetailList.size
}
override fun onBindViewHolder(holder: MyViewHolder1, position: Int) {
holder.card_category_detail_2.isEnabled = false
holder.txt_category_specify_detail_2_name.isEnabled = false
//if(the id =1 , item can be clicked)
if(categoryDetailList[position].id == 1) {
holder.card_category_detail_2.isEnabled = true
holder.txt_category_specify_detail_2_name.isEnabled = true
if(categoryDetailList[position].highScore> 10) {
//I added code here, but it doens't work
categoryDetailList[position].id++
holder.card_category_detail_2.isEnabled = true
}
}
holder.txt_category_specify_detail_2_name.text = categoryDetailList[position].name
holder.score.text = categoryDetailList[position].highScore.toString()
dialog = Dialog(context)
dialog!!.setContentView(R.layout.activity_lesson)
holder.card_category_detail_2.setOnClickListener {
var txtLesson: TextView = dialog!!.findViewById(R.id.txtLesson)
txtLesson.text = categoryDetailList[position].lesson
var txtDescribe: TextView = dialog!!.findViewById(R.id.txtDescribe)
txtDescribe.text = categoryDetailList[position].nameOfTypeDetail
var txtType: TextView = dialog!!.findViewById(R.id.txtType)
txtType.text = categoryDetailList[position].mathCategoryDetail
var btnPlay: Button =dialog!!.findViewById(R.id.btnPlay)
btnPlay.setOnClickListener{
Common2.selectedCategoryId = categoryDetailList[position]
val intent = Intent(context, QuestionActivity::class.java)
context.startActivity(intent)
}
var window : Window? = dialog!!.window
window!!.setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT)
dialog!!.show()
}
}
inner class MyViewHolder1(itemView: View) : RecyclerView.ViewHolder(itemView),
View.OnClickListener {
internal val txt_category_specify_detail_2_name: TextView
internal val score: TextView
internal val card_category_detail_2: CardView
internal lateinit var iOnRecyclerViewItemClickListener2: IOnRecyclerViewItemClickListener2
fun setiOnRecyclerViewItemClickListener2(iOnRecyclerViewItemClickListener2: IOnRecyclerViewItemClickListener2) {
this.iOnRecyclerViewItemClickListener2 = iOnRecyclerViewItemClickListener2
}
init {
txt_category_specify_detail_2_name =
itemView.findViewById(R.id.txt_category_specify_detail_2_name) as TextView
score = itemView.findViewById(R.id.score) as TextView
card_category_detail_2 = itemView.findViewById(R.id.card_category_detail_2) as CardView
itemView.setOnClickListener(this)
}
override fun onClick(view: View) {
iOnRecyclerViewItemClickListener2.onClick(view, adapterPosition)
}
}
}
MathCategorySpecifyDetail
class MathCategorySpecifyDetail(
var id: Int,
var idMathCategoryDetail: Int,
var name: String,
var lesson: String,
var nameOfTypeDetail: String,
var mathCategoryDetail: String,
var highScore: Int
)
Common1
object Common1 {
var selectedCategoryIdDetail: MathCategoryDetail? = null
}
I have tried in many ways but it doesn't work. Please help me
Try creating a method inside the adapter that will enable/disable the items.
For example:
fun toggleEnabled(enabled: Boolean){
//enable or disable the items according to the score
}
Use RxJava or LiveData for observing the score. It will check and trigger the toggleEnabled() every time when the score is changed.
p.s. You can get items by position (f.e. if the score is between 10 and 20 you will get the item with index of 1 from your adapter and call toggleEnabled() for that item.)

How can i make my spinner value set to the last value i selected before leaving the page when i come back in kotlin

I want to make sure when I select an item from the dropdown spinner and leave the page anytime I come back to the page the last selected item should be the current value of the dropdown
At the moment when I return to the page the first value in my dropdown is what appears in the spinner and I need it to be the last selected value from the spinner
override fun initialize() {
val customerLocation = ParentActivity.getCustomerLocation()
stateItem = ""
areaItem = ""
backBtn= PageView?.findViewById(R.id.backBtn)!!
backBtn.setOnClickListener {
ParentActivity.removeFragment()
}
submitLocationBtn = PageView?.findViewById(R.id.submitLocationBtn)!!
userLocation = PageView?.findViewById(R.id.userLocation)!!
stateSpinner = PageView?.findViewById(R.id.stateSpinner)!!
areaSpinner = PageView?.findViewById(R.id.areaSpinner)!!
if (customerLocation == null) {
userLocation.text = ""
} else {
userLocation.text = customerLocation!!.Area + ", " + customerLocation!!.State
}
stateSpinner.setOnItemSelectedListener(ParentActivity)
areaSpinner.setOnItemSelectedListener(ParentActivity)
areaSpinnerAdapter = AreaDropDownAdapter(areaList, ParentActivity)
areaSpinner?.adapter = areaSpinnerAdapter
stateSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
stateItem = stateList[p2].Name
getArea(stateItem)
}
override fun onNothingSelected(p0: AdapterView<*>?) {
}
}
areaSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
areaItem = areaList[p2].Name
override fun onNothingSelected(p0: AdapterView<*>?) {
}
}
submitLocationBtn.setOnClickListener {
customerLocation!!.State = stateItem
customerLocation.Area = areaItem
var location = customerLocation!!.Area + ", " + customerLocation!!.State
ParentActivity.getInAppDB().saveCustomerLocation(customerLocation!!)
userLocation.text = location
replaceFragment(Home(), removeFragment = false, showBottom = false)
}
}
You can select an item in Spinner by the following code
spinner.setSelection(index)
In your case what you can do is:
Save the selected item maybe you can use SharedPreferences (your case: location name)
When come back to the same view get the selected item from SharedPreferences
Find the index of that item (your case: from the stateList)
Set the found index to spinner by spinner.setSelection(index)
It should like this
stateInt = preference.getInt(customerLocation!!.State, 0)
stateSpinner.setSelection(stateInt)
stateSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
stateItem = stateList[p2].Name
getArea(stateItem)
preference.edit {
putInt(customerLocation!!.State, stateInt)
}
}
override fun onNothingSelected(p0: AdapterView<*>?) {
}
}

How to use multiple countDownTimer into recyclerView on Android

In my application i want use multiple CountDownerTimer for show offer times into RecyclerView.
For write this application i used Kotlin language.
I write below codes, but when scrolling on recyclerView's items start again this timer!
My mean is, timer started from 4:19sec, when scrolling on items and after 10sec show me 4:19 again instead of 4:09!
Activity codes:
class MainActivity : AppCompatActivity() {
private lateinit var apisList: ApisList
private lateinit var retrofit: Retrofit
private lateinit var todayAdapter: AuctionsTodayAdapter
private val todayModel: MutableList<Today> = mutableListOf()
private lateinit var layoutManager: RecyclerView.LayoutManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//Initialize
retrofit = ApiClient.instance
apisList = retrofit.create(ApisList::class.java)
todayAdapter = AuctionsTodayAdapter(themedContext, todayModel)
layoutManager = LinearLayoutManager(themedContext)
//RecyclerView
main_list.setHasFixedSize(true)
main_list.layoutManager = layoutManager
main_list.adapter = todayAdapter
if (isNetworkAvailable()) getData(1, 10)
}
private fun getData(page: Int, limit: Int) {
main_loader.visibility = View.VISIBLE
val call = apisList.getAuctionsToday(page, limit)
call.let {
it.enqueue(object : Callback<AuctionsTodayResponse> {
override fun onFailure(call: Call<AuctionsTodayResponse>, t: Throwable) {
main_loader.visibility = View.GONE
Log.e("auctionsTodayList", t.message)
}
override fun onResponse(call: Call<AuctionsTodayResponse>, response: Response<AuctionsTodayResponse>) {
if (response.isSuccessful) {
response.body()?.let { itBody ->
main_loader.visibility = View.GONE
if (itBody.toString().isNotEmpty()) {
todayModel.clear()
todayModel.addAll(itBody.res.today)
todayAdapter.notifyDataSetChanged()
}
}
}
}
})
}
}
}
Adapter codes:
class AuctionsTodayAdapter(val context: Context, val model: MutableList<Today>) :
RecyclerView.Adapter<AuctionsTodayAdapter.MyHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyHolder {
val view = LayoutInflater.from(context).inflate(R.layout.row_main_list, parent, false)
val holder = MyHolder(view)
//holder.setIsRecyclable(false)
return holder
}
override fun getItemCount(): Int {
return model.size
}
override fun onBindViewHolder(holder: MyHolder, position: Int) {
val modelUse = model[position]
holder.setData(modelUse)
if (holder.newCountDownTimer != null) {
holder.newCountDownTimer!!.cancel()
}
var timer = modelUse.calculateEnd
timer = timer * 1000
holder.newCountDownTimer = object : CountDownTimer(timer, 1000) {
override fun onTick(millisUntilFinished: Long) {
var seconds = (millisUntilFinished / 1000).toInt()
val hours = seconds / (60 * 60)
val tempMint = seconds - hours * 60 * 60
val minutes = tempMint / 60
seconds = tempMint - minutes * 60
holder.rowMain_timer.rowMain_timer.text =
String.format("%02d", hours) + ":" + String.format(
"%02d",
minutes
) + ":" + String.format("%02d", seconds)
}
override fun onFinish() {
holder.rowMain_timer.text = "00:00:00"
}
}.start()
}
inner class MyHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var newCountDownTimer: CountDownTimer? = null
lateinit var rowMain_timer: TextView
init {
rowMain_timer = itemView.findViewById(R.id.rowMain_timer)
}
fun setData(model: Today) {
model.image.let {
Glide.with(context)
.load(Constants.MAIN_BASE_URL + it)
.apply(RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.RESOURCE))
.into(itemView.rowMain_img)
}
model.title.let { itemView.rowMain_title.text = it }
}
}
How can I fix it?
How can I fix it?
ViewHolder is destroyed after you scroll it down or up (making not visible). You should store timer value when it is destroyed and restore value when it is visible again.
Welcome to the recyclerview architecture!
First of all you ought to understand that onBindViewHolder can be called multiple times when a view is scrolled out and in from your screen, and you are starting a timer in every call.
What you should do is to save the timer remaining millis in onTick in the viewHolder data and start a timer with this remaining time.
Update:
In your viewHolder class, use a long variable that represents how much time left for coutDown, lets call it timeLeft. In onTick method type holder.timeLeft = millisUntilFinished and in onFinish method type holder.timeLeft = -1 .
Now, when initiating a new timer, after checking the timer is not null check,
if (holder.leftTime > 0) {
holder.newCountDownTimer = object : CountDownTimer(holder.timeLeft, 1000) {
...
}
}
If the timeLeft is -1, decide what to do with the timer.
Also, you might need to change the ticking from every 1000 millis to smaller number, like 100 millis, for better accurate timing when saving holder.timeLeft.
Hope this will answer your question.

Categories