How to setOnSeekBarChangeListener in a Kotlin Fragment - kotlin

I am currently working on a mobile application in my uni classes that utilizes a seekBar to let the user decide to use a timer for a quiz. This application uses main to host all of the fragments. currently I just want the textbox to display where the user scrolled the seek bar to but am struggling to find a solution. any advice would be greatly appreciated. this is the code I have within the
TitleFragment.kt:
package com.example.android.guesstheword.screens.title
import android.os.Bundle
import android.view.DragEvent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.SeekBar
import android.widget.SeekBar.OnSeekBarChangeListener
import android.widget.Switch
import android.widget.TextView
import android.widget.Toast
import androidx.core.view.isVisible
import androidx.databinding.DataBindingUtil
import androidx.databinding.adapters.SeekBarBindingAdapter
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import com.example.android.guesstheword.R
import com.example.android.guesstheword.databinding.TitleFragmentBinding
import kotlinx.android.synthetic.main.title_fragment.*
/**
* Fragment for the starting or title screen of the app
*/
class TitleFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View {
// Inflate the layout for this fragment
val binding: TitleFragmentBinding = DataBindingUtil.inflate(
inflater, R.layout.title_fragment, container, false)
binding.timerSwitch.setOnClickListener {
if(binding.timerSwitch.isChecked){
binding.timerBar.visibility = View.VISIBLE
}
else{
binding.timerBar.visibility = View.INVISIBLE
}
}
val seekBar = binding.timerBar
seekBar.setOnSeekBarChangeListener(binding)
binding.playGameButton.setOnClickListener {
findNavController().navigate(TitleFragmentDirections.actionTitleToGame())
}
return binding.root
}
}
private fun SeekBar.setOnSeekBarChangeListener(binding: TitleFragmentBinding) {
val seconds = binding.timerSeconds
seconds.visibility = View.VISIBLE
seconds.text = binding.timerBar.progress.toString()
}
title_fragment.xml:
<?xml version="1.0" encoding="utf-8"?><!--
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/title_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".screens.title.TitleFragment">
<TextView
android:id="#+id/get_ready_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:fontFamily="sans-serif"
android:text="#string/get_ready"
android:textColor="#color/black_text_color"
android:textSize="14sp"
android:textStyle="normal"
app:layout_constraintBottom_toTopOf="#+id/title_text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed" />
<TextView
android:id="#+id/title_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:fontFamily="sans-serif"
android:text="#string/title_text"
android:textColor="#color/black_text_color"
android:textSize="34sp"
android:textStyle="normal"
app:layout_constraintBottom_toTopOf="#+id/play_game_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/get_ready_text"
app:layout_constraintVertical_chainStyle="packed" />
<Button
android:id="#+id/play_game_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:text="#string/play_button"
android:theme="#style/GoButton"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<Switch
android:id="#+id/timer_switch"
android:layout_width="95dp"
android:layout_height="52dp"
android:text="Timer"
android:min="5"
android:max="360"
app:layout_constraintBottom_toTopOf="#+id/play_game_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/title_text"
app:layout_constraintVertical_bias="0.064"/>
<SeekBar
android:id="#+id/timerBar"
android:layout_width="132dp"
android:layout_height="85dp"
android:clickable="false"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="#+id/play_game_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/timer_switch"
app:layout_constraintVertical_bias="0.144" />
<TextView
android:id="#+id/timer_seconds"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="#+id/play_game_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/timerBar"
app:layout_constraintVertical_bias="0.17" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

You created an extension function called setOnSeekBarChangeListener which does not actually set it.
In order to set the listener, you need to do something like this:
binding.timerBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener{
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
// here, you react to the value being set in seekBar
}
override fun onStartTrackingTouch(seekBar: SeekBar) {
// you can probably leave this empty
}
override fun onStopTrackingTouch(seekBar: SeekBar) {
// you can probably leave this empty
}
})
But keep in mind that this is NOT the extension function you created.

Related

The RecyclerView doesn't retrieve any data from Firebase and doesn't show any data

