This question already has answers here:
ViewPager and RecyclerView issue with fragment transition
(2 answers)
Closed 1 year ago.
ViewPager2 Caused by IllegalStateException FragmentManager is already executing transactions
Replicated Crash : while rotating screen
How to fix Viewpager2 FragmentManager is already executing transactions?
Full source code:
https://github.com/stevdza-san/ViewPager2-with-Navigation-Component-TestApp
ViewPagerFragment.kt
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.test.zigmaster.databinding.FragmentViewPagerBinding
class ViewPagerFragment : Fragment() {
var binding : FragmentViewPagerBinding?= null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// Inflate the layout for this fragment
// val view = inflater.inflate(R.layout.fragment_view_pager, container, false)
binding = FragmentViewPagerBinding.inflate(inflater)
val fragmentList = arrayListOf<Fragment>(
FirstScreen(),
SecondScreen(),
ThirdScreen()
)
val adapter = ViewPagerAdapter(
fragmentList,
requireActivity().supportFragmentManager,
lifecycle
)
binding!!.viewPager.adapter = adapter
return binding!!.root
}
override fun onDestroyView() {
super.onDestroyView()
binding=null
}
}
ViewPagerAdapter.kt
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Lifecycle
import androidx.viewpager2.adapter.FragmentStateAdapter
class ViewPagerAdapter(
list: ArrayList<Fragment>,
fm: FragmentManager,
lifecycle: Lifecycle
) : FragmentStateAdapter(fm, lifecycle) {
private val fragmentList = list
override fun getItemCount(): Int {
return fragmentList.size
}
override fun createFragment(position: Int): Fragment {
return fragmentList[position]
}
}
fragment_view_pager.xml
<?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=".ui.app_intro.ViewPagerFragment">
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
FirstScreen.kt
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.viewpager2.widget.ViewPager2
import com.test.zigmaster.R
import com.test.zigmaster.databinding.FragmentFirstScreenBinding
class FirstScreen : Fragment() {
private var binding : FragmentFirstScreenBinding?= null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// Inflate the layout for this fragment
binding = FragmentFirstScreenBinding.inflate(inflater)
val viewPager = activity?.findViewById<ViewPager2>(R.id.viewPager)
binding!!.next.setOnClickListener {
viewPager?.currentItem = 1
}
return binding!!.root
}
override fun onDestroyView() {
super.onDestroyView()
binding=null
}
Change view.viewPager.adapter = adapter to
Handler(Looper.getMainLooper()).post {
view.viewPager.adapter = adapter
}
in https://github.com/stevdza-san/ViewPager2-with-Navigation-Component-TestApp/blob/master/app/src/main/java/com/jovanovic/stefan/mytestapp/onboarding/ViewPagerFragment.kt
Related
I've worked on the FragmentContainerView activity with some fragments, in that fragment, I want to use Compose alongside XML using ComposeViewin the first fragment, but when I try to navigate from the second fragment and back to the first fragment, the component from ComposeView was disappeared.
I've set up a fragment with FragmentContainerView using the replace strategy and I put the fragment inside the list like usual, and I have no idea what happens with this situation.
You can watch in this video
also this is my code
NavigationActivity.kt
import android.os.Bundle
import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity
import androidx.core.net.toUri
import androidx.navigation.NavDeepLinkRequest
import androidx.navigation.Navigation
import androidx.navigation.findNavController
import id.derysudrajat.inlinebinding.viewBinding
import id.derysudrajat.library.R
import id.derysudrajat.library.databinding.ActivityNavigationBinding
class NavigationActivity : AppCompatActivity() {
private val binding by viewBinding(ActivityNavigationBinding::inflate)
private val listOfFragment = listOf(FragmentMainNav(), FragmentDetailNav())
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
binding.btnHome.setOnClickListener{
replace(HOME_NAV)
}
binding.btnDetail.setOnClickListener{
replace(DETAIL_NAV)
}
Navigation.createNavigateOnClickListener(R.id.fragmentDetailNav, null)
}
private fun replace(nav: String) {
supportFragmentManager
.beginTransaction()
.replace(
binding.fragmentContainerView.id,
if (nav == HOME_NAV) listOfFragment[0] else listOfFragment[1]
)
.commit()
}
companion object {
const val HOME_NAV = "home_nav"
const val DETAIL_NAV = "detail_nav"
}
}
FragmentMainNav.kt
import android.os.Bundle
import android.view.View
import androidx.compose.material3.Text
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.lifecycle.Lifecycle
import androidx.navigation.findNavController
import id.derysudrajat.inlinebinding.BindingFragment
import id.derysudrajat.inlinebinding.viewBinding
import id.derysudrajat.library.R
import id.derysudrajat.library.databinding.FragmentNavMainBinding
class FragmentMainNav : BindingFragment<FragmentNavMainBinding>() {
override val binding by viewBinding(FragmentNavMainBinding::inflate)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.textMain.text = "Fragment Main"
binding.composeView.apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
Text(text = "This is Compose")
}
}
}
}
fragment_nav_main.xml
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/text_main"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Fragment Main"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.compose.ui.platform.ComposeView
android:id="#+id/compose_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/text_main" />
</androidx.constraintlayout.widget.ConstraintLayout>
FragmentDetailNav.kt
import android.os.Bundle
import android.view.View
import id.derysudrajat.inlinebinding.BindingFragment
import id.derysudrajat.inlinebinding.viewBinding
import id.derysudrajat.library.databinding.FragmentNavDetailBinding
import id.derysudrajat.library.databinding.FragmentNavMainBinding
class FragmentDetailNav : BindingFragment<FragmentNavDetailBinding>() {
override val binding by viewBinding(FragmentNavDetailBinding::inflate)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.textDetail.text = "Detail Navigation"
}
}
fragment_nav_detail.kt
<?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">
<TextView
android:id="#+id/text_detail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Detail Navigation"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
I've tried using different ways like changing ViewCompositionStrategy and it still not working, I expected to show ComposeView still show when the fragment was replaced and back, btw the compose that I was tried is from compose BOM 2022.10.00 - 2023.01.00 but still disappeared when the fragment was replaced.
I think you could move the binding.composeView.apply {} block from onViewCreated to onCreateView as shown by provided sample in official documentation here. I've tested it using compose bom 2023.01.00 and plain ViewBinding and it works fine. So the HomeFragment would be like this:
class HomeFragment : Fragment() {
private var _binding: FragmentHomeBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentHomeBinding.inflate(inflater, container, false)
val view = binding.root
binding.composeView.apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
Text(text = "This is Compose")
}
}
return view
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
As additional note, i don't think the Navigation.createNavigateOnClickListener() is necessary and could just be removed.
I am trying to develope an app which lets the user choose and read a csv file from his phone, so the app can set a variable with the chosen file content (string). This variable can then be used to set the text of a TextView.
I already was able to let the user of my app choose the desired file. I couldn't get the file content though. And I've already seen that onActivityResult and startActivityForResult are depreacated which means that a lot of tutorials are outdated. Plus I was not able to get a solution to my problem by the documentation here.
This picture shows you, that after the user chooses his csv file, the textView is not changing.
This is what I have come up with so far for my ContentFragment.kt file. As you can see I already have a line for setting the TextView text to the content of a csv file which is located in the asset folder. That works great (here it is commented out - only there fyi), but I would like to do the same for the user's csv file:
package com.example.myapplication
import android.content.Intent
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.example.myapplication.databinding.FragmentContentBinding
import kotlinx.android.synthetic.main.fragment_content.*
import java.io.BufferedReader
import java.io.InputStreamReader
class ContentFragment : Fragment() {
var _binding: FragmentContentBinding? = null
val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
_binding = FragmentContentBinding.inflate(inflater, container, false)
val file = BufferedReader(InputStreamReader(resources.assets.open("Testfile.csv"))).use {
it.readText()
} // The variable is unused. This line of code only exists to demonstrate that the file_content.settext(file) in the TestButton click event is working.
binding.TestButton.setOnClickListener {
// file_content.setText(file) // <-- This line works
val intent = Intent()
.setType("*/*")
.setAction(Intent.ACTION_GET_CONTENT)
startActivity(Intent.createChooser(intent, "Select a file"))
}
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
override fun onActivityResult(
requestCode: Int, resultCode: Int, resultData: Intent?) {
resultData?.data?.also { uri ->
val file = BufferedReader(InputStreamReader(resources.assets.open(resultData.toString()))).use {
it.readText()
}
file_content.setText(file)
}
}
}
This is the xml of the fragment:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ContentFragment"
android:orientation="vertical">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/TestButton"
android:text="Import File"
tools:ignore="MissingConstraints"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="In here the content of the file will appear."
android:id="#+id/file_content"
tools:ignore="MissingConstraints" />
</LinearLayout>
And here are more files, which are not so necessary:
MainActivity.kt:
package com.example.myapplication
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.example.myapplication.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var binding : ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
}
}
activity_main.xml:
<?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"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="56dp"
app:layout_constraintBottom_toBottomOf="parent">
<fragment
android:id="#+id/fragment"
class="com.example.myapplication.ContentFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Replace:
val file = BufferedReader(InputStreamReader(resources.assets.open(resultData.toString()))).use {
it.readText()
}
with:
val file = BufferedReader(InputStreamReader(requireContext().contentResolver.openInputStream(uri))).use {
it.readText()
}
Note that your use of */* for the MIME type means that the user can choose non-text content. Consider using text/*.
I was able to solve my problem by editing ContentFragment.kt. Now it works to let the user choose a csv-file and show its content in a textView.
package com.example.myapplication
import android.content.Intent
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.result.contract.ActivityResultContracts
import com.example.myapplication.databinding.FragmentContentBinding
import kotlinx.android.synthetic.main.fragment_content.*
import java.io.BufferedReader
import java.io.InputStreamReader
class ContentFragment : Fragment() {
var _binding: FragmentContentBinding? = null
val binding get() = _binding!!
private val request = registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
if (uri != null) {
val inputStream = requireContext().contentResolver.openInputStream(uri)
val file = BufferedReader(InputStreamReader(inputStream)).use {
it.readText()
}
file_content.setText(file)
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
_binding = FragmentContentBinding.inflate(inflater, container, false)
binding.TestButton.setOnClickListener {
val intent = Intent()
.setType("*/*")
.setAction(Intent.ACTION_GET_CONTENT)
request.launch("*/*")
}
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
}
I am trying to create a tabular layout for my Android application through an adapter class. However, the layout represented in the ViewPager does not change when I click on different tabs. Below is my code:
activity_main.xml:
<?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:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff"
tools:context=".MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/AppTheme.AppBarOverlay"/>
<ScrollView 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">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ffffff"
tools:context=".MainActivity"
app:tabTextAppearance="#style/MyCustomTextAppearance"
>
<!-- The tabular layout -->
<com.google.android.material.tabs.TabLayout
android:id="#+id/tablayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:textAllCaps="false"
android:background="#ffffff"
>
</com.google.android.material.tabs.TabLayout>
<!-- Contains the fragment corresponding to each tab -->
<androidx.viewpager.widget.ViewPager
android:id="#+id/viewPager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
total_fragment.xml, individual_fragment.xml, usage_fragment.xml are all very basic layouts with just a TextView in them.
Below is my Kotlin code (MainActivity.kt):
import PageAdapter
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*
import android.util.Log
import androidx.viewpager.widget.ViewPager
import com.google.android.material.tabs.TabLayout
var tabLayout: TabLayout? = null
var viewPagerReference: ViewPager? = null
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
tabLayout = findViewById<TabLayout>(R.id.tablayout)
viewPagerReference = findViewById<ViewPager>(R.id.viewPager)
tabLayout!!.addTab(tabLayout!!.newTab().setText("TabA"))
tabLayout!!.addTab(tabLayout!!.newTab().setText("TabB"))
tabLayout!!.addTab(tabLayout!!.newTab().setText("TabC"))
tabLayout!!.tabGravity = TabLayout.GRAVITY_FILL
val adapter = PageAdapter(this, supportFragmentManager, tabLayout!!.tabCount)
viewPager!!.adapter = adapter
viewPager!!.addOnPageChangeListener(TabLayout.TabLayoutOnPageChangeListener(tabLayout))
tabLayout!!.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab) {
Log.i("TextStats","NEW TAB SELECTED: " + tab.position)
viewPager!!.currentItem = tab.position
}
override fun onTabUnselected(tab: TabLayout.Tab) {
}
override fun onTabReselected(tab: TabLayout.Tab) {
}
})
}
}
For the adapter class (PageAdapter.kt):
import android.content.Context
import android.util.Log
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentPagerAdapter
import com.example.textstats.IndividualFragment
import com.example.textstats.TotalFragment
import com.example.textstats.UsageFragment
class PageAdapter(private val myContext: Context, fm: FragmentManager, internal var totalTabs: Int) : FragmentPagerAdapter(fm) {
// this is for fragment tabs
override fun getItem(position: Int): Fragment? {
Log.i("TextStats", "POSITION = " + position);
when (position) {
0 -> {
// val homeFragment: HomeFragment = HomeFragment()
return TotalFragment()
}
1 -> {
return IndividualFragment()
}
2 -> {
// val movieFragment = MovieFragment()
return UsageFragment()
}
else -> return null
}
}
// this counts total number of tabs
override fun getCount(): Int {
return totalTabs
}
}
TotalFragment, UsageFragment, and IndividualFragment have all the same code below, except for the layout id being different (R.layout.[layout name]):
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
class TotalFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater!!.inflate(R.layout.total_fragment, container, false)
}
}
add this line after set adapter
tabLayout.setupWithViewPager(viewPager)
class LayoutPagerAdapter internal
constructor(var mContext: Context):PagerAdapter() {
override fun isViewFromObject(view: View, `object`: Any): Boolean {
return view === `object` as ImageView
}
private val sliderImageId = intArrayOf(
R.drawable.sym_action_call,R.drawable.presence_audio_away
)
override fun instantiateItem(container: ViewGroup, position: Int): Any {
val imageView = ImageView(mContext)
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP)
imageView.setImageResource(sliderImageId[position])
(container as ViewPager).addView(imageView, 0)
return imageView
}
override fun destroyItem(
container: ViewGroup,
position: Int,
`object`: Any
) {
(container as ViewPager).removeView(`object` as ImageView)
}
override fun getCount(): Int {
return sliderImageId.size
}
}
I am trying to use the YouTubePlayerSupportFragment inside my own fragment that I created. When trying to add it into the childFragmentManager's fragment transaction, I get a type cast error saying that the object's type needs to be Fragment and not YouTubePlayerSupportFragment. I am adding it into the fragment transaction so that I can choose when to hide or show the fragment. Everything I have seen online and written in Java works the same way my code works unless it has something to do with Kotlin?
Fragment.xml
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<fragment
android:id="#+id/youtube_player_fragment"
android:name="com.google.android.youtube.player.YouTubePlayerSupportFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Fragment.kt
import android.os.Bundle
import android.text.SpannableString
import android.text.style.BulletSpan
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
import androidx.fragment.app.Fragment
import com.google.android.youtube.player.YouTubeInitializationResult
import com.google.android.youtube.player.YouTubePlayer
import com.google.android.youtube.player.YouTubePlayerSupportFragment
class ExpandedItemFragment(private val hasVideo: Boolean) : Fragment(), YouTubePlayer.OnInitializedListener {
private lateinit var mosquitoVideo: YouTubePlayerSupportFragment
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(
R.layout.expanded_item, container, false
)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
initializeLayout()
}
private fun initializeLayout() {
mosquitoVideo = YouTubePlayerSupportFragment.newInstance()
childFragmentManager.beginTransaction()
.add(R.id.youtube_player_fragment, mosquitoVideo) //Type cast error happening here
if (hasVideo) {
mosquitoVideo.initialize(getString(R.string.google_api_key), this)
}
}
override fun onInitializationSuccess(
provider: YouTubePlayer.Provider,
youTubePlayer: YouTubePlayer,
b: Boolean
) {
if (!b) {
youTubePlayer.cueVideo("rD8SmacBUcU")
}
}
override fun onInitializationFailure(
provider: YouTubePlayer.Provider,
youTubeInitializationResult: YouTubeInitializationResult
) {
Toast.makeText(activity, "Youtube video failed to play", Toast.LENGTH_SHORT).show()
}
Try to use this component here it implements web frame
https://github.com/PierfrancescoSoffritti/android-youtube-player
add to build.grandle
dependencies {
implementation 'com.pierfrancescosoffritti.androidyoutubeplayer:core:10.0.5'
}
add to you XML
<com.pierfrancescosoffritti.androidyoutubeplayer.core.player.views.YouTubePlayerView
android:id="#+id/youtube_player_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
load video
YouTubePlayerView youTubePlayerView = findViewById(R.id.youtube_player_view);
youTubePlayerView.addYouTubePlayerListener(new AbstractYouTubePlayerListener() {
#Override
public void onReady(#NonNull YouTubePlayer youTubePlayer) {
String videoId = "rD8SmacBUcU";
youTubePlayer.loadVideo(videoId, 0);
}
});
I'm trying to set navController to bottomNavigation, but it's requires activity, the problem is my borromNavigation hosted in fragment, so i don't have activity instance.
I've tried
bottom_nav_view.setupWithNavController(findNavController())
and:
val host = Navigation.findNavController(this.activity!!.parent, R.id.my_nav_host_fragment)
bottom_nav_view.setupWithNavController(host)
home_fragment.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".ui.fragments.home.HomeFragment">
<fragment
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#id/bottom_nav_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:id="#+id/my_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
app:navGraph="#navigation/main_navigation"
app:defaultNavHost="true"
/>
<com.google.android.material.bottomnavigation.BottomNavigationView
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:id="#+id/bottom_nav_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:menu="#menu/bottom_menu" />
</androidx.constraintlayout.widget.ConstraintLayout>
So, I need somehow take my_nav_host_fragment from my home_fragment.xml in HomeFragment.kt
HomeFragment.kt
package andy.schedulekpi.ui.fragments.home
import androidx.lifecycle.ViewModelProviders
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.navigation.NavController
import androidx.navigation.Navigation
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import androidx.navigation.ui.NavigationUI
import androidx.navigation.ui.setupWithNavController
import andy.schedulekpi.R
import andy.schedulekpi.ui.fragments.start.StartFragmentDirections
import kotlinx.android.synthetic.main.home_fragment.*
class HomeFragment : Fragment() {
companion object {
fun newInstance() = HomeFragment()
}
private lateinit var viewModel: HomeViewModel
private lateinit var host : NavController
val safeArgs : HomeFragmentArgs by navArgs()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.home_fragment, container, false)
//host = Navigation.findNavController(this.activity!!.parent, R.id.my_nav_host_fragment)
return view
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = ViewModelProviders.of(this).get(HomeViewModel::class.java)
// TODO: Use the ViewModel
Toast.makeText(this.context, safeArgs.currentWeek.toString(), Toast.LENGTH_SHORT).show()
//val navController = Navigation.findNavController(activity!!.parent, R.id.my_nav_host_fragment) - don't work
//val host = Navigation.findNavController() -ERROR
// bottom_nav_view.setupWithNavController(findNavController()) -ERROR
}
}
bottom_menu.xml:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:icon="#drawable/ic_schedule"
android:id="#+id/scheduleFragment"
android:title="Schedule"/>
<item
android:icon="#drawable/ic_database"
android:id="#+id/archiveFragment"
android:title="Database"/>
<item
android:id="#+id/teachersFragment"
android:icon="#drawable/ic_teachers"
android:title="Teachers"/>
<item
android:id="#+id/settingsFragment"
android:icon="#drawable/ic_settings_"
android:title="Settings"/>
</menu>
main_navigation.xml:
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/main_navigation"
app:startDestination="#id/scheduleFragment">
<fragment
android:id="#+id/scheduleFragment"
android:name="andy.schedulekpi.ui.fragments.schedule.ScheduleFragment"
android:label="schedule_fragment"
tools:layout="#layout/schedule_fragment"/>
<fragment
android:id="#+id/archiveFragment"
android:name="andy.schedulekpi.ui.fragments.archive.ArchiveFragment"
android:label="archive_fragment"
tools:layout="#layout/archive_fragment"/>
<fragment
android:id="#+id/teachersFragment"
android:name="andy.schedulekpi.ui.fragments.teachers.TeachersFragment"
android:label="teachers_fragment"
tools:layout="#layout/teachers_fragment"/>
<fragment
android:id="#+id/settingsFragment"
android:name="andy.schedulekpi.ui.fragments.settings.SettingsFragment"
android:label="settings_fragment"
tools:layout="#layout/settings_fragment"/>
</navigation>
I expected to handle bottomNavigation from fragment, but now i clicked on menu items and nothing happend or error.
In HomeFragment to set navController on BottomNavigationView:
import androidx.navigation.findNavController
Navigation.setViewNavController(bottom_nav_view, activity!!.findNavController(R.id.my_nav_host_fragment))
then when u want get navController from bottom_nav_view:
bottom_nav_view.findNavController()
As said Artur Gniewowski, in HomeFragment in onActivityCreated I need just add
bottom_nav_view.setupWithNavController(activity!!.findNavController(R.id.my_nav_host_fragment))
So, now in HomeFragment i have:
package andy.schedulekpi.ui.fragments.home
import androidx.lifecycle.ViewModelProviders
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.navigation.Navigation
import androidx.navigation.findNavController
import androidx.navigation.fragment.navArgs
import androidx.navigation.ui.setupWithNavController
import andy.schedulekpi.R
import kotlinx.android.synthetic.main.home_fragment.*
import kotlinx.android.synthetic.main.home_fragment.view.*
class HomeFragment : Fragment() {
companion object {
fun newInstance() = HomeFragment()
}
private lateinit var viewModel: HomeViewModel
val safeArgs : HomeFragmentArgs by navArgs()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.home_fragment, container, false)
return view
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = ViewModelProviders.of(this).get(HomeViewModel::class.java)
// TODO: Use the ViewModel
//Navigation.setViewNavController(bottom_nav_view, activity!!.findNavController(R.id.my_nav_host_fragment))
bottom_nav_view.setupWithNavController(activity!!.findNavController(R.id.my_nav_host_fragment))
Toast.makeText(this.context, safeArgs.currentWeek.toString(), Toast.LENGTH_SHORT).show()
}
}
Thanks to Artur Gniewowski.