I have a View Pager2 To hold my on boarding screens
The current screens change only when a user manually slides it.
I am trying to achieve a functionality where it auto slides ever 2 seconds and also the user has the option to manually slide.
The code of my View Pager2 Adapter is :
class ViewPagerAdapter(list: ArrayList<Fragment>, fm: FragmentManager, lifecycle: Lifecycle) : FragmentStateAdapter(fm , lifecycle) {
private val fragment_list :ArrayList<Fragment> = list
override fun getItemCount(): Int {
return fragment_list.size
}
override fun createFragment(position: Int): Fragment {
return fragment_list[position]
}
}
How can I Achieve it in Kotlin?
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 months ago.
Improve this question
I got crazy ı am not able find solution. Please help me. 2 days ı am looking for solution. I clearly watn that if I click any recylerView Item starting a new activity. Each item has to own its activity.How to implement codes?. Thanks in advance.
My adapter code:
class CustomAdapter(private val mList: List<ItemsViewModel>) : RecyclerView.Adapter<CustomAdapter.ViewHolder>() {
// create new views
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
// inflates the card_view_design view
// that is used to hold list item
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.card_view_design, parent, false)
return ViewHolder(view)
}
// binds the list items to a view
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val ItemsViewModel = mList[position]
// sets the image to the imageview from our itemHolder class
holder.imageView.setImageResource(ItemsViewModel.image)
// sets the text to the textview from our itemHolder class
holder.textView.text = ItemsViewModel.text
holder.itemView.setOnClickListener {
}
}
// return the number of the items in the list
override fun getItemCount(): Int {
return mList.size
}
// Holds the views for adding it to image and text
class ViewHolder(ItemView: View) : RecyclerView.ViewHolder(ItemView) {
val imageView: ImageView = itemView.findViewById(R.id.imageview)
val textView: TextView = itemView.findViewById(R.id.textView)
}
}
My recyclerview.kt :
class recyclerView : AppCompatActivity() {
private lateinit var binding: ActivityRecyclerViewBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityRecyclerViewBinding.inflate(layoutInflater)
setContentView(binding.root)
supportActionBar?.hide()
// getting the recyclerview by its id
val recyclerview = findViewById<RecyclerView>(R.id.recyclerview)
// this creates a vertical layout Manager
binding.recyclerview.layoutManager = LinearLayoutManager(this)
// ArrayList of class ItemsViewModel
val data = ArrayList<ItemsViewModel>()
data.add(ItemsViewModel(R.drawable.jumping_jack, "Jumping Jack"))
data.add(ItemsViewModel(R.drawable.jumping_aquats, "Jumping Squat"))
// This will pass the ArrayList to our Adapter
val adapter = CustomAdapter(data)
// Setting the Adapter with the recyclerview
recyclerview.adapter = adapter
}
}
If you put a list object to adapter like param, you can get position of item, and use this position to start activity you want.
something like this:
add listener in your adapter
class CustomAdapter(
private val mList: List<ItemsViewModel>,
val clickListener: RecycleViewOnClickListener
) : RecyclerView.Adapter<CustomAdapter.ViewHolder>() {
...
}
in your bindViewHolder:
override fun onBindViewHolder(holder:
RecyclerView.ViewHolder,position: Int) {
holder.itemView.setOnClickListener {
clickListener.onItemClick(position)
}
}
and add simple interface:
interface RecycleViewOnClickListener {
fun onItemCLick(position: Int)
}
in your activity, get model from list with your position:
class recyclerView : AppCompatActivity(), RecycleViewOnClickListener {
override fun onItemClick(pos: Int) {
val model = yourList[pos]
//start new activity
}
}
then when you init adapter:
// This will pass the ArrayList to our Adapter
val adapter = CustomAdapter(data, this)
You can do this by using high order function it will be easy
In you adapter class (pass list and high order function as callback)
class YourAdapter(private val itemList: List<Model>, private val onItemClicked: (item) -> Unit) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
// your code
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
onItemClicked(itemList[position])
}
In your activity/Fragment where you are assigning adapter assign in this way.
whenever item is clicked it will land here, and you can start your activity, you will get item also from adapter on which user clicked
YourAdapter(list) { item ->
//start activity
}
Note: Always write some code with question, it will be easy for us to answer you and guide you what is wrong in your code.
Happy Coding!
I have an activity that is controlled with a navigation component, it has few fragments, inside one of these fragments there is a recyclerView that has some items, when I click on an Item I want it to navigate me to another fragment that has additional information about the item, I don't know how to use navigation component inside a recycelerView, when I type findNavController it has some parameters that am not sure what to put in or if its even the right function, I also tried to do it by code like this:
val fm = (context as AppCompatActivity).supportFragmentManager
fm.beginTransaction()
.replace(R.id.fragmentContainer, fragment)
.addToBackStack(null)
.commit()
by the way this is the code that asks for other parameters:
// it asks for a (fragment) or (activity, Int)
findNavController().navigate(R.id.action_settingsFragment_to_groupUnits)
the problem is when I navigate out of this fragment or use the drawer navigation (nav component for the other fragments), this fragment that I navigated to stays displayed in the screen, I see both fragments at the same time, I assume its a fragment backStack issue but I don't know how to solve it, thanks for the help and your time in advance
You do not need to navigate from RecyclerView item click to AdditionalDetails fragment directly.
You can do this same thing by help of interface.
Steps:
Create an interface with a method declaration.
Extend Interface from the fragment where you are using your RecyclerView and Implement interface method.
Pass this interface with the adapter.
Using the interface from adapter you just pass object when click on item.
Finally from your fragment you just navigate to AdditionalDetails fragment with argument.
Lets see sample code from my current project:
Interface
interface ChatListClickListener {
fun onChatListItemClick(view:View, user: User)
}
Adapter Class
class UserAdapter(val Users: List<User>, val chatListClickListener: ChatListClickListener) : RecyclerView.Adapter<UserAdapter.UserViewHolder>() {
inner class UserViewHolder(
val recyclerviewUsersBinding: RecyclerviewChatlistBinding
) : RecyclerView.ViewHolder(recyclerviewUsersBinding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
val vh = UserViewHolder(
DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.recyclerview_chatlist,
parent,
false
)
)
return vh
}
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
holder.recyclerviewUsersBinding.user = Users[position]
holder.recyclerviewUsersBinding.root.setOnClickListener{
chatListClickListener.onChatListItemClick(it,Users[position])
}
}
override fun getItemCount(): Int {
return Users.size
}
}
My fragment
class FragmentChatList : Fragment(), ChatListClickListener {
lateinit var binding: FragmentChatListBinding
lateinit var viewModel: ChatListViewModel
lateinit var listener: ChatListClickListener
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val args: FragmentChatListArgs by navArgs()
binding = FragmentChatListBinding.inflate(layoutInflater, container, false)
val factory = ChatListFactory(args.user)
viewModel = ViewModelProvider(this, factory).get(ChatListViewModel::class.java)
binding.viewModel = viewModel
listener = this
lifecycleScope.launch {
viewModel.addUserWhenUserConnect()
}
viewModel.userList.observe(viewLifecycleOwner, Observer { data ->
binding.rvChatList.apply {
layoutManager = LinearLayoutManager(requireContext())
setHasFixedSize(true)
adapter = UserAdapter(data, listener)
}
})
return binding.root
}
override fun onChatListItemClick(view: View, user: User) {
Toast.makeText(requireContext(), user.name + "", Toast.LENGTH_SHORT).show()
// here you navigate to your fragment....
}
}
I guess this will be helpful.
This question already has answers here:
RecyclerView Item Click Listener the Right Way
(14 answers)
Closed 1 year ago.
I want to access a variable in an adapter class called item so that I could use it in my main activity. So far I've tried adding open before the adapter class and using object and companion object. How can I fix this problem?
class GrammarAdapter(private val context: Context, private val items: ArrayList<String>) :
RecyclerView.Adapter<GrammarAdapter.ViewHolder>() {
class ViewHolder(binding: GrammarItemRowBinding) : RecyclerView.ViewHolder(binding.root) {
val tvItem = binding.tvItemName
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
GrammarItemRowBinding.inflate(LayoutInflater.from(context), parent, false)
)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = items[position]
holder.tvItem.text = item
holder.itemView.setOnClickListener {
when (item) {
"Countable and uncountable nouns" -> {
val intent = Intent(holder.itemView.context, CountableUncountable::class.java)
intent.putExtra("clickedGrammarTopic", item)
holder.itemView.context.startActivity(intent)
}
"Singular and plural nouns" -> {
val intent = Intent(holder.itemView.context, Singular and plural nouns::class.java)
intent.putExtra("clickedGrammarTopic", item)
holder.itemView.context.startActivity(intent)
}
...
As long as items is not private you can do something like:
val item = (myRecycler.adapter as MyAdapterClass).items[position]
However thats not really the best practice and you may want to rethink how your managing your apps data perhaps you should look into ViewModels
You have to use the callback pattern via an interface
interface Callback {
fun onClickedRow(item: String)
}
In the adapter constructor
class GrammarAdapter(... , private val callback: Callback) : ...
And modify the interaction
holder.itemView.setOnClickListener {
callback.onClickedRow(item)
}
You have to implement the interface in the activity or fragment
clas MyFragment : Callback {
override fun onClickedRow(item: String) {
//do something here with your item
}
}
And instantiate your adapter with the callback as an argument
val adapter = GrammarAdapter(..., this#MyFragment) //depending on your scope it can be simple this
Now when the user clicks the method override fun onClickedRow(... is going to be triggered on the activity or fragment
I have created a simple notes app that uses some of the Android Architecture components. I am using dataBinding to set data to my recycler view. One of the functionalities is bookmarking a note and displaying it in the Bookmarks fragment. In the Bookmarks fragment a user can tap the 'unbookmark' icon to remove a bookmarked note. I have used a simple on click listener on the icon inside my Bookmarks Recycler View Adapter to achieve this. I have a boolean property in my notes entity. Inside the Bookmarks fragment I update the bookmarks boolean value to false when the user taps the icon so that it changes to a false value(meaning it's not bookmarked and does not appear in the bookmarks fragment). However, when I click the 'unbookmark' icon the recycler view still displays the notes removed from the bookmarks.
Here is my Bookmarks Adapter :
class BookmarksAdapter(private var bookmarksList: List<Note>, var context: Context):
RecyclerView.Adapter<BookmarksAdapter.BookmarkViewHolder>(), CoroutineScope {
private lateinit var job: Job
var deletedId : Int? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BookmarkViewHolder {
val layoutItemBinding: BookmarksLayoutItemBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.bookmarks_layout_item,
parent,
false)
job = Job()
return BookmarkViewHolder(layoutItemBinding.root)
}
override fun onBindViewHolder(holder: BookmarkViewHolder, position: Int) {
val bookmarkNote: Note = bookmarksList.get(position)
holder.bookmarkBinding?.setVariable(BR.bookmarkItem, bookmarkNote)
holder.bookmarkBinding?.executePendingBindings()
holder.bookmarkBinding?.imageRemoveBookmark?.setOnClickListener {
removeBookmark(bookmarkNote, position)
}
}
override fun getItemCount(): Int {
return bookmarksList.size
}
inner class BookmarkViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var bookmarkBinding: BookmarksLayoutItemBinding? = DataBindingUtil.bind(itemView)
}
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
fun deleteNote(note : Note) {
launch(Dispatchers.IO) {
note.isBookmarked = false
NotesDatabase(context).getDao().updateNote(note)
}
val pos = bookmarksList.toMutableList().indexOf(note)
bookmarksList.toMutableList().removeAt(pos)
notifyItemRemoved(pos)
}
// Remove a bookmark
fun removeBookmark(bookmarkNote: Note, removedPos: Int) {
bookmarkNote.isBookmarked = false
launch (Dispatchers.IO){
NotesDatabase(context).getDao().updateNote(bookmarkNote)
withContext(Dispatchers.Main) {
bookmarksList.toMutableList().removeAt(removedPos)
notifyItemRemoved(removedPos)
notifyItemRangeChanged(removedPos, 1)
}
}
}
}
Below is my Bookmarks View Model:
class BookmarksViewModel(application: Application) : AndroidViewModel(application) {
val myContext: Context = application.applicationContext
private var _bookmarksList = MutableLiveData<List<Note>>()
val bookmarksList : LiveData<List<Note>>
get() = _bookmarksList
init{
viewModelScope.launch {
_bookmarksList.value = getNotes()
}
}
suspend fun getNotes() : List<Note> = NotesDatabase(myContext).getDao().getBookmarkedNotes()
}
Here is my Bookmarks Fragment:
class BookmarksFragment : BaseFragment(){
private lateinit var bookmarksBinding: FragmentBookmarksBinding
private lateinit var bookmarksViewModel: BookmarksViewModel
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
bookmarksBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_bookmarks, container, false)
bookmarksViewModel = ViewModelProvider(this).get(BookmarksViewModel::class.java)
// Observe the list of bookmarks
bookmarksViewModel.bookmarksList.observe(viewLifecycleOwner, { bList ->
bookmarksBinding.listOfBookmarks = bList
})
return bookmarksBinding.root
}
}
Here is my Notes Entity:
val bookmarkSate: Boolean
get() = true
#Entity
data class Note(
val title : String,
val note : String,
var isBookmarked : Boolean = false
) : Serializable{
#PrimaryKey(autoGenerate = true)
var noteID : Int = 0
}
Here is my Notes DAO:
#Dao
interface NoteDao {
#Insert
suspend fun saveNote(note : Note)
#Query("SELECT * FROM note ORDER BY noteID DESC")
suspend fun getAllNotes() : List<Note>
#Query("SELECT * FROM note WHERE isBookmarked")
suspend fun getBookmarkedNotes() : List<Note>
// add multiple notes
#Insert
suspend fun addMultipleNotes(vararg note: Note)
#Update
suspend fun updateNote(note: Note)
#Delete
suspend fun deleteNote(note : Note)
}
I am stuck on how to achieve the desired functionality of 'unbookmarking' a note and making it 'disappear 'from the bookmarks fragment. Kindly anyone who can help out?
There are a few problems and possible solutions I see here:
1-) In your adapter, in your delete method, you have this piece of code:
val pos = bookmarksList.toMutableList().indexOf(note)
bookmarksList.toMutableList().removeAt(pos)
notifyItemRemoved(pos)
The problem here is that toMutableList() doesn't change your bookmarksList as mutable, it returns a new mutable list filled with the items in your bookmarkList, a mutable version of your list, but a new list! So you remove the item from this new list, and not from the original list. Simply correcting this could fix the problem. (You can define the bookmarksList as a mutable list from the beginning)
2-) You seem to observe the list from your viewmodel, it is livedata in your viewmodel. But it is not defined as livedata in your dao. So you are not observing the changes in the database. So an alternative solution could be to observe the changes from the database by wrapping your list within a livedata in your dao.
#Query("SELECT * FROM note WHERE isBookmarked")
suspend fun getBookmarkedNotes() : LiveData<List<Note>>
But this second one would require some additional changes in your code.
I've tried to build a recyclerview with an adapter class and cardview layout however even though the code runs I cannot see the list with cardview (I just get a blank recyclerview)?
SpeakerAdapter.kt
class SpeakerAdapter (val speakers: ArrayList<String>): RecyclerView.Adapter<SpeakerAdapter.ViewHolder>() {
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val speakerName: TextView = itemView.findViewById(R.id.name)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.speakerName.text = speakers[position]
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val View = LayoutInflater.from(parent.context).inflate(R.layout.speaker_list, parent, false)
return ViewHolder(View)
}
override fun getItemCount() = speakers.size
}
Speaker.kt
class Speaker : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
setContentView(R.layout.activity_speaker)
val speakers : ArrayList<String> = ArrayList()
for (i in 1..100){
speakers.add("Speaker 1 #$i")
recyclerView_speaker.layoutManager = LinearLayoutManager(this)
recyclerView_speaker.adapter = SpeakerAdapter(speakers)
}
}}
It's a bit hard to debug without the xml. But here goes nothing:
I think it'd be worth a shot modifying your view holder class. The reference for the text view is being set when you init the view holder and the layout may not be inflated yet. Try finding the reference when you bind the data to the view to avoid this issue.
Try this:
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(name: String?) {
itemView. name.text = name
}
}
and then modify your adapter to call this method
class SpeakerAdapter (val speakers: ArrayList<String>): RecyclerView.Adapter<SpeakerAdapter.ViewHolder>() {
...
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(speakers[position])
}
}
PS.: it'd be a good idea to call your view holder something else. ViewHolder is a name that is already used by the android SDK. Ex.: SpeakerViewholder