onAttach callback from fragment to activity - android

I want to send String data from fragment to activity.
I have read the article about communicating between fragment and activity in android developer, using onAttach callback.
can anyone explain clearly how to send data from fragment to activity?

You should do something like this. First create an interface which will use to comunicate with your activity for example :
public interface OnViewSelected {
public void onViewSelected(int viewId);
}
and in your onAttach do this :
OnViewSelected _mClickListener;
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
_mClickListener = (OnViewSelected) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + " must implement onViewSelected");
}
}
In your Fragment implement OnClickListener and in your onClick() method do this :
#Override
public void onClick(View v) {
_mClickListener.onViewSelected(456);
}
After that in your Activity you have to implement the interface you created in your Fragment and it will ask you to add unimplemented methods and in your activity you will have function like this :
#Override
public void onViewSelected(int data) {
Log.d("","data : "+data); // this value will be 456.
}
That's all. : )

Related

How can a BottomSheetDialogFragment communicate with its host fragment?

I have a button in my fragment which opens a BottomSheetDialogFragment. I want to notify the host fragment if the user selected an item on the BottomSheetDialogFragment. In order to achieve this, I have made an interface in my BottomSheetDialogFragment. However, that interface only communicates with the host activity, not the fragment. How can I send the information from the dialog to the fragment?
This is my interface:
public interface BottomSheetListener {
void onButtonClicked(int index);
}
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
try {
mListener = (BottomSheetListener) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + " must implement BottomSheetListener");
}
}
getParentFragment will return the parent fragment, if the current fragment is attached to a fragment else it will return null if it is attached directly to an Activity
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
try {
mListener = (BottomSheetListener) getParentFragment();
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + " must implement BottomSheetListener");
}
}
When you use a lot of fragments, nested fragments or dialogfragments it becomes messy for communicate between them. I am suggesting to use ViewModel with LiveData for passing and updating data.
first add this to build gradle :
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
then create ViewModel class :
public class YourViewModel extends AndroidViewModel {
private MutableLiveData<Integer> yourMutableLiveData=new MutableLiveData<>();
public YourViewModel(#NonNull Application application) {
super(application);
}
public MutableLiveData<Integer> getYourMutableLiveData() {
return yourMutableLiveData;
}
}
This the fragment you want set value :
public class FragmentA extends Fragment{
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
YourViewModel yourViewModel =new ViewModelProvider(getActivity()).get(YourViewModel.class);
yourViewModel.getYourMutableLiveData().setValue(0);
}
}
And this is the fragment you want to get value when updated :
public class FragmentB extends Fragment{
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
YourViewModel yourViewModel =new ViewModelProvider(getActivity()).get(YourViewModel.class);
yourViewModel.getYourMutableLiveData().observe(getViewLifecycleOwner(), new Observer<Integer>() {
#Override
public void onChanged(Integer integer) {
}
});
}
}
It can work on dialog fragment as well as I tested.
Notes :
-Do not pass context or any view into view model.
-Remember that onActivityCreated comes after onCreateView.
-Do not set this key to
YourViewModel yourViewModel =new ViewModelProvider(this).get(YourViewModel.class);
in fragment if you want to pass data fragment to fragment but you can pass in activity.
-You can set more than one observer to the data.

Enter EditText on a Fragment and fill also all other Fragments

I have implemented the Android Tutorial from the Android Training Site and everything works so far.
Inside of a Fragment, I added an EditText field, where the user can enter a simple text string.
What I want to achieve is: If the user entered something, and then swipes to left or right, the input should be "copied" the new fragment as well.
As in the training, I have got a ScreenSlidePageFragment.java class and a ScreenSlideActivity.java class.
I've implemented an Interface in the ScreenSlidePageFragment, as mentioned here:
Communicator mCallback;
public interface Communicator {
void sendData(String inpString);
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
mCallback = (Communicator) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + "must Implement OnEditChangedListener");
}
}
But at the end, I just managed to receive it on the ScreenSlideActivity. How do I update a specified fragment?
In ScreenSlideActivity I tried to send it back to the new fragment.
#Override
public void sendData(String inpString) {
WHATFRAGMENT.changeData(inpString);
}
But how do I access another one (WHATFRAGMENT), which are created in the ScreenSlidePagerAdapter:
#Override
public Fragment getItem(int position) {
return NewReservationSlidePageFragment.create(position);
}
Do I need to create a whole Fragment and overwrite the new position? Or is it even necessary to work around the Activity? Is it possible to use FragmentTransaction somehow?
What would be the simplest solution for that?
You can achieve with your interface and public void setUserVisibleHint(boolean isVisibleToUser)
Add one more method in your interface like this,
public interface Communicator {
void sendData(String inpString);
String getData();
}
And in you activity create one String variable Globally to store the value.
And assign the data coming form sendData in that
#Override
public void sendData(String inpString) {
this.value = inpString;
//WHATFRAGMENT.changeData(inpString);
}
and implement getData too in your Activity
#Override
public void getData() {
return this.value;
}
Add setUserVisibleHint in your fragment
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
if(isVisibleToUser) {
mCallback.sendData("[string from your edit text]");
} else {
String value = mCallback.getData();
}
}
Now you can see the data which you edited in one fragment can show in other visible fragment.

Accessing to other Fragment variables or elements from Fragment

For example I have 2 fragments including 1 integer variable and 1 TextView for each. One of them has a button. I want this button to change all Integers and TextViews including the other fragment. How can I access to Variable and TextView of the other fragment ? Please explain with example code.
Fragment to Fragment Communication basically happens via an activity which generally hosts the Fragments, define an interface in your Fragment A, and let your Activity implement that Interface. Now you can call the interface method in your Fragment, and your Activity will receive the event. Now in your activity, you can call your second Fragment to update the textview(For example) with the received value:
// You Activity implements your interface which is defined in FragmentA
public class YourActivity implements FragmentA.TextClicked{
#Override
public void sendText(String text){
// Get instance of Fragment B using FragmentManager
FraB frag = (FragB)
getSupportFragmentManager().findFragmentById(R.id.fragment_b);
frag.updateText(text);
}
}
// Fragment A defines an Interface, and calls the method when needed
public class FragA extends Fragment{
TextClicked mCallback;
public interface TextClicked{
public void sendText(String text);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
mCallback = (TextClicked) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement TextClicked");
}
}
public void someMethod(){
mCallback.sendText("YOUR TEXT");
}
#Override
public void onDetach() {
mCallback = null; // => avoid leaking
super.onDetach();
}
}
// Fragment B has a public method to do something with the text
public class FragB extends Fragment{
public void updateText(String text){
// Here you have it
}
}

Multiple Interfaces in Single Fragment

I have a Fragment that needs to communicate more than one Action back to it's Activity. For example,
When a button is clicked, it needs to communicate the onClick back to the Activity.
2.When a user's login and password match, a boolean value is sent to the Activity notifying it to start an Intent.
My first question is, is this common where a Fragment needs to relay more that one type of Action back to the Activity? And secondly, how is this solved? Is the following a good way to do it...
I created a custom class, which extends Fragment and included the two interfaces that I need (One to pass the onClick back to the Activity and One to pass a boolean value):
public class CustomInterfaceFragment extends Fragment {
public OnClickedListener listener;
public LogInInterface loggedInListener;
static interface OnClickedListener{
public void buttonClicked(View v);
}
static interface LogInInterface{
public void userLoggedIn(boolean loggedIn);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
this.listener = (OnClickedListener)activity;
this.loggedInListener = (LogInInterface)activity;
}}
I then extended this custom class in my Fragment and used the appropriate methods where needed. This is the onClick method in the Fragment...
#Override
public void onClick(View v) {
switch (v.getId()){
case R.id.register_button:{
listener.buttonClicked(v);//***Pass onClick Back to Activity
break;
}
case R.id.fragment_login_loginButton:{
ParseUser.logInInBackground(userName.getText().toString(), password.getText().toString(), new LogInCallback() {
#Override
public void done(ParseUser user, ParseException e) {
if (user!=null){
boolean verified = user.getBoolean("emailVerified");
if(!verified){
Toast.makeText(getActivity(),"Please Verify",Toast.LENGTH_LONG).show();
progressDialog.dismiss();
ParseUser.logOut();
}else{
progressDialog.dismiss();
loggedInListener.userLoggedIn(true);//***Pass boolean Back to Activity
}
}else {
Toast.makeText(getActivity(),e.getMessage(),Toast.LENGTH_LONG).show();
progressDialog.dismiss();
}
}
});
}
break;
}
}
Finally I implemented the custom fragment class and its interfaces in my Activity in order to retrieve the data.
Is this a reasonable way to solve this problem or am I missing something? The application seems to work fine. I just want to know what the best programming practice would be. Thank you.
all i can say is you can bring down this two interfaces to one like this below
public interface fragmentInteractions{
public void OnClickedListener(View v);
public void userLoggedIn(boolean loggedIn);
....
....
}
and i don't think the interface here needs to be static
Elaborating on Avinash Joshi's answer :
public interface CustomListener {
void onButtonClicked();
void onLoginResult( boolean isUserLoggedIn ); // You can pass User object via this method in case its required to do some operations
}
public class MainActivity extends Activity implements CustomListener {
#Override
public void onCreate( Bundle savedInstance ) {
// Initialize UI elements
// Initialize Fragment
}
#Override
public void onButtonClicked() {
//Action to be performed on button click
}
#Override
public void onLoginResult( boolean isUserLoggedIn ) {
if( isUserLoggedIn ) {
//take user to dashboard or any other screen
//Usually with the help of SupportFragmentManager
}
else {
//Take user to signup screen with an optional toast message
//In case parameters like User name and password need not be entered by user again, you can access them as function parameters and pass them to signupFragment via bundle
}
}
}
public class LoginFragment extends Fragment {
CustomListener mCustomListener;
#Override
public void onAttach( Context context ) {
super.onAttach( Context context );
try {
mCustomListner = (CustomListener) context;
} catch ( ClassCastException e {
Log.e(TAG, "Activity must implement CustomListener")
}
}
//Rest of Fragment initialization code here
}
Here's a complete example :
http://www.truiton.com/2015/12/android-activity-fragment-communication/

Communication between Fragments

I have default Master-Detail flow, which was created automatically when creating new project. My question is. When I add a button to detail side. Is there a way to update my list side by pressing that button ? In other words, can ItemDetailFragment and ItemListFragment communicate ?
Yes just communicate through the activity with a listener.
Your activity:
public class MyActivity extends FragmentActivity implements OnFragmentClickListener {
#Override
public void OnFragmentClick(int action, Object object) {
switch(action) {
}
}
}
The listener class:
public interface OnFragmentClickListener {
public void OnFragmentClick(int action, Object object);
}
Your fragments will then have following somewhere in code in order to implement the interface:
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (OnFragmentClickListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement listeners!");
}
}
Then your fragments communicate with each other like this: fragmentA -> activity -> fragmentB. Your activity can call methodes directly on the fragments without worrying about synchronization problems.
Example of a call from fragment a:
mListener.OnFragmentClick(GLOBAL_ACTION_KEY someObject);
Activity then handle:
public class MyActivity extends FragmentActivity implements OnFragmentClickListener {
#Override
public void OnFragmentClick(int action, Object object) {
switch(action) {
case GLOBAL_ACTION_KEY:
// you call fragmentB.someMethod();
break;
}
}
}

Categories

Resources