I just started working with Android Studio, and I've been trying to figure out what's wrong with this code for a few days now. When I run the program it shows nothing, and also there is no errors from Android Studio. I read the posts on the subject, but unfortunately I found nothing appropriate.
This is my activity class.
package ie.wit.donationx.activities
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.firebase.database.*
import ie.wit.donationx.adapters.EventsAdapter
import ie.wit.donationx.databinding.ActivityEventsFeedBinding
import ie.wit.donationx.models.EventData
class EventsFeed : AppCompatActivity() {
lateinit var mDataBase: DatabaseReference
private lateinit var eventList:ArrayList<EventData>
private lateinit var mAdapter: EventsAdapter
private lateinit var binding: ActivityEventsFeedBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//setContentView(R.layout.activity_events_feed)
binding = ActivityEventsFeedBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
eventList = ArrayList()
mAdapter = EventsAdapter(this,eventList)
var linearLayoutManager = LinearLayoutManager(this)
binding.recyclerEvents.layoutManager = linearLayoutManager
binding.recyclerEvents.setHasFixedSize(true)
binding.recyclerEvents.adapter = mAdapter
getEventsData()
}
private fun getEventsData() {
mDataBase = FirebaseDatabase.getInstance().getReference("Events")
mDataBase.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
if (snapshot.exists()) {
for (eventSnapshot in snapshot.children) {
val event = eventSnapshot.getValue(EventData::class.java)
eventList.add(event!!)
}
binding.recyclerEvents.adapter = mAdapter
}
}
override fun onCancelled(error: DatabaseError) {
TODO("Not yet implemented")
}
})
}
}
This is my Adapter
package ie.wit.donationx.adapters
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.RecyclerView
import ie.wit.donationx.R
import ie.wit.donationx.databinding.ItemListBinding
import ie.wit.donationx.models.EventData
class EventsAdapter(
var c: Context, var eventList: ArrayList<EventData>)
: RecyclerView.Adapter<EventsAdapter.EventViewHolder>() {
inner class EventViewHolder(var v: ItemListBinding): RecyclerView.ViewHolder(v.root){}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EventViewHolder {
val infilter = LayoutInflater.from(parent.context)
val v = DataBindingUtil.inflate<ItemListBinding>(
infilter,R.layout.item_list,parent,
false
)
return EventViewHolder(v)
}
override fun onBindViewHolder(holder: EventViewHolder, position: Int) {
val newList = eventList[position]
holder.v.isEvent = eventList[position]
holder.adapterPosition
}
override fun getItemCount(): Int {
return eventList.size
}
}
XML recyclerView
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activities.EventsFeed">
<com.google.android.material.imageview.ShapeableImageView
android:id="#+id/main_shapeable"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginStart="10dp"
android:layout_marginTop="5dp"
android:layout_marginEnd="10dp"
android:background="#color/white"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="#+id/main_search"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:ems="10"
android:hint="search"
android:inputType="textPersonName"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="#+id/main_shapeable"
app:layout_constraintEnd_toEndOf="#+id/main_shapeable"
app:layout_constraintStart_toStartOf="#+id/main_shapeable"
app:layout_constraintTop_toTopOf="#+id/main_shapeable" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerEvents"
tools:listitem="#layout/item_list"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/main_shapeable"/>
</androidx.constraintlayout.widget.ConstraintLayout>
XML file for items in recyclerView
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="isEvent"
type="ie.wit.donationx.models.EventData" />
</data>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="5dp"
>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="horizontal"
android:layout_margin="10dp"
android:padding="8dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/eventImg"
android:imageUrl="#{isEvent.image}"
android:scaleType="centerCrop"
android:layout_width="140dp"
android:layout_height="140dp"/>
<LinearLayout
android:orientation="vertical"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/eventTitle"
android:textColor="#color/black"
android:textSize="20sp"
android:gravity="center"
android:textStyle="bold|normal"
android:layout_gravity="center"
android:text="#{isEvent.evenTitle}"
android:layout_margin="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:id="#+id/eventInfo"
android:textColor="#android:color/darker_gray"
android:textSize="15sp"
android:gravity="center"
android:textStyle="bold|normal"
android:layout_gravity="center"
android:text="#{isEvent.info}"
android:layout_margin="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</LinearLayout>
<View
android:background="#android:color/darker_gray"
android:layout_width="match_parent"
android:layout_height="1dp"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
</layout>
Data file
package ie.wit.donationx.models
class EventData {
var evenTitle: String? = null
var info: String? = null
var image: String? = null
constructor(){}
constructor(
evenTitle:String?,
info:String?,
image:String?
){
this.evenTitle = evenTitle
this.info = info
this.image = image
}
}
Firebase DB structure
When adding new list to the RecyclerView Adapter you need to call NotifyDataSetChanged()
Also, i advise to remove the list from the constructor of the adapter, and make a public function inside the adapter class that will update the private list created in the class.
private var items: MutableList<EventData> = mutableListOf()
fun setItems(newItemsList : List<EventData>){
eventList.clear()
eventList.addAll(newItemsList)
notifyDataSetChanged()
}
If you only update part of the list, you can create a separate function for that and use the more efficient notifyItemRangeInserted(), notifyItemInserted() or notifyItemChanged() \ notifyItemRangeChanged
You don't need to setAdaper again after getting EventsData
Just replace this line in getEventsData() :
binding.recyclerEvents.adapter = mAdapter
With :
if (eventList.size > 0) {
mAdapter.notifyDataSetChanged()
}
Edit:
You should not to add
android:imageUrl="#{isEvent.image}" in the ImageView,
you can use library to download the images from URL Like:
Picasso.
or
Fresco.
For Example using Picasso in your adapter:
override fun onBindViewHolder(holder: EventViewHolder, position: Int) {
val newList = eventList[position]
holder.v.isEvent = eventList[position]
holder.adapterPosition
// here to download image for URL
Picasso.get().load(eventList[position].image)
.into(holder.v.eventImg)
}
Don't forget to add dependencies
implementation 'com.squareup.picasso:picasso:2.8'

