简体   繁体   中英

Get value from Bottom Sheet Dialog Fragment

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?

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.

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.

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
    
                    }
    
                }

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());

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!

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.

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 .

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") 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM