How can I play mp3 from recyclerview using Kotlin? - android

so I am trying to play an audio file from the raw folder by using a button that found in recyclerview but I tried everything and nothing seems to work for me
can you please help me and thanks in advance
P.C. this is a sample just to Know how to add an audio file in a recyclerview hopefully I will be adding more so if you have any tutorials on to add progress bar, image buttons... etc in recyclerview please share it with me
here is my progress so far
my Recyclerview Adater
class shaikh( val context: Context, val names: ArrayList<String>, val images: ArrayList<Int>): RecyclerView.Adapter<ViewHolder>() {
private var mediaPlayer: MediaPlayer? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val callXml = layoutInflater.inflate(R.layout.quranvoice,parent, false)
return ViewHolder(callXml)
}
override fun getItemCount(): Int {
return names.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.itemView.ShaikhName.text = names.get(position)
holder.itemView.Shaikh.setImageResource(images.get(position))
holder.itemView.setOnClickListener {
when(position){
0->{
holder.itemView.voice.setOnClickListener {
if (mediaPlayer != null){
mediaPlayer = MediaPlayer.create(context, R.raw.abdelbasset)
mediaPlayer?.start()
}
}
}
1->{
Toast.makeText(context,"الشيخ ناصر",Toast.LENGTH_LONG).show()
}
}
}
}
}
public class ViewHolder (view: View) : RecyclerView.ViewHolder(view) {
fun ViewHolder( ){
}
}
my Activity
var texts: ArrayList<String> = ArrayList()
var images : ArrayList<Int> = ArrayList()
// nothing is in here except the onCreate method
val Adapter = shaikh(this, texts, images)
QuranVoice.layoutManager = LinearLayoutManager(this, RecyclerView.HORIZONTAL, true)
QuranVoice.setHasFixedSize(true)
QuranVoice.setAdapter( Adapter)
QuranVoice.addOnScrollListener(CenterScrollListener())
loadData()
private fun loadData() {
texts.add("الشيخ عبدالباسط عبدالصمد - رحمه الله-")
images.add(R.drawable.abdelbasset_abdessamad)
texts.add("الشيخ خالد مغمسي")
images.add(R.drawable.ic_esha_time)
texts.add("الشيخ خالد مغمسي")
images.add(R.drawable.ic_dhur_time)
}

Related

RecyclerViewAdapter in Android Kotlin

I have an array that save different sensors data (in non-activity class) and I want the RecyclerView to be updated based on data of that array. Is it possible to automatically change the presented data?
I used setOnClickListener but nothing updated. The RecyclerView just display the default data. Also, I used text view however the data is updated with each click not continuously.
Thanks for your help.
In fragment:
override fun onClick(v: View?) {
when (v?.id) {
R.id.buttonStart-> {
start(v)
}
R.id.buttonStop-> {
stop(v)
}
}
}
fun start (_v: View) {
listListSensors.adapter = SensorsRecyclerViewAdapter(model.listSensors(), mListener!!)
}
In the other class:
fun listSensors(): Sensors {
return currentSensor
}
In recycle view:
class SensorsRecyclerViewAdapter (items: Sensors, listener: ListSensorsFragment.OnListFragmentInteractionListener)
: RecyclerView.Adapter<SensorsRecyclerViewAdapter.SensorsViewHolder>() {
private var mValues: Sensors = items
private var mListener: ListSensorsFragment.OnListFragmentInteractionListener = listener
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SensorsViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.recyclerviewlistsensors_item, parent, false)
return SensorsViewHolder(itemView)
}
override fun onBindViewHolder(holder: SensorsViewHolder, position: Int) {
holder.mItem = mValues
holder.listSensorsLightView.text = mValues.getLight()
holder.listSensorsTemperatureView.text = mValues.getTemperature()
holder.listSensorsGyroscopeView.text = mValues.getGyroscope()
holder.listSensorsAccelerometerView.text = mValues.getAccelerometer()
holder.listSensorsGravityView.text = mValues.getGravity()
holder.mView.setOnClickListener { mListener.onListFragmentInteraction(holder.mItem) }
}
in your Adapter
var itemClickListener: ((position: Int, name: String) -> Unit)? = null
fun setData(data:List<AnyList>){
this.list.clear()
this.list.addAll(data)
notifyDataSetChanged()
}
//bindviewholder
itemClickListner.invoke(1,"anyvalue")
// in fragment
adapter.itemClickListener = {
position, name ->
Toast.makeText(requireContext(),"position is $position name is $name ",Toast.LENGTH_SHORT).show()
}
// call setData() any place in fragment