Buttons will not show on displayed fragment

I am currently creating a chat application for a project in Android Studio using Kotlin, Firebase Cloud Storage and Cloud Firestore, and Glide, and I am following a few tutorials to do so. Everything was fine until I created a fragment to handle the user's account settings, such as name, bio, etc. The fragment appears to work and the edit text fields and profile picture can all be edited and interacted with, but neither of the buttons for saving and signing out seem to be displayed, and I can't figure out what the issue is exactly. As a result, I can't actually test the code until I can these buttons to be displayed. Any ideas?
This is what the fragment is supposed to look like, according to the layout: https://i.stack.imgur.com/0qR0C.png
This is what the fragment looks like when the application is actually running: https://i.stack.imgur.com/vAgLG.png
This is what the fragment layout looks like in code:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".fragment.MyAccountFragment">
<ImageView
android:id="#+id/imageView_profile_picture"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginLeft="155dp"
android:layout_marginTop="50dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0"
app:srcCompat="#drawable/ic_baseline_account_circle_24" />
<EditText
android:id="#+id/editText_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="225dp"
android:layout_marginBottom="8dp"
android:layout_marginLeft="0dp"
android:hint="Your Name"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/imageView_profile_picture"
app:layout_constraintVertical_bias="0.025" />
<EditText
android:id="#+id/editText_bio"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="275dp"
android:layout_marginBottom="8dp"
android:layout_marginLeft="0dp"
android:hint="Your Bio"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/imageView_profile_picture"
app:layout_constraintVertical_bias="0.025" />
<Button
android:id="#+id/btn_save"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="550dp"
android:backgroundTint="?attr/colorAccent"
android:text="Save"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.971"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.982"
tools:visibility="visible" />
<Button
android:id="#+id/btn_sign_out"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="310dp"
android:layout_marginTop="550dp"
android:backgroundTint="?attr/colorPrimaryDark"
android:text="Sign Out"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.028"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.982"
tools:visibility="visible" />
</FrameLayout>
This is the code for the fragment itself:
class MyAccountFragment : Fragment() {
// Request code:
private val RC_SELECT_IMAGE = 2
// Variable for the selected image:
private lateinit var selectedImageBytes: ByteArray
// Picture change flag:
private var pictureJustChanged = false
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_my_account, container, false)
view.apply {
findViewById<ImageView>(R.id.imageView_profile_picture).setOnClickListener{
val intent = Intent().apply {
type = "image/*"
action = Intent.ACTION_GET_CONTENT
putExtra(Intent.EXTRA_MIME_TYPES, arrayOf("image/jpeg", "image/png"))
}
startActivityForResult(Intent.createChooser(intent, "Select Image"), RC_SELECT_IMAGE)
}
findViewById<Button>(R.id.btn_save).setOnClickListener{
if(::selectedImageBytes.isInitialized){
CloudStorageUtil.uploadProfilePicture(selectedImageBytes) { imagePath ->
CloudFirestoreUtil.updateCurrentUser(findViewById<EditText>(R.id.editText_name).text.toString(),
findViewById<EditText>(R.id.editText_bio).text.toString(),
imagePath)
}
}
else {
CloudFirestoreUtil.updateCurrentUser(findViewById<EditText>(R.id.editText_name).text.toString(),
findViewById<EditText>(R.id.editText_bio).text.toString(),
null)
}
}
findViewById<Button>(R.id.btn_sign_out).setOnClickListener {
FirebaseAuth.getInstance().signOut()
val intent = Intent(this#MyAccountFragment.context, LoginOrRegisterActivity::class.java)
intent.flags = (Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
}
}
return view
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?){
if(requestCode == RC_SELECT_IMAGE
&& resultCode == Activity.RESULT_OK
&& data != null
&& data.data != null) {
val selectedImagePath = data.data
val selectedImageBmp = MediaStore.Images.Media.getBitmap(activity?.contentResolver, selectedImagePath)
val outputStream = ByteArrayOutputStream()
selectedImageBmp.compress(Bitmap.CompressFormat.JPEG, 90, outputStream)
selectedImageBytes = outputStream.toByteArray()
GlideApp.with(this).load(selectedImageBytes).into(view?.findViewById(R.id.imageView_profile_picture))
pictureJustChanged = true
}
}
override fun onStart() {
super.onStart()
CloudFirestoreUtil.getCurrentUser { user ->
if(this#MyAccountFragment.isVisible){
if (user != null) {
view?.findViewById<EditText>(R.id.editText_name)?.setText(user.name)
view?.findViewById<EditText>(R.id.editText_bio)?.setText(user.bio)
if(!pictureJustChanged && user.profilePicturePath != null){
GlideApp.with(this)
.load(CloudStorageUtil.pathToReference(user.profilePicturePath))
.placeholder(R.drawable.ic_baseline_account_circle_24)
.into(view?.findViewById(R.id.imageView_profile_picture))
}
}
}
}
}
}

