I'm new to Android development. I'm trying to add a Multi countdown timer in recycler view but it does not work. Editing and deleting items in the list are okay, but I have no idea how to add a countdown timer function.
When I click the play button for starting the countdown, nothing happens. I would really appreciate it if you could tell me with a simple example.
Here is Adapter.kt.
class UserAdapter(val c: Context, val userList:ArrayList<UserData>): RecyclerView.Adapter<UserAdapter.UserViewHolder>() {
inner class UserViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
var name: TextView
var DeleteButton: ImageView
var EditButton: ImageView
var PlayButton: ImageView
val start = 600_000L
var timer = start
lateinit var countDownTimer: CountDownTimer
init {
name = v.findViewById<TextView>(R.id.alarm_name)
DeleteButton = v.findViewById(R.id.alarm_button_delete)
EditButton=v.findViewById(R.id.alarm_button_edit)
PlayButton=v.findViewById(R.id.alarm_button_start)
DeleteButton.setOnClickListener { DeleteItem(it) }
EditButton.setOnClickListener { EditItem(it) }
PlayButton.setOnClickListener { PlayItem(it)}
}
private fun PlayItem(v: View) {
countDownTimer = object : CountDownTimer(timer,1000){
override fun onFinish() {
}
override fun onTick(millisUntilFinished: Long) {
}
}.start()
}
private fun DeleteItem(v: View) {
userList.removeAt(adapterPosition)
notifyDataSetChanged()
Toast.makeText(c, "Deleted this Information", Toast.LENGTH_SHORT).show()
}
private fun EditItem(v: View){
val position = userList[adapterPosition]
val v = LayoutInflater.from(c).inflate(R.layout.add_item,null)
val name = v.findViewById<EditText>(R.id.add_alarm_name)
AlertDialog.Builder(c)
.setView(v)
.setPositiveButton("Ok"){
dialog,_->
position.add_alram_name = name.text.toString()
notifyDataSetChanged()
Toast.makeText(c,"User Information is Edited",Toast.LENGTH_SHORT).show()
dialog.dismiss()
}
.setNegativeButton("Cancel"){
dialog,_->
dialog.dismiss()
}
.create()
.show()
true
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
val inflater = LayoutInflater.from(parent.context)
val v = inflater.inflate(R.layout.list_item, parent, false)
return UserViewHolder(v)
}
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
val newList = userList[position]
holder.name.text = newList.add_alram_name
}
override fun getItemCount(): Int {
return userList.size
}
Seems like your adapter is not holding instance for each row items, as per recycling behavior the timer unable to hold the view position.
you can refer this
Related
I have a list of item in RecylerView, with have a delete button with delete the item from the list.
When I click the delete button, the item at last got deleted not the item selected to be deleted.
What's more, it creates a new item with old item's data instead of new data.
The following below is the code written in Kotlin:
class MedicineAdapter(private val activity: Activity, private val dataSet: MutableList<MedicineModel>, private val medicineSet: List<MedicineData>): RecyclerView.Adapter<MedicineAdapter.ViewHolder>() {
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val deleteLayout: LinearLayout
val medicineLayout: LinearLayout
val medicineName: TextView
val timeLayout: LinearLayout
val medicineTime: TextView
init {
deleteLayout = view.findViewById(R.id.delete_layout)
medicineLayout = view.findViewById(R.id.medicine_layout)
medicineName = view.findViewById(R.id.medicine_name)
timeLayout = view.findViewById(R.id.time_layout)
medicineTime = view.findViewById(R.id.medicine_time)
}
}
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(viewGroup.context)
.inflate(R.layout.layout_medicine_item, viewGroup, false)
return ViewHolder(view)
}
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
viewHolder.deleteLayout.setOnClickListener {
deleteRecord(viewHolder.adapterPosition)
}
viewHolder.medicineLayout.setOnClickListener {
val spinnerDialog = SpinnerDialog(activity, medicineSet.map { p -> p.name } as ArrayList<String>, "请选择药品", "关闭")
spinnerDialog.setCancellable(true)
spinnerDialog.setShowKeyboard(false)
spinnerDialog.bindOnSpinerListener { item, position ->
viewHolder.medicineName.text = item
}
spinnerDialog.showSpinerDialog()
}
}
override fun getItemCount(): Int {
return dataSet.count()
}
fun addRecord() {
val position = dataSet.count()
dataSet.add(position, MedicineModel())
notifyItemInserted(position)
}
#SuppressLint("NotifyDataSetChanged")
private fun deleteRecord(position: Int) {
dataSet.removeAt(position)
notifyItemRemoved(position)
notifyDataSetChanged()
}
I have called notifyDataSetChanged however it does not seems to work.
I tried my best to make something work with my recyclerView and my onClickListener. Two days ago I have no idea how but I made it work.
Today I tried to work on my app again, and well it's not working anymore. I changed nothing in the code, so I don't get it.
My adapter class with my onClickListener code in my onBindViewHolder :
class LvlAdapter( var items: List<Lvl>, private var c:Context) : RecyclerView.Adapter<LvlAdapter.LvlViewHolder>() {
val limit = 10;
inner class LvlViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var titleLvl: TextView
var buttonLvl: ImageView
init {
titleLvl = itemView.findViewById(R.id.titleLvl)
buttonLvl = itemView.findViewById(R.id.buttonLvl)
}
fun bind(lvl: Lvl) {
titleLvl.text = lvl.title
buttonLvl.setImageResource(lvl.button)
}
}
// return the viewHolder with the item view, but inflate from xml to view first
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LvlViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.lvl_card, parent, false)
return LvlViewHolder(itemView)
}
// what we bind in the viewHolder (ce qu'on injecte)
override fun onBindViewHolder(holder: LvlViewHolder, position: Int) {
var lvl = items[position]
holder.bind(lvl)
holder.itemView.setOnClickListener{
var mode1 = items.get(position)
var title1 : String = mode1.title
var intent = Intent(c, GameQuestionsActivity::class.java)
intent.putExtra("title", title1)
c.startActivity(intent)
}
}
// return a size of the items
override fun getItemCount(): Int {
if(items.size > limit){
return limit
} else {
return items.size
}
}
}
How to display some information from recyclerview selected item without using onClick method. When the app is started first item is selected and highlighted. I need to eg. use Toast with value of anything that is in data class. I have implemented onClick method but the question is how to do it without using this method.
This is MainActivity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val exampleList = generateDummyList(20)
val exampleAdapter = ExampleAdapter(getItem, exampleList)
exampleAdapter.onItemClick = { item, position: Int ->
Toast.makeText(this, "Position: $position", Toast.LENGTH_SHORT).show()
val intent = Intent(this, ItemActivity::class.java).apply {
putExtra("itempos", position)
putExtra("maxSize", maxS)
}
startActivity(intent)
}
}
}
This is adapter:
class ExampleAdapter(val chosen_item: Int, private val exampleList: List<ExampleItem>):
RecyclerView.Adapter<ExampleAdapter.ExampleViewHolder>()
{
var onItemClick: ((ExampleItem, Int) -> Unit)? = null
var selected_item: Int = chosen_item
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ExampleViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.item_recy, parent, false)
return ExampleViewHolder(itemView)
}
override fun onBindViewHolder(holder: ExampleViewHolder, position: Int){
val currentItem = exampleList[position]
holder.tv_ID.text = currentItem.id.toString()
holder.tv_NAME.text = currentItem.name
holder.tv_EMAIL.text = currentItem.email
if (position == selected_item){
holder.tv_NAME.setBackgroundColor(Color.GREEN)
} else {
holder.tv_NAME.setBackgroundColor(Color.TRANSPARENT)
}
}
override fun getItemCount(): Int {
return exampleList.size
}
inner class ExampleViewHolder(itemView:View): RecyclerView.ViewHolder(itemView) {
val tv_ID: TextView = itemView.tv_ID
val tv_NAME: TextView = itemView.tv_NAME
val tv_EMAIL: TextView = itemView.tv_EMAIL
init {
itemView.setOnClickListener{
onItemClick?.invoke(exampleList[absoluteAdapterPosition], absoluteAdapterPosition)
notifyItemChanged(selected_item)
selected_item = absoluteAdapterPosition
notifyItemChanged(selected_item)
}
itemView.isSelected
}
}
}
I have second activity - when user click on item in first activity(recyclerview) - this second activity is open - then I raise the id of item by one and open again first activity where another item is highlighted. And I need to display eg. EMAIL from ExampleItem class.
This is second activity:
class ItemActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_item)
var itempos = intent.getIntExtra("itempos",0)
val maxSize = intent.getIntExtra("maxSize",0)
button2.setOnClickListener {
if (itempos == maxSize){
itempos = itempos
} else {
itempos = itempos + 1
}
val intent = Intent(this, MainActivity::class.java).apply {
putExtra("itemposplus", itempos)
}
startActivity(intent)
}
}
}
If I understood correctly, you want to get the selected item at any time (without a click). There are several ways to do this. I recommend to you use getAdapterPosition() method in ViewHolder
First, save your ViewHolder
class ExampleAdapter(val chosen_item: Int, private val exampleList: List<ExampleItem>):
RecyclerView.Adapter<ExampleAdapter.ExampleViewHolder>()
{
var onItemClick: ((ExampleItem, Int) -> Unit)? = null
var selected_item: Int = chosen_item
lateinit var viewHolder: ExampleViewHolder
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ExampleViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.item_recy, parent, false)
viewHolder = ExampleViewHolder(itemView)
return viewHolder
}
And then write a public method into the adapter for get the current item in activity
fun getCurrentItem(): ExampleItem = exampleList.get(viewHolder.getAdapterPosition())
Finally you can get selected item in activity
val selectedItem = exampleAdapter.getCurrentItem()
Also you can check getLayoutManager().findFirstVisibleItemPosition() method in your RecyclerView
I would like to start a new activity from a RecyclerView with Kotlin.
I am still exploring Kotlin and currently stuck on how to open a new activity from a RecyclerView.
class HomeScreenRecyclerAdapter : RecyclerView.Adapter<HomeScreenRecyclerAdapter.ViewHolder>()
{
private val titles = arrayOf("About Me",
"About Me", "About Me", "About Me"
)
private val details = arrayOf("Item one details", "Item two details",
"Item three details", "Item four details")
private val images = intArrayOf(R.drawable.ic_launcher_background,
R.drawable.ic_launcher_background, R.drawable.ic_launcher_background,
R.drawable.ic_launcher_background)
override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): ViewHolder {
val v = LayoutInflater.from(viewGroup.context)
.inflate(R.layout.main_card_view, viewGroup, false)
return ViewHolder(v)
}
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
viewHolder.itemTitle.text = titles[position]
viewHolder.itemDetail.text = details[position]
viewHolder.itemImage.setImageResource(images[position])
}
override fun getItemCount(): Int {
return titles.size
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val intent: Intent? = null
var itemImage: ImageView
var itemTitle: TextView
var itemDetail: TextView
init {
itemImage = itemView.findViewById(R.id.main_image_view)
itemTitle = itemView.findViewById(R.id.main_title_view)
itemDetail = itemView.findViewById(R.id.main_description_view)
itemView.setOnClickListener {
}
}
}
}
I just cant figure out how to start a new activity for each item within the RecyclerView. I know I am making it more complicated than it is.
Just use this :
context.startActivity(Intent(context, Activity::class.java))
Just start the activity within the ViewHolder itemOnClick method as like below. And you need to pass the context reference of adapter's activity.
class HomeScreenRecyclerAdapter(var mContext:Context) : RecyclerView.Adapter<HomeScreenRecyclerAdapter.ViewHolder>()
{
private val titles = arrayOf("About Me",
"About Me", "About Me", "About Me")
private val details = arrayOf("Item one details", "Item two details",
"Item three details", "Item four details")
private val images = intArrayOf(R.drawable.ic_launcher_background,
R.drawable.ic_launcher_background, R.drawable.ic_launcher_background,
R.drawable.ic_launcher_background)
override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): ViewHolder {
val v = LayoutInflater.from(viewGroup.context)
.inflate(R.layout.main_card_view, viewGroup, false)
return ViewHolder(v)
}
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
viewHolder.itemTitle.text = titles[position]
viewHolder.itemDetail.text = details[position]
viewHolder.itemImage.setImageResource(images[position])
}
override fun getItemCount(): Int {
return titles.size
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val intent: Intent? = null
var itemImage: ImageView
var itemTitle: TextView
var itemDetail: TextView
init {
itemImage = itemView.findViewById(R.id.main_image_view)
itemTitle = itemView.findViewById(R.id.main_title_view)
itemDetail = itemView.findViewById(R.id.main_description_view)
itemView.setOnClickListener {
mContext.startActivity(Intent(mContext, ActivityNameWhichYouWantCall::class.java))
}
}
}
}
Just try this solution:
Firstly in your adapter write listener to your recyclerview items:
class HomeScreenRecyclerAdapter(val data: ArrayList<Data>) : RecyclerView.Adapter<ContactAdapter.ViewHolder>() {
private var listener: ((Data) -> Unit)? = null
...
fun setOnItemClickListener(f: (Data) -> Unit) {
listener = f
}
And inside init :
itemView.setOnClickListener {
listener?.invoke(data[adapterPosition])
}
And Finally in your Activity you can take handle item clickListeners:
private val data = ArrayList<Data>()
val adapter = HomeScreenRecyclerAdapter(data)
adapter.setOnItemClickListener {
when(it){
1->{startActivity(Intent(context, YourActivity1::class.java))}
2->{startActivity(Intent(context, YourActivity2::class.java))}
...
else->{}
}
}
There Data is model class of your item.
That's all, I hope this help you!
I am new to the Kotlin and I am trying to create an application with MVVM.
so what I am trying to do is to make a web call using retrofit in the repository and returning data to the view model and then observing the data from the fragment and as soon as data changes I am notifying it to the recycler view adapter everything is working fine.
Now, the problem is I want to update that live data of ViewHolder from the recyclerview. when I click on the CheckBox in recyclerView the data should be updated. But I don't know what is the actual way of doing this.
Here is my ViewHolder.
class MainActivityViewModel : ViewModel() {
private var mutableLiveGitUsers: MutableLiveData<ArrayList<GitUsers>>? = null
private lateinit var gitUsersRepository: GitUsersRepository
var allUsersListener: AllUsersListener? = null
fun init() {
gitUsersRepository = GitUsersRepository()
allUsersListener?.onStarted()
mutableLiveGitUsers = gitUsersRepository.getGitUsers()
allUsersListener?.onSuccess(mutableLiveGitUsers!!)
}
fun getGitUsersData(): MutableLiveData<ArrayList<GitUsers>>? {
return mutableLiveGitUsers
}
}
here is how I observe changes in fragment
mutableLiveGitUsers.observe(this, Observer {
progressBar.hide()
rvAllUsersAdapter = AllUsersAdapter(mainActivityViewModel.getGitUsersData()?.value!!)
rvAllUsers.adapter = rvAllUsersAdapter
rvAllUsersAdapter.notifyDataSetChanged()
})
RecylerView Adapter
class AllUsersAdapter(private var gitUsersArrayList: ArrayList<GitUsers>) :
RecyclerView.Adapter<AllUsersAdapter.AllUsersViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AllUsersViewHolder {
val inflater: LayoutInflater = LayoutInflater.from(parent.context)
val v: View = inflater.inflate(R.layout.items_all_users, parent, false)
return AllUsersViewHolder(v)
}
override fun getItemCount(): Int {
return gitUsersArrayList.size
}
override fun onBindViewHolder(holder: AllUsersViewHolder, position: Int) {
holder.tvUserName.text = gitUsersArrayList[position].login
Glide.with(holder.itemView.context)
.load(gitUsersArrayList[position].avatarUrl)
.centerCrop()
.into(holder.imageView)
holder.checkBox.isSelected = gitUsersArrayList.get(position).isSelected
holder.checkBox.setOnCheckedChangeListener{compoundButton, isChecked ->
if (isChecked){
/* Here I want to change the live data so I can observe that changes in my
fragment and can have an effect in UI*/
Log.d("TESTC","AllUsersAdapter IsChecked")
}else{
Log.d("TESTC","AllUsersAdapter UnChecked")
}
}
}
class AllUsersViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var tvUserName: TextView = itemView.findViewById(R.id.tvUserName)
var imageView: ImageView = itemView.findViewById(R.id.imageView)
var checkBox: CheckBox = itemView.findViewById(R.id.checkBox)
}
}
Introduce an item click callback to your adapter:
class AllUsersAdapter(private var gitUsersArrayList: ArrayList<GitUsers>,
private val itemClickCallback: ((Boolean) -> Unit)?) :
RecyclerView.Adapter<AllUsersAdapter.AllUsersViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AllUsersViewHolder {
val inflater: LayoutInflater = LayoutInflater.from(parent.context)
val v: View = inflater.inflate(R.layout.items_all_users, parent, false)
return AllUsersViewHolder(v)
}
override fun getItemCount(): Int {
return gitUsersArrayList.size
}
override fun onBindViewHolder(holder: AllUsersViewHolder, position: Int) {
holder.tvUserName.text = gitUsersArrayList[position].login
Glide.with(holder.itemView.context)
.load(gitUsersArrayList[position].avatarUrl)
.centerCrop()
.into(holder.imageView)
holder.checkBox.isSelected = gitUsersArrayList.get(position).isSelected
holder.checkBox.setOnCheckedChangeListener{compoundButton, isChecked ->
if (isChecked){
/* Here I want to change the live data so I can observe that changes in my
fragment and can have an effect in UI*/
Log.d("TESTC","AllUsersAdapter IsChecked")
itemClickCallback?.invoke(true)
}else{
Log.d("TESTC","AllUsersAdapter UnChecked")
itemClickCallback?.invoke(false)
}
}
}
class AllUsersViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var tvUserName: TextView = itemView.findViewById(R.id.tvUserName)
var imageView: ImageView = itemView.findViewById(R.id.imageView)
var checkBox: CheckBox = itemView.findViewById(R.id.checkBox)
}
}
Handle the click event in your fragment onViewCreated() where you initialise the adapter
val rvAdapter = AllUsersAdapter(
gitUsersArrayList = gitUsersArrayList, itemClickCallback = fun(status: Boolean) {
navController().navigate(
viewModel.updateValue(status)
)
}
)
Create the required ViewModel function:
class MainActivityViewModel : ViewModel() {
private var mutableLiveGitUsers: MutableLiveData<ArrayList<GitUsers>>? = null
private lateinit var gitUsersRepository: GitUsersRepository
var allUsersListener: AllUsersListener? = null
fun init() {
gitUsersRepository = GitUsersRepository()
allUsersListener?.onStarted()
mutableLiveGitUsers = gitUsersRepository.getGitUsers()
allUsersListener?.onSuccess(mutableLiveGitUsers!!)
}
fun getGitUsersData(): MutableLiveData<ArrayList<GitUsers>>? {
return mutableLiveGitUsers
}
fun updateValue(status: Boolean) {
//#Todo Set new value based on status received
// mutableLiveGitUsers.value =
}
}