How to put videos in recyclerview from firebase storage using android kotlin?

I have used the similar code that I use to get Images into the recyclerview from firebase storage. Here is the code of the adapter.:-
class VideoAdapter (private var mContext: Context,
private var mvideos: List<VID>,
private var isActivity: Boolean = false) : RecyclerView.Adapter<VideoAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VideoAdapter.ViewHolder {
val view = LayoutInflater.from(mContext).inflate(R.layout.videos_adapter, parent, false)
return VideoAdapter.ViewHolder(view)
}
override fun onBindViewHolder(holder: VideoAdapter.ViewHolder, position: Int) {
val videoid = mvideos[position]
holder.VidId.text = videoid.getilp()
val mediaController = MediaController(mContext)
mediaController.setAnchorView(holder.VideoIv)
VideoIv.setMediaController(mediaController)
VideoIv.setVideoURI(videoid.getVideoUrl())
VideoIv.requestFocus()
VideoIv.pause()
}
override fun getItemCount(): Int {
return mvideos.size
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var VideoIv: VideoView = itemView.videos_ret
var VidId: TextView = itemView.videos_id_ret
}
}
while using this code I am unable to VideoIv is unable to setmediacontroller and setvideouri. The data that I am getting is of URI type. Can someone please tell me how to get videos in my recyclerview?
Instead of VideoIv. Use holder.VideoIv
val mediaController = MediaController(mContext)
mediaController.setAnchorView(holder.VideoIv)
holder.VideoIv.setMediaController(mediaController)
holder.VideoIv.setVideoURI(videoid.getVideoUrl())
holder.VideoIv.requestFocus()
holder.VideoIv.pause()

Editing list item on list view

Imagine we have a simple list of items. Each item contains only a short title.
To handle the list we are using RecyclerView with ListAdapter and ViewHolders.
Each item/view is not editable unless we click it.
In this scenario I am using one view model for list and one for item under edit.
Unfortunately all my attempts failed.
I have tried to use two different view holders but the list was flickering, after all inflating view (in this case binding) is heavy.
Another shot I was giving to use the same view holder but with two various bind methods - one binding plain item, second binding with viewmodel instead of data object but it failed as well - suddenly a few rows were editable.
Has anyone solved it ?
class MistakesAdapter(private val editViewModel: MistakeEditViewModel) :
ListAdapter<Mistake, RecyclerView.ViewHolder>(MistakesDiffCallback()) {
companion object{
const val ITEM_PLAIN_VIEW_TYPE = 0
const val ITEM_EDITABLE_VIEW_TYPE = 1
}
private var itemPositionUnderEdit = -1
private val listener = object: MistakeItemListener{
override fun onClick(view: View, position: Int) {
Timber.d("OnClick : edit - $itemPositionUnderEdit, clickPos - $position")
editViewModel.onEditMistake(getItem(position))
itemPositionUnderEdit = position
notifyItemChanged(itemPositionUnderEdit)
}
}
override fun getItemViewType(position: Int) =
when (position) {
itemPositionUnderEdit -> ITEM_EDITABLE_VIEW_TYPE
else -> ITEM_PLAIN_VIEW_TYPE
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
when (viewType) {
ITEM_EDITABLE_VIEW_TYPE -> EditableMistakeViewHolder.from(parent)
else -> MistakeViewHolder.from(parent)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is EditableMistakeViewHolder -> holder.bind(editViewModel, listener)
is MistakeViewHolder -> holder.bind(getItem(position), listener)
else -> throw ClassCastException("Unknown view holder type")
}
}
class MistakeViewHolder private constructor(private val binding: ListItemMistakesBinding) :
RecyclerView.ViewHolder(binding.root) {
companion object {
fun from(viewGroup: ViewGroup): MistakeViewHolder {
val inflater = LayoutInflater.from(viewGroup.context)
val binding = ListItemMistakesBinding.inflate(inflater, viewGroup, false)
return MistakeViewHolder(binding)
}
}
fun bind(item: Mistake, listener: MistakeItemListener) {
binding.apply {
mistake = item
inputType = InputType.TYPE_NULL
this.listener = listener
position = adapterPosition
executePendingBindings()
}
}
}
class EditableMistakeViewHolder private constructor(private val binding: ListItemMistakesBinding)
: RecyclerView.ViewHolder(binding.root) {
companion object{
fun from(viewGroup: ViewGroup): EditableMistakeViewHolder {
val inflater = LayoutInflater.from(viewGroup.context)
val binding = ListItemMistakesBinding.inflate(inflater, viewGroup, false)
return EditableMistakeViewHolder(binding)
}
}
fun bind(viewModel: MistakeEditViewModel, listener: MistakeItemListener){
binding.apply {
this.viewModel = viewModel
inputType = InputType.TYPE_CLASS_TEXT
this.listener = listener
position = adapterPosition
root.setBackgroundColor(Color.GRAY)
}
}
}
}
class MistakeEditViewModel(private val repository: MistakesRepository) : ViewModel() {
#VisibleForTesting
var mistakeUnderEdit: Mistake? = null
//two-way binding
val mistakeName = MutableLiveData<String>()
fun onEditMistake(mistake: Mistake) {
mistakeUnderEdit = mistake
mistakeName.value = mistake.name
}
}
By changing my approach to the problem I solved it.
I make all list items editable but at the same time I am following focus.
To cut the long story short, I invoke item view model methods with help of OnFocusChangeListener and TextWatcher on my editTexts.

(Kotlin) Position of RecyclerView returns -1 when trying to click on an item

I'm new to Android development (and Kotlin).
I'm trying to implement a RecyclerView (which works fine) and when I click on a specific row it opens a new activity (Intent).
However, whenever I've press/click on one of the rows, I'm only able to get the value "-1" returned.
I've tried a number of different approaches (you should see the number of tabs in my browser).
This seems like it should be a fairly straightforward occurrence for something as common as a RecyclerView, but for whatever reason I'm unable to get it working.
Here is my RecyclerView Adapter file:
class PNHLePlayerAdapter (val players : ArrayList<PNHLePlayer>, val context: Context) : RecyclerView.Adapter<ViewHolder>() {
var onItemClick: ((Int)->Unit) = {}
// Gets the number of items in the list
override fun getItemCount(): Int {
return players.size
}
// Inflates the item views
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val itemView = LayoutInflater.from(context).inflate(
R.layout.pnhle_list_item,
parent,
false
)
val viewHolder = ViewHolder(itemView)
itemView.setOnClickListener {
onItemClick(viewHolder.adapterPosition)
}
return ViewHolder(itemView)
}
// Binds each item in the ArrayList to a view
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.tvPlayerName?.text = players[position].Name
holder.tvPlayerRank?.text = position.toString()
holder.tvPNHLe?.text = players[position].PNHLe.toString()
holder.tvTeam?.text = players[position].Team
holder.ivLeague?.setImageResource(leagueImageID)
}
}
class ViewHolder (view: View) : RecyclerView.ViewHolder(view) {
val linLayout = view.hor1LinearLayout
val ivTeam = view.teamImageView
val tvPlayerName = view.playerNameTextView
val tvPlayerRank = view.rankNumTextView
val tvPNHLe = view.pnhleTextView
val tvTeam = view.teamTextView
val ivLeague = view.leagueImageView
}
As you can see, there is a class property "onItemClick" which uses a lambda as the click callback.
I setOnClickListener in the onCreateViewHolder method after the view is inflated.
Next, in my Activity I add the list to my Adapter and set the call back.
However, every time I 'Toast' the position it is displayed as '-1'.
val adapter = PNHLePlayerAdapter(list, this)
adapter.onItemClick = { position ->
Toast.makeText(this, position.toString(),Toast.LENGTH_SHORT).show()
var intent = Intent(this, PlayerCardActivity::class.java)
//startActivity(intent)
}
rv_player_list.adapter = adapter
Perhaps I'm not thinking about this properly, but shouldn't the position represent the row number of the item out of the RecyclerView???
Ideally, I need to use the position so that I can obtain the correct item from the 'list' (ArrayList) so that I can pass information to my next Activity using the Intent
I found the issue.
Change this line in onCreateViewHolder:
return ViewHolder(itemView)
to this one:
return viewHolder
I would reorganize the adapter like this:
class PNHLePlayerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter<Adapter.ViewHolder>() {
interface AdapterListener {
fun onItemSelected(position: Int?)
}
var players: List<Player> = listOf()
set(value) {
field = value
this.notifyDataSetChanged()
}
var listener: AdapterListener? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_car_selector, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(position)
}
override fun getItemCount(): Int {
return brands.size
}
inner class ViewHolder(view: View): androidx.recyclerview.widget.RecyclerView.ViewHolder(view) {
private var position: Int? = null
private val baseView: LinearLayout? = view.findViewById(R.id.baseView) as LinearLayout?
...
init {
baseView?.setOnClickListener {
listener?.onManufacturerSelected(position)
}
}
fun bind(position: Int) {
this.position = position
...
}
}
}
And from your activity/fragment set the listener as adapter.listener = this, and implement the onItemSelected(position: Int?)
override fun onItemSelected(position: Int?) {
...
}