No adapter attached; skipping layout fragment Kotlin

This is my code and I can't see what is the problem. The error I keep seeing is No adapter attached; skipping layout.
AndroidManifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.gsixacademy.android.bikesrevisited">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/Theme.BikesRevisited"
android:usesCleartextTraffic="true"
>
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
ADAPTER:
package com.gsixacademy.android.bikesrevisited.adapters
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.gsixacademy.android.bikesrevisited.R
import com.gsixacademy.android.bikesrevisited.models.BikeModel
import com.gsixacademy.android.bikesrevisited.models.BikeResult
import kotlinx.android.synthetic.main.bikes_custom_row.view.*
class Adapter(
var bikeList: ArrayList<BikeResult>,
val bikesOnClickEvent:(BikesOnClickEvent)->Unit
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return MyViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.bikes_custom_row, parent, false)
)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
var myHolder = holder as MyViewHolder
myHolder.bindData(bikeList[position], position)
}
inner class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) {
fun bindData(itemModel: BikeResult, position: Int) {
itemView.city_TV.text = itemModel.city
itemView.country_TV.text = itemModel.country
}
}
override fun getItemCount(): Int {
return bikeList.size
}
}
BIKES ON CLICK EVENT:
package com.gsixacademy.android.bikesrevisited.adapters
import com.gsixacademy.android.bikesrevisited.models.BikeResult
sealed class BikesOnClickEvent {
data class BikesClicked(val bike:BikeResult):BikesOnClickEvent()
}
APISERVICEBUILDER:
package com.gsixacademy.android.bikesrevisited.api
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
object ApiServiceBuilder {
val client=OkHttpClient.Builder().build()
val retrofit= Retrofit.Builder()
.baseUrl("http://api.citybik.es/v2/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
fun <T>buildService(service:Class<T>):T{
return retrofit.create(service)
}
}
BIKESAPI:
package com.gsixacademy.android.bikesrevisited.api
import com.gsixacademy.android.bikesrevisited.models.BikeModel
import retrofit2.Call
import retrofit2.http.GET
interface BikesApi {
#GET("networks")
fun getBikes(): Call<BikeModel>
}
BIKE MODEL
package com.gsixacademy.android.bikesrevisited.models
class BikeModel (val networks:ArrayList<BikeResult> )
BIKE RESULT
package com.gsixacademy.android.bikesrevisited.models
class BikeResult (
val city:String,val country:String
)
BIKE FRAGMENT
package com.gsixacademy.android.bikesrevisited
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.Fragment
import com.gsixacademy.android.bikesrevisited.R
import kotlinx.android.synthetic.main.recycler_view.*
import androidx.recyclerview.widget.LinearLayoutManager
import com.gsixacademy.android.bikesrevisited.adapters.Adapter
import com.gsixacademy.android.bikesrevisited.api.ApiServiceBuilder
import com.gsixacademy.android.bikesrevisited.api.BikesApi
import com.gsixacademy.android.bikesrevisited.models.BikeModel
import com.gsixacademy.android.bikesrevisited.models.BikeResult
import kotlinx.android.synthetic.main.recycler_view.*
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
class BikeFragment:Fragment() {
var request=ApiServiceBuilder.buildService(BikesApi::class.java)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view=inflater.inflate(R.layout.recycler_view,container,false)
val call=request.getBikes()
call.enqueue(object :Callback<BikeModel>{
override fun onResponse(call: Call<BikeModel>, response: Response<BikeModel>) {
if (response.isSuccessful){
var bikeResponse=response.body()
var bikeList=bikeResponse?.networks
if (bikeList!=null){
var bikelistAdapter=Adapter(bikeList){
}
recycler_view.layoutManager=LinearLayoutManager(context)
recycler_view.adapter=bikelistAdapter
}
}
else{}
}
override fun onFailure(call: Call<BikeModel>, t: Throwable) {
Toast.makeText(activity,t.message, Toast.LENGTH_SHORT)
.show()
}
})
return view
}
}
MAIN ACTIVITY:
package com.gsixacademy.android.bikesrevisited
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
SPLASH FRAGMENT:
package com.gsixacademy.android.bikesrevisited
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
class SplashFragment:Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.splash,container,false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
GlobalScope.launch {
Thread.sleep(1500)
findNavController().navigate(R.id.action_splashFragment_to_bikeFragment)
}
}
}
layouts
activity_main
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<fragment
android:id="#+id/navigation_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:navGraph="#navigation/navigation_graph"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
bikes_custom_row
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical">
<androidx.cardview.widget.CardView
android:id="#+id/card_view_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:padding="10dp"
app:cardCornerRadius="10dp"
app:cardElevation="3dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="vertical"
tools:ignore="UseCompoindDrawables">
<TextView
android:id="#+id/city_TV"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="city"
android:textColor="#color/black"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="#+id/country_TV"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="country"
android:textColor="#color/black"
android:textSize="18sp"
android:textStyle="bold" />
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
recycler_view
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/recycler_view"/>
</androidx.constraintlayout.widget.ConstraintLayout>
splash
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/imageView_splash"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/ic_launcher_background"
android:scaleType="centerInside"/>
</androidx.constraintlayout.widget.ConstraintLayout>
navigation_graph
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/navigation_graph"
app:startDestination="#id/splashFragment">
<fragment
android:id="#+id/splashFragment"
android:name="com.gsixacademy.android.bikesrevisited.SplashFragment"
android:label="SplashFragment" >
<action
android:id="#+id/action_splashFragment_to_bikeFragment"
app:destination="#id/bikeFragment"
app:popUpToInclusive="true" />
</fragment>
<fragment
android:id="#+id/bikeFragment"
android:name="com.gsixacademy.android.bikesrevisited.BikeFragment"
android:label="BikeFragment" />
</navigation>

