I need to disable user from dragging bottom sheet, but I have no idea on how to do that.
This is my fragment:
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
class MyFragment : BottomSheetDialogFragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val main = inflater.inflate(R.layout.fragment_layout, container, false)
return main
}
companion object {
fun newInstance(): MyFragment = newInstance()
}
}
This is how I show it:
val test = MyFragment()
test.show(supportFragmentManager, "test")
You need to override onCreateDialog() and put below code
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog
dialog.setOnShowListener {
val bottomSheet = (it as BottomSheetDialog).findViewById<View>(com.google.android.material.R.id.design_bottom_sheet) as FrameLayout?
val behavior = BottomSheetBehavior.from(bottomSheet!!)
behavior.state = BottomSheetBehavior.STATE_EXPANDED
behavior.setBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
override fun onStateChanged(bottomSheet: View, newState: Int) {
if (newState == BottomSheetBehavior.STATE_DRAGGING) {
behavior.state = BottomSheetBehavior.STATE_EXPANDED
}
}
override fun onSlide(bottomSheet: View, slideOffset: Float) {}
})
}
// Do something with your dialog like setContentView() or whatever
return dialog
}
or check this link
How to disable BottomSheetDialogFragment dragging
Related
I have ViewPager2, Tablayout with Lazyloder.
At the first getting in the fragment it works, dots move while loading but after I swipe from one fragment to another and back to my first fragment dots don't move anymore.
.xml
<com.agrawalsuneet.dotsloader.loaders.LazyLoader
android:id="#+id/dots_loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_margin="20dp"
android:layout_marginTop="30dp"
app:lazyloader_animDur="400"
app:lazyloader_dotsDist="10dp"
app:lazyloader_dotsRadius="7dp"
app:lazyloader_firstDelayDur="150"
app:lazyloader_firstDotColor="#fff"
app:lazyloader_interpolator="#android:anim/decelerate_interpolator"
app:lazyloader_secondDelayDur="300"
app:lazyloader_secondDotColor="#fff"
app:lazyloader_thirdDotColor="#fff"
tools:ignore="MissingClass" />
fragment
#AndroidEntryPoint
class InspirationQuotesFragment :
BaseFragment<FragmentInspirationQuotesBinding, InspirationQuotesViewModel>
(FragmentInspirationQuotesBinding::inflate) {
override val vm: InspirationQuotesViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
backgroundAnimation()
getRandomPicture()
onFloatingActionClick()
}
override fun onResume() {
super.onResume()
}
private fun getRandomPicture() {
layout.floatingActionBtn.setOnClickListener {
onFloatingActionClick()
}
}
private fun onFloatingActionClick() {
layout.floatingActionBtn.animate().apply {
rotationBy(360f)
duration = 1000
}.start()
layout.ivRandomPicture.visibility = View.GONE
makeApiRequest()
}
private fun backgroundAnimation() {
val animationDrawable: AnimationDrawable = layout.rlLayout.background as AnimationDrawable
animationDrawable.apply {
setEnterFadeDuration(1000)
setExitFadeDuration(3600)
start()
}
}
private fun makeApiRequest() = lifecycleScope.launch {
vm.randomPicture
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
.collect { response ->
if (response.fileSizeBytes < 600000) {
Log.d("fragment", "itGetsValue")
Glide.with(requireContext()).load(response.url)
.into(layout.ivRandomPicture)
layout.ivRandomPicture.visibility = View.VISIBLE
} else {
onFloatingActionClick()
}
}
}
}
viewpagerfragment
package com.example.learningmanager.fragments
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.View
import androidx.fragment.app.viewModels
import com.example.learningmanager.base.ui.BaseFragment
import com.example.learningmanager.databinding.FragmentViewPagerBinding
import com.example.learningmanager.fragments.inspirationquotes.ui.InspirationQuotesFragment
import com.example.learningmanager.fragments.notesmanager.ui.NotesManagerFragment
import com.example.learningmanager.fragments.setgoals.ui.SetGoalsFragment
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
#AndroidEntryPoint
class ViewPagerFragment #Inject constructor() : BaseFragment<FragmentViewPagerBinding, ViewPagerViewModel>(
FragmentViewPagerBinding::inflate
) {
override val vm: ViewPagerViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// viewpager with list of fragments
val fragmentList = arrayListOf<Fragment>(
InspirationQuotesFragment(),
NotesManagerFragment(),
SetGoalsFragment()
)
val adapter = ViewPagerAdapter(
fragmentList,
childFragmentManager,
lifecycle
)
layout.viewPager.adapter = adapter
// initialize tablayout with names
TabLayoutMediator(layout.tabLayout, layout.viewPager) {tab, position ->
when (position) {
0 -> tab.text = "Inspiration"
1 -> tab.text = "Notes"
2 -> tab.text = "Set Goals"
}
}.attach()
listener if tab changes and it is possible to make specific action
layout.tabLayout.addOnTabSelectedListener(object: TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab?) {
}
override fun onTabUnselected(tab: TabLayout.Tab?) {
}
override fun onTabReselected(tab: TabLayout.Tab?) {
}
})
}
}
viewpageradapter
class ViewPagerAdapter(list: ArrayList<Fragment>, fm: FragmentManager, lifecycle: Lifecycle) :
FragmentStateAdapter(fm, lifecycle) {
val fragmentList = list
override fun getItemCount(): Int {
return fragmentList.size
}
override fun createFragment(position: Int): Fragment {
return fragmentList[position]
}
}
Have you got an information how to fix it or maybe change to another library?
Thank you
I ran the same sample at my end. I found out that the trick is to use initView() on onResume of a fragment. Which internally calls startLoading.
Sample code:
lateinit var dotsLoader: LazyLoader
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_test1, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
dotsLoader = view.findViewById(R.id.dots_loading)
}
override fun onResume() {
super.onResume()
dotsLoader.initView()
}
I created a bottom sheet dialog and am implementing cancel function.
It has to be not dismissed when outside of the dialog is touched, but the bottom navigation should catch the touch. After a long trial, I realized I cannot use "setCanceledOnTouchOutside" on Kotlin. "isCancelable=false" is working but cannot use backbutton on the bottom navigation. How should I do if I want to make the bottom navigation touchable only?
Any help will be greatly appreciated
My code is here
class BiometricChangeDetectDialog: BottomSheetDialogFragment() {
private var _binding: DialogBiometricChangeDetectBinding? = null
private val binding get() = _binding!!
private lateinit var mContext: MainActivity
override fun onAttach(context: Context) {
super.onAttach(context)
mContext = context as MainActivity
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AlertDialog.Builder(requireContext()).apply {
isCancelable = false
//change here to setCanceledOnTouchOutside
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View? {
_binding = DialogBiometricChangeDetectBinding.inflate(inflater, container, false)
val view = binding.root
binding.btnEmail.setOnClickListener {
dismiss()
findNavController().navigate(R.id.action_biometricChangeDetectDialog_to_biometricChangeEmailDialog)
}
binding.btnSms.setOnClickListener {
dismiss()
findNavController().navigate(R.id.action_biometricChangeDetectDialog_to_biometricChangeEmailDialog)
}
return view
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
add this line in onCreateView
dialog?.setCanceledOnTouchOutside(false)
So, I am trying to migrate from kotlin synthetic to Jetpack view binding.
Here is the kotlin synthetic code (works fine) that simply set visibility to invisible of TextView in the parent activity from fragment.
import kotlinx.android.synthetic.main.activity_main.*
class FirstFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_first, container, false)
requireActivity().textView.visibility = View.INVISIBLE
return view
}
}
And here is what I'm doing to migrate:
import com.mypc.myapp.databinding.FragmentFirstBinding
import com.mypc.myapp.databinding.ActivityMainBinding
class FirstFragment : Fragment() {
private var _binding: FragmentFirstBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentFirstBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.textView.visibility = View.INVISIBLE
binding.textview.setOnClickListener {
Navigation.findNavController(view).navigate(R.id.goto_secondfragment)
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
I'm getting error as 'Unsolved reference' at 'textview':
binding.textView.visibility = View.INVISIBLE
And at:
binding.textview.setOnClickListener {
Navigation.findNavController(view).navigate(R.id.goto_secondfragment)
}
Obviously the compiler is not able to find TextView that is in Activity
I've added this line:
import com.mypc.myapp.databinding.ActivityMainBinding
Since your binding is private to MainActivity you can refer to your textView from the MainActivity only. To show/hide this view from FirstFragment you can create a public function in MainActivity and call it from your FirstFragment.
class MainActivity: AppCompatActivity {
private var _binding: ActivityMainBinding? = null
private val binding get() = _binding!!
fun showHideTextView(visible: Boolean) {
binding.textView.isVisible = visible
}
}
And in your fragment, you can call:
(requireActivity() as MainActivity).showHideTextView(false) // This will hide the textView
First of all, you should define instance of activity view binding in baseActivity which is a parent class of your MainActivity, and then define method to change your text view like 'showTextView' , after that in the base fragment class initalize base activity instance with casting context object in onAttach method.
I provide you some code:
abstract class BaseRegisterActivity : BaseActivity() {
//---
protected lateinit var binding: ActivityRegisterBinding
private val navHostFragment by lazy {
supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as
NavHostFragment
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_register)
//---
}
fun showTextView() {
binding.textView.visibility = View.VISIBLE
binding.textView.setOnClickListener {
val fragment =
navHostFragment.childFragmentManager.fragments[0]
if (fragment is FirstFragment) {
//todo
}
}
}
}
abstract class BaseFragment : Fragment(), Injectable {
//--
lateinit var baseActivity: BaseRegisterActivity
override fun onAttach(context: Context) {
super.onAttach(context)
baseActivity = context as BaseRegisterActivity
}
//--
}
class ShopFragment : Fragment() {
var binding:FragmentShopBinding ?= null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = FragmentShopBinding.inflate(inflater,container,false)
return binding?.root
}
I struggle to do a property delegate for ViewBinding on BottomSheets.
The general Idea is similar to this
For Fragments i use something like this
fun <T : ViewBinding> Fragment.viewBinding(viewBindingFactory: (View) -> T) =
FragmentViewBindingDelegate(this, viewBindingFactory)
On BottomSheetsDialogFragments which are Fragments it does not accept the delegate.
fun <T : ViewBinding> BottomSheetDialogFragment.viewBinding(viewBindingFactory: (View) -> T) =
FragmentViewBindingDelegate(this, viewBindingFactory)
The Lifecycle of BottomSheets would be the same as on regular Fragments, so i would not expect any problems.
Anyone came up with a solution on this ?
You can still use the FragmentViewBindingDelegate and extension from Gabor.
You just also need to inflate the view inside onCreateView().
For example:
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.zhuinden.fragmentviewbindingdelegatekt.viewBinding
class ExampleBottomSheet : BottomSheetDialogFragment() {
//Using ::bind here since the view is already inflated in onCreateView()
private val binding by viewBinding(YourCustomViewBinding::bind)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.your_custom_view, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//Use binding here or wherever you need it
}
}
Implement ViewBinding in BottomSheetFragment like this (Works for me):
class CustomBottomSheet : BottomSheetDialogFragment() {
private lateinit var binding: CustomBottomSheetBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = CustomBottomSheetBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//onClick listener
binding.button.setOnClickListener {
Toast.makeText(context, "Clicked", Toast.LENGTH_LONG).show()
}
}
}
This works:
class CampaignBottomSheet : BottomSheetDialogFragment() {
private var _binding: MenuCampaignsBinding? = null
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
_binding = MenuCampaignsBinding.inflate(layoutInflater)
binding.startCampaign.setOnClickListener { println("__print::CampaignBottomSheet") }
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
Unlike Fragment class, BottomSheetDialogFragment doesn't have a constructor that accepts layout resource.
You can create a custom dialog for it. Basically, copy everything from AppCompatDialogFragment and BottomSheetDialogFragment.
I have 7 fragments, I swip between them but it's slow, I did a test adding a fragment that just contains a textView, the swip to this fragment is fast, the difference between my fragments and this one is the amount of components inside.
class ViewPagerAdapter(fragmentManager: FragmentManager, lifecycle: Lifecycle) :
FragmentStateAdapter(fragmentManager, lifecycle) {
val fragments:ArrayList<Fragment> = arrayListOf(
Fragment_Onboarding_page1(),
Fragment_Onboarding_page2(),
Fragment_Onboarding_page3(),
Fragment_Onboarding_page4(),
Fragment_Onboarding_page5(),
Fragment_Onboarding_page6(),
Fragment_page6_be_yourself()
)
override fun getItemCount(): Int {
return fragments.size
}
override fun createFragment(position: Int): Fragment {
return fragments[position]
}
}
class EcranAuthentification : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.fragment_view_pager)
supportActionBar?.hide()
initViewPager2WithFragments()
}
private fun initViewPager2WithFragments()
{
var viewPager2: ViewPager2 = findViewById(R.id.viewpager)
var adapter = ViewPagerAdapter(supportFragmentManager,lifecycle)
viewPager2.adapter = adapter
}
}
class Fragment_Onboarding_page2 : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val rootView=inflater.inflate(R.layout.fragment__onboarding_page2, container, false)
val viewpager=activity?.findViewById<ViewPager2>(R.id.viewpager)
rootView.skip2.setOnClickListener {
viewpager?.currentItem=6
}
rootView.boutonRetour.setOnClickListener {
viewpager?.currentItem=0
}
return rootView
}
}
Please can you help me