Can't assign new value to a simple Singleton using an onClickeListener for a RecyclerView

I am trying to build a Slack-like chat app following a tutorial in a course I am taking online.
In the tutorial the instructor is using a ListView and the OnItemClickListener method, but I am trying to do it with recycler view, and I am stuck with the onClickListener in the adapter.
I have tried to find answers in other questions but couldn't find one that solves my problem. The closest ones were this and this
My two problems are:
1. The app's main activity has on the top of the screen a title that states what channel is currently active. I have created a singleton that holds the "current channel" and the title's text is being pulled from that singleton.
I am having a hard time changing the value of that singleton on click.
The main activity also has all the channels in a list view in a drawer.
I am trying to close the drawer when a channel is clicked but that isn't happening either.
This is my current adapter:
class ChannelsAdapter(val context: Context, val channels: ArrayList<Channel>) :
RecyclerView.Adapter<ChannelsAdapter.Holder>() {
inner class Holder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val singleChannel = itemView.findViewById<TextView>(R.id.single_channel)
val mainLayout = LayoutInflater.from(context).inflate(R.layout.activity_main, null)
fun bindText(textVar: String, context: Context) {
singleChannel.text = textVar
}
}
override fun onBindViewHolder(holder: Holder, position: Int) {
holder.bindText(channels[position].toString(), context)
holder.itemView.setOnClickListener {
ChannelName.activeChannel = channels[position]
holder.mainLayout.drawer_layout.closeDrawer(GravityCompat.START)
}
}
override fun getItemCount(): Int {
return channels.count()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChannelsAdapter.Holder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.channel_list_layout, parent, false)
return Holder(view)
}
}
This is the singleton
object ChannelName {
var activeChannel : Channel? = null
}
You can rewrite the setter for activeChanell variable and call a listener that has been added before to notify your Activity:
object ChannelName {
private val listeners = ArrayList<(Channel?) -> Unit>()
fun addChannelNameChangedListener(listener: (Channel?) -> Unit) {
listeners.add(listener)
}
fun removeChannelNameChangedListener(listener: (Channel?) -> Unit) {
listeners.remove(listener)
}
var activeChannel: Channel? = null
set(value) {
field = value
listeners.forEach { it.invoke(value) }
}
}
And inside the Activity add a listener like this:
ChannelName.addChannelNameChangedListener {
// Do your operation
}
The alternative solution is to use Observable utils like LiveData, so you shouldn't worry about the Android life cycle any more:
object ChannelName {
val activeChannel: MutableLiveData<ChannelName> = MutableLiveData()
}
To change the value inside your adapter simply call:
ChannelName.activeChannel.value = channels[position]
And inside your activity Observe to the variable by calling:
ChannelName.activeChannel.observe(this, Observer {
// Do your operation
})
class ChannelsAdapter(val context: Context, val channels: ArrayList<Channel>) :
RecyclerView.Adapter<ChannelsAdapter.Holder>() {
private var itemClickListener: OnItemClickListener? = null
fun setItemClickListener(itemClickListener: OnItemClickListener) {
this.itemClickListener = itemClickListener
}
interface OnItemClickListener {
fun onItemClick(position: Int)
}
inner class Holder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
val singleChannel = itemView.findViewById<TextView>(R.id.single_channel)
val mainLayout = LayoutInflater.from(context).inflate(R.layout.activity_main, null)
fun bindText(textVar: String, context: Context) {
singleChannel.text = textVar
}
override fun onClick(v: View?) {
val position = adapterPosition
itemClickListener?.let {
if (position != RecyclerView.NO_POSITION) {
it.onItemClick(position)
}
}
}
}
override fun onBindViewHolder(holder: Holder, position: Int) {
holder.bindText(channels[position].toString(), context)
}
override fun getItemCount(): Int {
return channels.count()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChannelsAdapter.Holder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.channel_list_layout, parent, false)
return Holder(view)
}
}
This way you can setItemClickListener to the adapter in your activity and get callback from your recyclerView.
You should not set listener in onBind() method since it will be called more than your items' count.

Categories

Resources