How to find a view for Snackbar in fragment's onCreateView method if I use view binding and navigation component?

I'm trying to show a snack bar in fragment's onCreateView method but I don't know what view to passing inside the snack bar.
I'm very confused because I first time use the Navigation component and view binding and maybe is there a problem.
I tried binding.root but I got this exception:
java.lang.IllegalArgumentException: No suitable parent found from the
given view. Please provide a valid view.
After that I tried requireView().rootView as parameter but I also got this exception:
java.lang.IllegalStateException: Fragment XFragment{7a091b5}
(cefa1aef-59c3-4602-bcf1-b36f7d538cf9) id=0x7f0800f7} did not return a
View from onCreateView() or this was called before onCreateView()
MY CODE IN XFragment:
package com.sdsd.sds
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.navigation.Navigation
import com.google.android.material.snackbar.Snackbar
import com.sdsd.sds.databinding.FragmentXBinding
private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"
class XFragment : Fragment() {
private var _binding: FragmenXBinding? = null
private val binding get() = _binding!!
private var param1: String? = null
private var param2: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
param1 = it.getString(ARG_PARAM1)
param2 = it.getString(ARG_PARAM2)
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentXBinding.inflate(inflater, container, false)
binding.tvFgRegistration.setOnClickListener {
Navigation.findNavController(binding.root)
.navigate(R.id.action_f1_to_f2)
}
val snackbar: Snackbar = Snackbar.make(binding.root, "Succesful", Snackbar.LENGTH_LONG)
snackbar.show()
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
companion object {
#JvmStatic
fun newInstance(param1: String, param2: String) =
XFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
putString(ARG_PARAM2, param2)
}
}
}
}
XML LAYOUT FILE:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/ic"
tools:context=".XFragment">
<EditText
android:id="#+id/etE"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="24dp"
android:layout_marginEnd="16dp"
android:background="#color/translucent"
android:drawableStart="#drawable/ic_vector"
android:drawablePadding="5dp"
android:ems="10"
android:hint="#string/e_mail"
android:inputType="text"
android:textSize="25sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/imageView" />
<EditText
android:id="#+id/etP"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="24dp"
android:layout_marginEnd="16dp"
android:background="#color/translucent"
android:drawableStart="#drawable/ic_vector"
android:drawablePadding="5dp"
android:ems="10"
android:hint="#string/password"
android:inputType="textPassword"
android:textSize="25sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/etE" />
<TextView
android:id="#+id/tvFgRegistration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="180dp"
android:layout_marginEnd="76dp"
android:layout_marginBottom="100dp"
android:text="#string/new_u"
android:textColor="#color/white"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
I only want to show a snack bar with a message when XFragment creates.
Can anyone tell me how to solve this and explain me in a few words why?
I found few ways to show a snack bar from a fragment
One of the solution is to use requireView().
Snackbar.make(
requireView(),
"Hello from Snackbar",
Snackbar.LENGTH_SHORT
).show()
This solution will work when you are inside any onClickListner{} you can simply pass it as a view and it will do the job.
binding.button.setOnClickListener {
Snackbar.make(
it,
"Hello from Snackbar",
Snackbar.LENGTH_SHORT
).show()
}
I found a simple solution and seems a Snackbar works excellent only with CoordinatorLayout as root layout so I set CoordinatorLayout as my root layout in the XML file.
In the Snackbar I just put binding.coordinatorlayout where coordinatorlayout is just an id of CoordinatorLayout in the XML file.
Code solution for a fragment is here:
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentXBinding.inflate(inflater, container, false)
binding.tvFgRegistration.setOnClickListener {
Navigation.findNavController(binding.root)
.navigate(R.id.action_f1_to_f2)
}
val snackbar: Snackbar = Snackbar.make(binding.coordinatorlayout, "Succesful", Snackbar.LENGTH_LONG)
snackbar.show()
return binding.root
}
XML layout file:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/coordinatorlayout" //ID OF COORDINATOR LAYOUT
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/ic"
tools:context=".XFragment" >
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/ic_backg">
<EditText
android:id="#+id/etE"
android:layout_width="0dp"
.
.
.
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
I faced the same issue while using navigation architecture.
What worked for me was
if(isAdded){
Snackbar.make(requireActivity.window.decorView.rootView,"message").show()
}

