20

I'm starting bottomSheetDialogFragment from a fragment A. I want to select the date from that bottomSheetDialogFragment then set it in the fragment A.

The select date is already done, I just want to get it in the fragment A to set it in some fields.

How can I get the value? Any suggestions how to do it?

Abdulrahman
  • 301
  • 1
  • 3
  • 12

8 Answers8

23

Create an interface class like this

public interface CustomInterface {

    public void callbackMethod(String date);
}

Implement this interface in your Activity or Fragment. and make an object of this Interface.

private CustomInterface callback;

Initialize it in onCreate or onCreateView

callback=this;

Now pass this callback in your BottomSheetDialogFragment constructor when you call it.

yourBottomSheetObject = new YourBottomSheet(callback);
yourBottomSheetObject.show(getSupportFragmentManager()," string");

Now in your BottomSheetFragment's constructor

private CustomInterface callback;

public SelectStartTimeSheet(CustomInterface callback){

this.callback=callback;

}

And at last use this callback object to set your date

callback.callbackMethod("your date");

and yout will recieve this date in your Fragment or Your Activity in callbackMethod function.

AbhayBohra
  • 2,047
  • 24
  • 36
  • 3
    this is not working for me. when i create a constructor with argument in bottomsheetfragment, it throws a warning saying that "Avoid non-default constructors in fragments"... any workaround? thanks – jackycflau Jun 12 '18 at 00:23
  • Just put this line @SuppressLint("ValidFragment") over your class name – AbhayBohra Jun 12 '18 at 05:06
  • 5
    Don't suppress, you can do: fragment.setCallbackListener(listener). But this is still not the preferred way if you are calling from a fragment or an activity. you can use getTargetFragment() for example in the case of fragment – Andrea Aranguren Jul 16 '18 at 15:10
  • @AndreaAranguren has given the better solution using getTargetFragment. This should be the chosen answer – Avi Mar 27 '19 at 10:18
10

override the constructor of a fragment is a bad practice as the document said:

Every fragment must have an * empty constructor, so it can be instantiated when restoring its * activity's state.

if you using another constructor that passing a callback as the param, when the fragment is resotored by the framework, your app crash

the recommend way is using viewModel and livedata.

hglf
  • 141
  • 1
  • 5
9

Android navigation architecture component

eg:

Suppose you open Fragment B from Fragment A using navController.

and you want some data from fragment B to Fragment A.

class B :BottomSheetDialogFragment() {

override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val root = inflater.inflate(R.layout.your_layout, container, false)

        root.sampleButton.setOnClickListener {
            val navController = findNavController()
            navController.previousBackStackEntry?.savedStateHandle?.set("your_key", "your_value")
            dismiss()

        }
}

and in your Fragment A:

findNavController().currentBackStackEntry?.savedStateHandle?.getLiveData<String>("your_key")
                ?.observe(viewLifecycleOwner) {
    
                    if (it == "your_value") {
                        //your code
    
                    }
    
                }
Yogesh Nikam Patil
  • 1,192
  • 13
  • 18
3

you can use do as below:

Select Account Fragment code


class SelectAccountFragment(val clickListener: OnOptionCLickListener) : BottomSheetDialogFragment() {


    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.bottom_fragment_accounts, container, false)
    }



    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val list = DataProcessorApp(context).allUsers

        val rvAccounts = view.findViewById<RecyclerView>(R.id.rvAccounts)

        rvAccounts.layoutManager = LinearLayoutManager(context)
        rvAccounts.adapter = AccountsAdapter(context, list)

        Log.e(tag,"Accounts "+list.size);

        tvAccountAdd.setOnClickListener {
            val intent = Intent(context,LoginActivity::class.java)
            startActivity(intent)
        }

        tvManageAccounts.setOnClickListener {
            Log.e(tag,"Manage Click")
            clickListener.onManageClick()
        }
    }


    interface OnOptionCLickListener{
        fun onManageClick()
    }

}


Now show and get call back into another fragment /activity as below

 SelectAccountFragment accountFragment = new SelectAccountFragment(() -> {

          //get fragment by tag and dismiss it

          BottomSheetDialogFragment fragment = (BottomSheetDialogFragment) getChildFragmentManager().findFragmentByTag(SelectAccountFragment.class.getSimpleName();
          if (fragment!=null){
               fragment.dismiss();
          }

});

accountFragment.show(getChildFragmentManager(),SelectAccountFragment.class.getSimpleName());
imran khan
  • 382
  • 3
  • 15
2

If you are using BottomSheetDialogFragment , since it's a fragment, you should create your interface and bind to it at onAttach lifecycle method of the fragment , doing the appropriate cast of activity reference to your listener/callback type.

Implement this interface in your activity and dispatch change when someone click in a item of fragment's inner recyclerview, for instance

It's a well known pattern and are explained better at here

One big advice is rethink your app architecture, since the best approach is to always pass primitive/simple/tiny data between Android components through Bundle, and your components are able to retrieve the required state with their dependencies later on.

For example, you should never pass along large Objects like Bitmaps, Data Classes , DTO's or View References.

  • first there is some serialization process going on regarding Parcel which impacts in app responsiveness
  • second it can lead you to TransactionTooLarge type of error.

Hope that helps!

william gouvea
  • 554
  • 4
  • 6
0

You can also use LocalBroadcastManager. And as hglf said, it is better to keep the empty constructor for your fragment and use newInstance(Type value) instead to instantiate your fragment if you still want to use the interface callBack way.

Abdelkader
  • 96
  • 5
0

You can use the benefit of Navigation library:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    val navController = findNavController();
    // After a configuration change or process death, the currentBackStackEntry
    // points to the dialog destination, so you must use getBackStackEntry()
    // with the specific ID of your destination to ensure we always
    // get the right NavBackStackEntry
    val navBackStackEntry = navController.getBackStackEntry(R.id.your_fragment)

    // Create our observer and add it to the NavBackStackEntry's lifecycle
    val observer = LifecycleEventObserver { _, event ->
        if (event == Lifecycle.Event.ON_RESUME
            && navBackStackEntry.savedStateHandle.contains("key")) {
            val result = navBackStackEntry.savedStateHandle.get<String>("key");
            // Do something with the result
        }
    }
    navBackStackEntry.lifecycle.addObserver(observer)

    // As addObserver() does not automatically remove the observer, we
    // call removeObserver() manually when the view lifecycle is destroyed
    viewLifecycleOwner.lifecycle.addObserver(LifecycleEventObserver { _, event ->
        if (event == Lifecycle.Event.ON_DESTROY) {
            navBackStackEntry.lifecycle.removeObserver(observer)
        }
    })
}

For more info, read the document.

Kaaveh Mohamedi
  • 1,399
  • 1
  • 15
  • 36
-5

The accepted answer is wrong.

What you can do is just user Fragment A's childFragmentManager when calling show().

like this:

val childFragmentManager = fragmentA.childFragmentManager
bottomSheetDialogFragment.show(childFragmentManager, "dialog") 
Alécio Carvalho
  • 13,481
  • 5
  • 68
  • 74