I'm new to programming and trying to figure out how to stream .mp3 files in recyclerview
How to convert my Base Adapter into Recycler View Adapter? is there a simple way?
I need to do this because eventually I need a seekbar in every view of the recycler
My Base Adapter in Main Activity:
class MainActivity : AppCompatActivity() {
var listSongs = ArrayList<SongFile>()
var adapter : MySongAdapter? = null
var mp : MediaPlayer? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
listSongs.add(SongFile("title1","https://firebasestorage...."))
listSongs.add(SongFile("title2","https://firebasestorage...."))
listSongs.add(SongFile("title3","https://firebasestorage...."))
listSongs.add(SongFile("title4","https://firebasestorage....."))
adapter = MySongAdapter(listSongs)
listview.adapter = adapter
}
inner class MySongAdapter : BaseAdapter {
var myListSong = ArrayList<SongFile>()
constructor(myListSong: ArrayList<SongFile>) : super() {
this.myListSong = myListSong
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
var myview = layoutInflater.inflate(R.layout.mylayout,null)
var Song = this.myListSong[position]
myview.textView.text = Song.Title
myview.button.setOnClickListener {
if(myview.button.text == "STOP") {
mp!!.stop()
myview.button.text = "PLAY"
}
else{
mp = MediaPlayer()
try {
mp!!.setDataSource(Song.SongURL)
mp!!.prepare()
mp!!.start()
myview.button.text = "STOP"
}catch (e:Exception){}
}
}
return myview
}
override fun getItem(p0: Int): Any {
return myListSong[p0]
}
override fun getItemId(p0: Int): Long {
return p0.toLong()
}
override fun getCount(): Int {
return myListSong.size
}
}
}
My empty Recycler View Adapter:
class Adapter(context: Context, private val List: ArrayList<Model>) : RecyclerView.Adapter<Adapter.ViewHodler>() {
private val mContext: Context
init {
mContext = context
}
override fun onCreateViewHolder(parent: ViewGroup, p1: Int): Adapter.ViewHodler {
val row = LayoutInflater.from(mContext).inflate(R.layout.list_item,parent,false)
return ViewHodler(row)
}
override fun getItemCount(): Int {
return List.size
}
inner class ViewHodler(v : View) : RecyclerView.ViewHolder(v) {
val viewTitle = v.title
}
override fun onBindViewHolder(holder: ViewHodler, position: Int) {
val model = List[position]
holder.viewTitle!!.text = model.Title
}
}
`
inner class ViewHodler(v : View) : RecyclerView.ViewHolder(v) {
val viewTitle = v.findViewById(R.id.title) as TextView
val button = v.findViewById(R.id.button) as Button
}
override fun onBindViewHolder(holder: ViewHodler, position: Int) {
val model = List[position]
holder.viewTitle?.text = model.Title
holder.button.setOnClickListener {
if(holder.button.text == "STOP") {
mp?.stop()
myview.button.text = "PLAY"
}else{
mp = MediaPlayer()
try {
mp?.setDataSource(Song.SongURL)
mp?.prepare()
mp?.start()
myview.button.text = "STOP"
}catch (e:Exception){}
}
}
}`
You can also avoid the code in onBindViewHolder() and create a method ViewHolder something like onBind(file : SongFile) and pass the object to this method.
Related
I'm new to Kotlin. I have a MainActivity and a list view inside.
When an item is clicked in the list view, I want to update a textView inside MainActivity.
However because data should be passed from ListView to MainActivity, I did something weird:
MainActivity.kt:
data class Country(val imgRes:Int, val name:String)
class MainActivity : AppCompatActivity() {
private lateinit var binding : ActivityMainBinding
private val data = arrayListOf<Country>()
val imgRes = intArrayOf()
val data1 = arrayOf()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
for(i in imgRes.indices) {
val country = Country(imgRes[i], data1[i])
data.add(country)
}
val adapter = RecyclerAdapter(data, object:RecyclerAdapter.OnItemClickListener {
override fun onItemClick(v: View, pos: Int) {
binding.textView.text = (v as TextView).text
}
})
binding.recycler1.adapter = adapter
binding.recycler1.layoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)
}
}
ListView.kt:
class RecyclerAdapter(private val dataSet:List<Country>, private val listener:OnItemClickListener) : RecyclerView.Adapter<RecyclerAdapter.ViewHolder>() {
public interface OnItemClickListener {
fun onItemClick(v:View, pos:Int)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding = RowBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(binding)
}
override fun getItemCount(): Int {
return dataSet.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(position)
}
inner class ViewHolder(val binding: RowBinding) : RecyclerView.ViewHolder(binding.root) {
val rowImageView = binding.rowImageView
val rowTextView = binding.rowTextView
init {
binding.root.setOnClickListener {
listener.onItemClick(rowTextView, adapterPosition)
}
}
fun bind(pos: Int) {
with(binding) {
rowImageView.setImageResource(dataSet[pos].imgRes)
rowTextView.text = dataSet[pos].name
}
}
}
}
I declared interface(OnItemClickListener) at ListView.kt and redefine it at MainActivity.kt.
It works fine but is there any better way to do this?
You can use higher order functions instead of interface.
Define this function top of your Adapter:
var onClick: ((text: String) -> Unit)? = null
in ViewHolder:
binding.root.setOnClickListener {
onClick?.invoke(dataSet[position].text)
}
in MainActivity:
adapter.onClick = { name ->
binding.textView.text = name
}
You can also find more information about higher orders in here
<include android:layout_width="match_parent"
android:layout_height="wrap_content"
layout="#layout/yourlayout" />
Try this in your Adapter:
var itemClickListener: ((position: Int, name: String) -> Unit)? = null
Bindviewholder:
itemClickListner.invoke(1,"anyvalue")
in Activity:
adapter.itemClickListener = {
position, name ->
Toast.makeText(requireContext(),"position is $position name is $name ",Toast.LENGTH_SHORT).show()
}
in Adapter:
private var mListener: OnItemClickListener,
interface OnItemClickListener {
fun onClick(position: Int, routerArray: ArrayList<Router>)
}
binding.yourView.setOnClickListener {
mListener.onClick(adapterPosition, routerArray)
}
in Activity:
override fun onClick(position: Int, yourArray: ArrayList<Model>) {
binding.textView.text = yourArray[position].text
}
I'm trying to pass my binding variable for the adapter to the ViewHolder to assing values to the textviews in my adapter but the values are not assigned and the click listeners dont do anything.
Here's my adapter class:
class DoneAppointmentsAdapter(var context: DoneAppointmentsFragment, listener: ContentListener, var arrayList: List<Appointment>) :
RecyclerView.Adapter<DoneAppointmentsAdapter.ItemHolder>() {
private val listener: ContentListener = listener
private var binding: DoneAppointmentsAdapterBinding? = null
var activity = context
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): ItemHolder {
val viewHolder = LayoutInflater.from(parent.context)
.inflate(R.layout.done_appointments_adapter, parent, false)
binding = DoneAppointmentsAdapterBinding.inflate(LayoutInflater.from(parent.context))
return ItemHolder(viewHolder,binding, parent.context)
}
override fun onBindViewHolder(holder: ItemHolder, position: Int) {
holder.bind(arrayList, listener)
}
override fun getItemCount(): Int {
return arrayList.size
}
class ItemHolder(view: View, var binding: DoneAppointmentsAdapterBinding?, val context: Context) : RecyclerView.ViewHolder(view) {
fun bind(listOfData: List<Appointment>, listener: ContentListener) {
val dataListItem = listOfData[adapterPosition]
binding?.donePatientItemName?.text = "${dataListItem.patientName}"
binding?.donePatientItemTime?.text = "Some date"
binding?.donePatientItemPatientInfo?.setOnClickListener {
tempAppointmentId = dataListItem.id
listener.onPatientHistoryViewClicked(dataListItem.requestedBy)
}
binding?.donePatientItemReviewCase?.setOnClickListener {
amendPrescriptionWarning(dataListItem,context)
}
binding.donePatientItemViewCase.setOnClickListener {
startPreview(dataListItem)
}
}
}
}
You have messed up inside onCreateViewHolder . you have created view twice once with the LayoutInflater.from(parent.context) and once with DoneAppointmentsAdapterBinding.inflate . There should be only one in this case onlyDoneAppointmentsAdapterBinding .
class DoneAppointmentsAdapter(var context: DoneAppointmentsFragment, listener: ContentListener, var arrayList: List<Appointment>) :
RecyclerView.Adapter<DoneAppointmentsAdapter.ItemHolder>() {
private val listener: ContentListener = listener
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): ItemHolder {
val binding = DoneAppointmentsAdapterBinding.inflate(LayoutInflater.from(parent.context))
return ItemHolder(binding)
}
override fun onBindViewHolder(holder: ItemHolder, position: Int) {
holder.bind(arrayList, listener)
}
override fun getItemCount(): Int {
return arrayList.size
}
class ItemHolder (var binding: DoneAppointmentsAdapterBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(listOfData: List<Appointment>, listener: ContentListener) {
val dataListItem = listOfData[adapterPosition]
binding.donePatientItemName.text = "${dataListItem.patientName}"
binding.donePatientItemTime.text = "Some date"
binding.donePatientItemPatientInfo.setOnClickListener {
tempAppointmentId = dataListItem.id
listener.onPatientHistoryViewClicked(dataListItem.requestedBy)
}
binding.donePatientItemReviewCase.setOnClickListener {
amendPrescriptionWarning(dataListItem,binding.donePatientItemReviewCase.context)
}
binding.donePatientItemViewCase.setOnClickListener {
startPreview(dataListItem)
}
}
}
}
It should be like this . I have also removed extra useless arguments from ItemHolder .
To make it clear the problem with your code was RecyclerView.ViewHolder(view) you were passing view while you were doing all action and stuff on binding those holds are two different instances of View . Which should be fixed with above code.
I am new to Android & Kotlin. I want to click my row and intent it to other activity. That's why I tried to create an interface in my Adapter but it does not work. How can I create it?
My adapter code:
class NoteAdapter(private val titleTextArray: ArrayList<String>, private val imageArray: ArrayList<String>) : RecyclerView.Adapter<NoteAdapter.NoteHolder>() {
class NoteHolder (view: View) : RecyclerView.ViewHolder(view) {
var recyclerTitleText: TextView ?= null
var recyclerImageView: ImageView ?= null
init {
recyclerTitleText = view.findViewById(R.id.recyclerTitleText)
recyclerImageView = view.findViewById(R.id.recyclerImage)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NoteHolder {
val inflater = LayoutInflater.from(parent.context)
val view = inflater.inflate(R.layout.recycler_row, parent, false)
return NoteHolder(view)
}
override fun onBindViewHolder(holder: NoteHolder, position: Int) {
holder.recyclerTitleText?.text = titleTextArray[position]
Picasso.get().load(imageArray[position]).into(holder.recyclerImageView)
}
override fun getItemCount(): Int {
return titleTextArray.size
}
}
With Kotlin, you could to use a lambda (higher order function) to fetch the user clicks in the holder. For example, a base approach could be the next:
class YourAdapter(
private val listener: (String) -> Unit
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private val yourDataList = emptyList<String>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.compound_checkbox_terms_layout, parent, false)
return YourViewHolder(view)
}
override fun getItemCount(): Int {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
private inner class YourViewHolder(view: View) : RecyclerView.ViewHolder(view) {
init {
if (adapterPosition != RecyclerView.NO_POSITION) {
val selectedData = yourDataList[adapterPosition]
listener.invoke(selectedData)
}
}
}
}
Pass the activity context to the adapter from Activity so it can launch intent
Activity Code :
mAdapter = new CustomAdapter(this, imageArray, ArrayList);
Adapter Code :
class NoteAdapter(private val titleTextArray: ArrayList, private val imageArray: ArrayList, private val context : Context) : RecyclerView.Adapter<NoteAdapter.NoteHolder>() {
class NoteHolder (view: View) : RecyclerView.ViewHolder(view) {
var recyclerTitleText: TextView?= null
var recyclerImageView : ImageView?= null
init {
recyclerTitleText = view.findViewById(R.id.recyclerTitleText)
recyclerImageView = view.findViewById(R.id.recyclerImage)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NoteHolder {
val inflater = LayoutInflater.from(parent.context)
val view = inflater.inflate(R.layout.recycler_row, parent, false)
return NoteHolder(view)
}
override fun onBindViewHolder(holder: NoteHolder, position: Int) {
holder.recyclerTitleText?.text = titleTextArray[position]
Picasso.get().load(imageArray[position]).into(holder.recyclerImageView)
holder.recyclerImageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(context, NesneTani.class);
context.startActivity(intent);
}
});
}
override fun getItemCount(): Int {
return titleTextArray.size
}
}
Create an Interface to catch the click event. (position is optional)
interface OnItemClickListener {
fun onItemClick(position: Int)
}
add OnItemClickListener as a parameter in your Adapter class
class NoteAdapter(
private val titleTextArray: ArrayList<String>,
private val imageArray: ArrayList<String>,
private val listener: OnItemClickListener
) : RecyclerView.Adapter<NoteAdapter.NoteHolder>()
Then do below inside onBindViewHolder
override fun onBindViewHolder(holder: NoteHolder, position: Int) {
listener.onItemClick(position)
}
now inside your activity, implement the OnItemClickListener
class ActivityName implements OnItemClickListener{}
and while initiating adapter, do this
mAdapter = new CustomAdapter(this, imageArray, ArrayList);
Then inside the overridden method do what you want
override fun onItemClick(position: Int) {
//start the activity you want.
}
I have a recyclerView with 10 names and in this activity is possible search for name. With a click in a name the activity moves to another(with help Intents to move for DetailsActivity). It possible make this because in adapter, i have a function that give the position that the user have clicked. But when put a word in menu search this functional gives the position 1 (the real position of name, in exactally moment).
I can send in function in adpter for example, the id of name? What is the correct way to make this?
MainActivity.kt
class MainActivity : AppCompatActivity(), PeopleInStarWarsAdapter.OnNoteListener {
private lateinit var mainViewModel: MainViewModel
private var listDataPeople = ArrayList<DataPeople>()
private lateinit var dataPeopleAdapter: PeopleInStarWarsAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mainViewModel = ViewModelProvider
.AndroidViewModelFactory
.getInstance(application)
.create(MainViewModel::class.java)
//iniciar ViewModel
mainViewModel.init(this)
mainViewModel.getList().observe(this, Observer{ it ->
if(it != null){
listDataPeople = it
dataPeopleAdapter.submitList(listDataPeople)
}
else{
Toast.makeText(this, "Something is wrong!", Toast.LENGTH_SHORT).show()
}
})
initRecyclerView(listDataPeople)
}
private fun initRecyclerView(listDataPeople : ArrayList<DataPeople>) {
recycler_view.layoutManager = LinearLayoutManager(this)
dataPeopleAdapter = PeopleInStarWarsAdapter(listDataPeople, this)
recycler_view.adapter = dataPeopleAdapter
}
override fun onNoteClick(position: Int){
val intent = Intent(this, DetailsActivity::class.java)
intent.putExtra(BuildConfig.POSITION, position)
startActivity(intent)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.main_menu, menu)
val manager = getSystemService(Context.SEARCH_SERVICE) as SearchManager
val searchItem = menu?.findItem(R.id.search)
val searchView = searchItem?.actionView as SearchView
searchView.setSearchableInfo(manager.getSearchableInfo(componentName))
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener{
override fun onQueryTextSubmit(query: String): Boolean {
searchView.clearFocus()
searchView.setQuery("", false)
searchItem.collapseActionView()
Log.v("Information", "Looking for $query")
mainViewModel.checkMatch(query, this#MainActivity)
return true
}
override fun onQueryTextChange(newText: String): Boolean {
Log.v("Information", "$newText")
return false
}
})
return true
}
}
PeopleInStarWarsAdapter.kt
class PeopleInStarWarsAdapter(listDataPeople: ArrayList<DataPeople>, onNoteListener: OnNoteListener) : RecyclerView.Adapter<RecyclerView.ViewHolder>(){
var listDataPeople = ArrayList<DataPeople>()
private var mOnNoteListener : OnNoteListener
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return ViewHolder(
LayoutInflater.from(parent.context).inflate(
R.layout.layout_list_item, parent,
false)
)
}
override fun getItemCount(): Int {
return listDataPeople.size
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is ViewHolder -> {
holder.ViewHolder(listDataPeople[position], mOnNoteListener)
}
}
}
fun submitList(dataPeopleList: ArrayList<DataPeople>) {
this.listDataPeople = dataPeopleList
notifyDataSetChanged()
}
class ViewHolder constructor(itemView: View): RecyclerView.ViewHolder(itemView), View.OnClickListener {
private val textViewName : TextView = itemView.textView_name
var onNoteListener: OnNoteListener? = null
fun ViewHolder(dataPeople: DataPeople, onNoteListener: OnNoteListener) {
val name = dataPeople.name
this.textViewName.text = name
this.onNoteListener = onNoteListener
itemView.setOnClickListener(this)
}
override fun onClick(v: View?) {
onNoteListener?.onNoteClick(adapterPosition)
}
}
interface OnNoteListener {
fun onNoteClick(position: Int)
}
init {
this.listDataPeople = listDataPeople
mOnNoteListener = onNoteListener
}
}
You should send position to ViewHolder .
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is ViewHolder -> {
holder.ViewHolder(listDataPeople[position], mOnNoteListener,position)
}
}
}
Then pass this value into onNoteClick()
fun ViewHolder(dataPeople: DataPeople, onNoteListener: OnNoteListener,position: Int) {
val name = dataPeople.name
this.textViewName.text = name
this.onNoteListener = onNoteListener
itemView.setOnClickListener(this)
}
override fun onClick(v: View?) {
onNoteListener?.onNoteClick(position)
}
I am trying to store the values from retrofit into a database by pressing a AddtoCartButton but app is getting crashed when I press this button. I am able to retrieve the result from retrofit without any issues but onclicklistener is the issue.
Code:
inner class MoviesAdapter : RecyclerView.Adapter<MoviesAdapter.MovieViewHolder>() {
private val movies: MutableList<Movie> = mutableListOf()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MovieViewHolder {
return MovieViewHolder(layoutInflater.inflate(R.layout.item_movie_layout, parent, false))
}
override fun getItemCount(): Int {
return movies.size
}
override fun onBindViewHolder(holder: MovieViewHolder, position: Int) {
holder.bindModel(movies[position])
}
fun setMovies(data: List<Movie>) {
movies.addAll(data)
notifyDataSetChanged()
}
inner class MovieViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val movieGenreTxt : TextView = itemView.findViewById(R.id.movieGenre)
val movieYearTxt : TextView = itemView.findViewById(R.id.movieYear)
val movieAvatarImage : ImageView = itemView.findViewById(R.id.movieAvatar)
val movieDescription: TextView =itemView.findViewById(R.id.movieDescription)
fun bindModel(movie: Movie) {
// movieTitleTxt.text = movie.name
movieGenreTxt.text = movie.menu
movieYearTxt.text = movie.price
Picasso.get().load(movie.picture).into(movieAvatarImage)
movieDescription.text=movie.description
var movieName:String= movie.name!!
var testvar=movie.name.toString()
}
}
}
fun addtocart(view: View)
{
Toast.makeText(context,"Success on Click", Toast.LENGTH_SHORT).show()
print testvar
}
Error:
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.Button.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
Below code solves the purpose.
Code:
inner class MoviesAdapter : RecyclerView.Adapter<MoviesAdapter.MovieViewHolder>() {
private val movies: MutableList<Movie> = mutableListOf()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MovieViewHolder {
return MovieViewHolder(layoutInflater.inflate(R.layout.item_movie_layout, parent, false))
}
override fun getItemCount(): Int {
return movies.size
}
override fun onBindViewHolder(holder: MovieViewHolder, position: Int) {
holder.bindModel(movies[position])
}
fun setMovies(data: List<Movie>) {
movies.addAll(data)
notifyDataSetChanged()
}
inner class MovieViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val movieGenreTxt : TextView = itemView.findViewById(R.id.movieGenre)
val movieYearTxt : TextView = itemView.findViewById(R.id.movieYear)
val movieAvatarImage : ImageView = itemView.findViewById(R.id.movieAvatar)
val movieDescription: TextView =itemView.findViewById(R.id.movieDescription)
fun bindModel(movie: Movie) {
// movieTitleTxt.text = movie.name
movieGenreTxt.text = movie.menu
movieYearTxt.text = movie.price
Picasso.get().load(movie.picture).into(movieAvatarImage)
movieDescription.text=movie.description
var movieName:String= movie.name!!
var testvar=movie.name.toString()
val addtocart = itemView.findViewById<Button>(R.id.idCart)
addtocart!!.setOnClickListener {
print $testvar
Toast.makeText(context,"Success on Click", Toast.LENGTH_SHORT).show()
startActivity(intent) }
}
}
}