Exoplayer with Viewpager2 and recycler view adapter

I have recently discovered abut Viewpager 2 and exoplayer and not sure how to integrate them both. Viewpager contains a list with video player and each takes up the full screen. You can swipe to move to the next video. Can anybody help me with a simple implementation of Exoplayer with Viewpager2
Yes I can help you.
It is a very simple implementation of viewpager2 with ExoPlayer.
It will show you online videos and you can swipe these videos horizontally.
Lets discuss it step by step:
Step 1:
Add internet permission in your Manifest file:
<uses-permission android:name="android.permission.INTERNET"/>
Step 2:
Add ExoPlayer dependency to your build.gradle
// exo player
implementation 'com.google.android.exoplayer:exoplayer:2.17.1'
Step 3 :
Add the following code to your themes.xml
<style name="ClickableView">
<item name="colorControlHighlight">#android:color/darker_gray</item>
<item name="android:background">?selectableItemBackgroundBorderless</item>
</style>
Step 4:
Create a controller layout file to control ExoPlayer named as custom_controller.xml. And then paste bellow code in that file.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="#80000000"
android:layout_height="match_parent">
<ImageView
android:id="#+id/exo_lock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:padding="10dp"
android:src="#drawable/ic_baseline_lock_open"
android:theme="#style/ClickableView" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/sec_controlvid1"
android:orientation="horizontal"
android:layout_centerInParent="true">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/exo_rew"
android:src="#drawable/ic_baseline_replay"
android:layout_marginLeft="30dp"
android:theme="#style/ClickableView"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/exo_play"
android:src="#drawable/ic_baseline_play_arrow"
android:layout_marginHorizontal="30dp"
android:theme="#style/ClickableView"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/exo_pause"
android:src="#drawable/ic_baseline_pause"
android:layout_marginHorizontal="30dp"
android:theme="#style/ClickableView"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/exo_ffwd"
android:layout_marginRight="30dp"
android:src="#drawable/ic_baseline_forward"
android:theme="#style/ClickableView"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/sec_controlvid2"
android:orientation="vertical"
android:padding="8dp"
android:gravity="center"
android:layout_alignParentBottom="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="8dp"
android:gravity="center"
android:layout_alignParentBottom="true">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/exo_position"
android:textColor="#color/white"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="/"
android:layout_marginHorizontal="4dp"
android:textColor="#CBCDC8"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/exo_duration"
android:layout_weight="1"
android:textColor="#CBCDC8"/>
<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:src="#drawable/ic_baseline_fullscreen"
android:id="#+id/bt_fullscreen"
android:theme="#style/ClickableView"/>
</LinearLayout>
<com.google.android.exoplayer2.ui.DefaultTimeBar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/exo_progress"
app:unplayed_color="#42454E"
app:buffered_color="#95989F"
app:played_color="#FF0000"
app:scrubber_color="#FF0000"
android:layout_alignParentBottom="true"
android:layout_marginTop="-8dp"/>
</LinearLayout>
</RelativeLayout>
Step 5:
Create a layout file for representing items in viewpager named as video_card_item.xml and write the following code in it:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:gravity="center_vertical"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.exoplayer2.ui.PlayerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:id="#+id/statusSliderVideo"
app:use_controller="true"
android:background="#android:color/background_dark"
app:player_layout_id="#layout/exo_player_view"
app:controller_layout_id="#layout/custom_controller"/>
<ImageView
android:id="#+id/statusSliderThumbnailImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:padding="10dp"
android:scaleType="fitCenter"
android:src="#drawable/ic_default_image" />
<ImageView
android:id="#+id/playPauseBtn"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerInParent="true"
android:src="#drawable/ic_play"
app:tint="#color/white" />
<ProgressBar
android:layout_width="80dp"
android:layout_height="80dp"
android:visibility="gone"
android:id="#+id/progress_bar"
android:layout_centerInParent="true"/>
</RelativeLayout>
Step 6 :
Create adapter class for viewpager items named as VideoSliderAdapter.java and write bellow code in it:
package com.example.practiceproject;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.media.ThumbnailUtils;
import android.net.Uri;
import android.provider.MediaStore;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.ui.PlayerView;
import java.util.ArrayList;
public class VideoSliderAdapter extends RecyclerView.Adapter<VideoSliderAdapter.ViewHolder> {
private Context context;
private ArrayList<String> pathsList;
private AppCompatActivity activity;
private boolean isFullScreen = false;
public static boolean isFullLock = false;
public VideoSliderAdapter(Context context, ArrayList<String> pathsList, AppCompatActivity activity) {
this.context = context;
this.pathsList = pathsList;
this.activity = activity;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.video_card_item, parent, false);
return new ViewHolder(view);
}
#SuppressLint("ClickableViewAccessibility")
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
holder.thumbnailImage.setImageBitmap(ThumbnailUtils.createVideoThumbnail(pathsList.get(position),
MediaStore.Video.Thumbnails.FULL_SCREEN_KIND));
// get data
Uri videoUri = Uri.parse(pathsList.get(position));
holder.bt_fullscreen.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (!isFullScreen) {
holder.bt_fullscreen.setImageDrawable(
ContextCompat.getDrawable(context, R.drawable.ic_baseline_fullscreen_exit)
);
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
} else {
holder.bt_fullscreen.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_baseline_fullscreen));
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
isFullScreen = !isFullScreen;
}
});
holder.bt_lockscreen.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (isFullLock) {
holder.bt_lockscreen.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_baseline_lock_open));
} else {
holder.bt_lockscreen.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_baseline_lock));
}
isFullLock = !isFullLock;
lockScreen(isFullLock, holder.sec_mid, holder.sec_bottom);
}
});
holder.playPauseBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
holder.playPauseBtn.setVisibility(View.GONE);
// holder.thumbnailImage.setVisibility(View.GONE);
holder.playerView.setVisibility(View.VISIBLE);
holder.simpleExoPlayer = new SimpleExoPlayer.Builder(context)
.setSeekBackIncrementMs(5000)
.setSeekForwardIncrementMs(5000)
.build();
holder.playerView.setPlayer(holder.simpleExoPlayer);
holder.playerView.setKeepScreenOn(true);
holder.simpleExoPlayer.addListener(new Player.Listener() {
#Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
if (playbackState == Player.STATE_BUFFERING) {
holder.progressBar.setVisibility(View.VISIBLE);
} else if (playbackState == Player.STATE_READY) {
holder.progressBar.setVisibility(View.GONE);
}
}
});
MediaItem mediaItem = MediaItem.fromUri(videoUri);
holder.simpleExoPlayer.setMediaItem(mediaItem);
holder.simpleExoPlayer.prepare();
holder.simpleExoPlayer.play();
}
});
}
private void lockScreen(boolean isFullLock, LinearLayout sec_mid, LinearLayout sec_bottom) {
if (isFullLock) {
sec_mid.setVisibility(View.INVISIBLE);
sec_bottom.setVisibility(View.INVISIBLE);
} else {
sec_mid.setVisibility(View.VISIBLE);
sec_bottom.setVisibility(View.VISIBLE);
}
}
#Override
public int getItemCount() {
return pathsList.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
PlayerView playerView;
ImageView thumbnailImage;
ImageView playPauseBtn;
ImageView bt_fullscreen, bt_lockscreen;
SimpleExoPlayer simpleExoPlayer;
ProgressBar progressBar;
LinearLayout sec_mid, sec_bottom;
public ViewHolder(#NonNull View view) {
super(view);
playerView = view.findViewById(R.id.statusSliderVideo);
thumbnailImage = view.findViewById(R.id.statusSliderThumbnailImage);
playPauseBtn = view.findViewById(R.id.playPauseBtn);
progressBar = view.findViewById(R.id.progress_bar);
bt_fullscreen = view.findViewById(R.id.bt_fullscreen);
bt_lockscreen = view.findViewById(R.id.exo_lock);
sec_mid = view.findViewById(R.id.sec_controlvid1);
sec_bottom = view.findViewById(R.id.sec_controlvid2);
}
}
}
Step 7 :
Paste following code in your activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.viewpager2.widget.ViewPager2
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:clipToPadding="true"
android:foregroundGravity="center"
android:overScrollMode="never"
android:id="#+id/videoViewPager"/>
</LinearLayout>
Step 8 :
Paste bellow code in your MainActivity.java:
package com.example.practiceproject;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager2.widget.ViewPager2;
import android.os.Bundle;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
private ViewPager2 viewPager;
private VideoSliderAdapter myAdapter;
ArrayList<String> videoPaths;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewPager = findViewById(R.id.videoViewPager);
videoPaths = new ArrayList<>();
videoPaths.add("http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4");
videoPaths.add("http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4");
videoPaths.add("http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4");
videoPaths.add("http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerFun.mp4");
videoPaths.add("http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerJoyrides.mp4");
// add paths for video simllarly
myAdapter = new VideoSliderAdapter(getApplicationContext(), videoPaths,
MainActivity.this);
viewPager.setAdapter(myAdapter);
viewPager.setPadding(10, 0, 10, 0);
}
}
Completed
Now you are good to go. Enjoy the app.. Best of Luck.
If you want to get project files (Source Code) for more simplicity, you can get source code from my github profile.
link : https://github.com/AbdullahProgrammer426351/Custom-Video-Slider-with-Exo-Player-and-ViewPager2