I know I am not the first to ask this question but I have referred many SO post regarding this but nothing is solved my query .
What I want to do is in my MainActivity(Bottom Navigation bar Activity) I have Bottom Navigation Bar, In this MainActivity I have cardviews If I clicked on the cardview I need to show another fragment in that fragment I want to hide the bottom navigation bar .And When I nav back to MainActivity botoom Navigation bar should be there.
Here in my case Alarm.java is the fragment where I want to hide the bottom navigation bar.
Alarm.java
public class Alarm extends Fragment {
private OnFragmentInteractionListener mListener;
public Alarm() {
}
public static Alarm newInstance(String param1, String param2) {
Alarm fragment = new Alarm();
Bundle args = new Bundle();
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_alarm, container, false);
}
public void onButtonPressed(Uri uri) {
if (mListener != null) {
mListener.onFragmentInteraction(uri);
}
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener) {
mListener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
public interface OnFragmentInteractionListener {
void onFragmentInteraction(Uri uri);
}
}
In your MainActivity you can implement two methods that will be responsible for showing and hiding your BottomNavigationView. For example, these two methods animate it sliding up and down:
private void hideBottomNavigationView(BottomNavigationView view) {
view.clearAnimation();
view.animate().translationY(view.getHeight()).setDuration(300);
}
public void showBottomNavigationView(BottomNavigationView view) {
view.clearAnimation();
view.animate().translationY(0).setDuration(300);
}
In MainActivity you call hide right before opening your CardView, and call show in onCreate or onResume.
EDIT:
But, I think that a cleaner way would probably be to create an interface in your Fragment:
public interface OnCardViewOpenedInterface{
void onOpen(); // hide bottom bar when photo is opened
void onClose(); // show bottom bar when photo is opened
}
And call these methods in onStop and onResume of your Fragment:
#Override
public void onStop() {
super.onStop();
mListener.onClose();
}
#Override
public void onResume() {
super.onResume();
mListener.onOpen();
}
And then implement the interface in your MainActivity, override the methods onOpen() and onClose() and inside call your hide and show methods.
They will probably both work, maybe the second one is overcomplicated, it's just I like it more.
I have tried the method by #Suleyman but it didn't work for me. The simplest solution is to use a public static method in the MainActivity and reference it from the fragment where you want to hide the bottom navigation bar.
Don't forget to initialize navView as public static.
In your MainActivity
public static void hideBottomNav(){
navView.setVisibility(View.GONE);
}
public static void showBottomNav(){
navView.setVisibility(View.GONE);
}
In your MyFragment
#Override
public void onResume() {
super.onResume();
MainActivity.hideBottomNav();
}
#Override
public void onStop() {
super.onStop();
MainActivity.showBottomNav();
}
I have this interface in my activity.
public interface LogoutUser {
void logout();
}
My fragment implements this interface, so in my fragment, I have this:
#Override
public void logout() {
// logout
}
In my activity I call
mLogoutUser.logout();
Where mLogoutUser is of the type LogoutUser interface.
My issue is the mLogoutUser object that is null. How can initialize it?
Thank you!
As I said in my comment, I resolved this issue using onAttach method in my fragment, but in this way you have to have the callback field (mLogoutUser in this case) declared in the fragment, and initialize it this way:
public class MyFragment extends ListFragment {
LogoutUser mLogoutUser;
// Container Activity must implement this interface
public interface LogoutUser {
public void logout();
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
mLogoutUser = (LogoutUser) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString()
+ " must implement LogoutUser");
}
}
...
}
More info in Communicating with Other Fragments.
But if your case is the field declared in the activity, you can use the onAttachFragment method from your activity to initialize your listener field this way:
#Override
public void onAttachFragment(Fragment fragment) {
super.onAttachFragment(fragment);
mLogoutUser = (LogoutUser) fragment;
}
Also, you can use an event bus to make this communication between fragments and activities. An option is the Otto library, from Square.
Sample for creating callback from Fragment to Activity
public interface CallBackListener {
void onCallBack();// pass any parameter in your onCallBack which you want to return
}
CallBackFragment.class
public class CallBackFragment extends Fragment {
private CallBackListener callBackListener;
public CallBackFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_call_back, container, false);
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//getActivity() is fully created in onActivityCreated and instanceOf differentiate it between different Activities
if (getActivity() instanceof CallBackListener)
callBackListener = (CallBackListener) getActivity();
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Button btn = (Button) view.findViewById(R.id.btn_click);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(callBackListener != null)
callBackListener.onCallBack();
}
});
}
}
CallbackHandlingActivity.class
public class CallbackHandlingActivity extends AppCompatActivity implements CallBackListener
{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_all_user);
}
#Override
public void onCallBack() {
Toast.makeText(mContext,"onCallback Called",Toast.LENGTH_LONG).show();
}
}
Android Fragments - Communicating with Activity
You need to get a reference to your fragment with getFragmentById() or getFragmentByTag()
getFragmentManager().findFragmentById(R.id.example_fragment);
You can use kotlinx Channel to send data or callback between fragments and activity or vice versa
In your Mainactivity:
val loginPromptChannel = Channel<LoginPromptState>()
val loginStateFlow = loginPromptChannel.receiveAsFlow()
//onCreate
lifecycleScope.launchWhenStarted {
loginStateFlow.collect() { state ->
when (state) {
is LoginPromptState.Login -> {
//smooth scroll to login fragment
binding.viewpager.setCurrentItem(2, true)
}
}
}
}
//create sealed a class
sealed class LoginPromptState {
object Login : LoginPromptState()
}
In your fragment send callback like:
lifecycleScope.launch {
val channelLogin = (activity as MainActivity).loginPromptChannel
channelLogin.send(MainActivity.LoginPromptState.Login)
}
I'm trying to pass data between a fragment and an activity and I can't. I get no errors or exceptions. On my fragment I have the vallue and on activity that value is null. I'm using interfaces.
Code of HoroscopeChoice Fragment, which is the fragment with buttons. Each button has a value, which I want to pass to the activity every time I push them.
(...)
static OnInfoChangedListener mCallback;
public HoroscopeChoice() {}
/******************************
* Callback
********/
public static void OnInfoChangedListener(OnInfoChangedListener callback) {
mCallback = callback;
}
public interface OnInfoChangedListener {
public void onInfoChanged(String horosocopo);
}
public View onCreateView(final LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_horoscope_choice,
container, false);
Button aquarius;
aquarius = (Button) view.findViewById(R.id.aquarius1);
final int id = view.getId();
View.OnClickListener onClickListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
String horoscopo = onClick2(v.getId());
Log.d("HoroscopeChoice", "push button->"+horoscopo);
mCallback.onInfoChanged(horoscopo);
}
};
aquarius.setOnClickListener(onClickListener);
public String onClick2(int id)
{
String horoscopo="";
if (id == R.id.aquarius1) {
horoscopo = "Aquarius";
}
}
(...)
Code of the Activity:
(...)
public void onInfoChanged(String horoscopo) {
Log.d("SchedulerActivity","OnInfoChanged na Scheduler->"+horoscope);
mHoroscopeDisplay = (TextView) findViewById(R.id.dailyHoroscope4);
mHoroscopeDisplay.setText(horoscopo);
}
When I do Log.d in the Fragment I get a value, on the Activity I have no value. Does anyone knows what is wrong?
You can do it via interface callbacks, but there is an even easier way with a great third party library called EventBus that is perfect for this kind of thing. You can send any object from one place to the other.
All you need to do is post the Event and create a listener method wherever is needed. Follow their guide, it's very easy.
Don't use a static listener object, especially to store something like an Activity. You should instead make the listener an instance variable. The standard pattern for using interface communication between Fragments and Activities is by using onAttach().
private OnInfoChangedListener mCallback;
#Override
public void onAttach(Activity activity) {
if (!(activity instanceof OnInfoChangedListener)) {
throw new IllegalStateException("Activity must implement OnInfoChangedListener!");
}
mCallback = (OnInfoChangedListener) activity;
}
#Override
public void onDetach() {
mCallback = null;
}
I'm making an Reddit app for my android exam and I have a question about inheritence.
I have a Fragment who has a RecyclerView. That recyclerview contains a list of redditposts. My app consists of multiple subreddits (funny, gaming, news, etc..). Every subreddit has his own Fragment. I have some methods that every Fragment has to have. (a showProgressBar, hideProgressBar, populateResult, etc...) I think it would be simple if i just make an Fragment class where all the subreddit Fragments can inheritance from. I could put all the methods in that fragment class because the methods are the same for every subreddit fragment. But my lecturer said that is a bad use of inheritance. So does anybody have a best practice around this problem?
This is the fragment i'm talking about:
package com.example.thomas.redditapp;
public class FunnyFragment extends Fragment {
private OnListFragmentInteractionListener mListener;
#Bind(R.id.funny_recyclerview)
RecyclerView mRecyclerView;
#Bind(R.id.progressBarFetch)
ProgressBar progress;
private RedditHelper helper;
private RedditPostRecyclerViewAdapter mAdapter;
List<RedditPost> redditPosts;
public FunnyFragment() {
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
helper = null;
helper = new RedditHelper(SubRedditEnum.funny, this);
redditPosts = new ArrayList<>();
startLoad();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_funny_list, container, false);
ButterKnife.bind(this, view);
showProgressBar();
mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
mAdapter = new RedditPostRecyclerViewAdapter(redditPosts, mListener, mRecyclerView);
mAdapter.setOnLoadMoreListener(new OnLoadMoreListener() {
#Override
public void onLoadMore() {
redditPosts.add(null);
helper.loadListFromUrl();
}
});
mRecyclerView.setAdapter(mAdapter);
return view;
}
protected void startLoad() {
if (helper != null) {
helper.loadListFromDb();
}
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
if (isTaskRunning()) {
showProgressBar();
} else {
hideProgressBar();
}
super.onActivityCreated(savedInstanceState);
}
public void hideProgressBar() {
progress.setVisibility(View.GONE);
}
public void showProgressBar() {
progress.setVisibility(View.VISIBLE);
progress.setIndeterminate(true);
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnListFragmentInteractionListener) {
mListener = (OnListFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnListFragmentInteractionListener");
}
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
public void populateResult(List<RedditPost> result) {
if(!redditPosts.isEmpty()){
redditPosts.remove(redditPosts.size() - 1);
}
redditPosts.addAll(result);
mAdapter.setLoaded();
mAdapter.notifyDataSetChanged();
}
protected boolean isTaskRunning() {
if (helper == null) {
return false;
} else if (helper.getStatus() == 0) {
return false;
} else {
return true;
}
}
}
I call the hideProgressBar(), showProgressBar() and populateResult() in my helper class.
There's a long standing mantra in programming that states: "Favor composition over inheritance"
You can read about the details of this statement and a lot of discussion here.
In this case, inheritance is unnecessary because you can simply build 1 Fragment and, on initialization pass it the subreddit, thus avoiding any constraining links between a super and subclass that may not even have any sort of polymorphic relationship.
I have one activity - MainActivity. Within this activity I have two fragments, both of which I created declaratively within the xml.
I am trying to pass the String of text input by the user into Fragment A to the text view in Fragment B. However, this is proving to be very difficult. Does anyone know how I might achieve this?
I am aware that a fragment can get a reference to it's activity using getActivity(). So I'm guessing I would start there?
Have a look at the Android developers page:
http://developer.android.com/training/basics/fragments/communicating.html#DefineInterface
Basically, you 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 with the received value
Your Activity implements your interface (See FragmentA below)
public class YourActivity implements FragmentA.TextClicked{
#Override
public void sendText(String text){
// Get Fragment B
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, thanks #Deepscorn
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
}
}
Some of the other examples (and even the documentation at the time of this writing) use outdated onAttach methods. Here is a full updated example.
Notes
You don't want the Fragments talking directly to each other or to the Activity. That ties them to a particular Activity and makes reuse difficult.
The solution is to make an callback listener interface that the Activity will implement. When the Fragment wants to send a message to another Fragment or its parent activity, it can do it through the interface.
It is ok for the Activity to communicate directly to its child fragment public methods.
Thus the Activity serves as the controller, passing messages from one fragment to another.
Code
MainActivity.java
public class MainActivity extends AppCompatActivity implements GreenFragment.OnGreenFragmentListener {
private static final String BLUE_TAG = "blue";
private static final String GREEN_TAG = "green";
BlueFragment mBlueFragment;
GreenFragment mGreenFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// add fragments
FragmentManager fragmentManager = getSupportFragmentManager();
mBlueFragment = (BlueFragment) fragmentManager.findFragmentByTag(BLUE_TAG);
if (mBlueFragment == null) {
mBlueFragment = new BlueFragment();
fragmentManager.beginTransaction().add(R.id.blue_fragment_container, mBlueFragment, BLUE_TAG).commit();
}
mGreenFragment = (GreenFragment) fragmentManager.findFragmentByTag(GREEN_TAG);
if (mGreenFragment == null) {
mGreenFragment = new GreenFragment();
fragmentManager.beginTransaction().add(R.id.green_fragment_container, mGreenFragment, GREEN_TAG).commit();
}
}
// The Activity handles receiving a message from one Fragment
// and passing it on to the other Fragment
#Override
public void messageFromGreenFragment(String message) {
mBlueFragment.youveGotMail(message);
}
}
GreenFragment.java
public class GreenFragment extends Fragment {
private OnGreenFragmentListener mCallback;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_green, container, false);
Button button = v.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String message = "Hello, Blue! I'm Green.";
mCallback.messageFromGreenFragment(message);
}
});
return v;
}
// This is the interface that the Activity will implement
// so that this Fragment can communicate with the Activity.
public interface OnGreenFragmentListener {
void messageFromGreenFragment(String text);
}
// This method insures that the Activity has actually implemented our
// listener and that it isn't null.
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnGreenFragmentListener) {
mCallback = (OnGreenFragmentListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnGreenFragmentListener");
}
}
#Override
public void onDetach() {
super.onDetach();
mCallback = null;
}
}
BlueFragment.java
public class BlueFragment extends Fragment {
private TextView mTextView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_blue, container, false);
mTextView = v.findViewById(R.id.textview);
return v;
}
// This is a public method that the Activity can use to communicate
// directly with this Fragment
public void youveGotMail(String message) {
mTextView.setText(message);
}
}
XML
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<!-- Green Fragment container -->
<FrameLayout
android:id="#+id/green_fragment_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginBottom="16dp" />
<!-- Blue Fragment container -->
<FrameLayout
android:id="#+id/blue_fragment_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
fragment_green.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:background="#98e8ba"
android:padding="8dp"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="#+id/button"
android:text="send message to blue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
fragment_blue.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:background="#30c9fb"
android:padding="16dp"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/textview"
android:text="TextView"
android:textSize="24sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
The nicest and recommended way is to use a shared ViewModel.
https://developer.android.com/topic/libraries/architecture/viewmodel#sharing
From Google doc:
public class SharedViewModel extends ViewModel {
private final MutableLiveData<Item> selected = new MutableLiveData<Item>();
public void select(Item item) {
selected.setValue(item);
}
public LiveData<Item> getSelected() {
return selected;
}
}
public class MasterFragment extends Fragment {
private SharedViewModel model;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
}
}
public class DetailFragment extends Fragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
model.getSelected().observe(this, { item ->
// Update the UI.
});
}
}
ps: two fragments never communicate directly
Consider my 2 fragments A and B, and Suppose I need to pass data from B to A.
Then create an interface in B, and pass the data to the Main Activity. There create another interface and pass data to fragment A.
Sharing a small example:
Fragment A looks like
public class FragmentA extends Fragment implements InterfaceDataCommunicatorFromActivity {
public InterfaceDataCommunicatorFromActivity interfaceDataCommunicatorFromActivity;
String data;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
return super.onCreateView(inflater, container, savedInstanceState);
}
#Override
public void updateData(String data) {
// TODO Auto-generated method stub
this.data = data;
//data is updated here which is from fragment B
}
#Override
public void onAttach(Activity activity) {
// TODO Auto-generated method stub
super.onAttach(activity);
try {
interfaceDataCommunicatorFromActivity = (InterfaceDataCommunicatorFromActivity) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement TextClicked");
}
}
}
FragmentB looks like
class FragmentB extends Fragment {
public InterfaceDataCommunicator interfaceDataCommunicator;
#Override
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
// call this inorder to send Data to interface
interfaceDataCommunicator.updateData("data");
}
public interface InterfaceDataCommunicator {
public void updateData(String data);
}
#Override
public void onAttach(Activity activity) {
// TODO Auto-generated method stub
super.onAttach(activity);
try {
interfaceDataCommunicator = (InterfaceDataCommunicator) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement TextClicked");
}
}
}
Main Activity is
public class MainActivity extends Activity implements InterfaceDataCommunicator {
public InterfaceDataCommunicatorFromActivity interfaceDataCommunicatorFromActivity;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public void updateData(String data) {
// TODO Auto-generated method stub
interfaceDataCommunicatorFromActivity.updateData(data);
}
public interface InterfaceDataCommunicatorFromActivity {
public void updateData(String data);
}
}
There are multiple ways to communicate between fragments.
Traditional way of communication via interface Example
Via ViewModel if you are following MVVM pattern Example
BroadcastReceivers: via LocalBraodcastManager Example or EventBus Example etc...
Take a look at https://github.com/greenrobot/EventBus
or http://square.github.io/otto/
or even ... http://nerds.weddingpartyapp.com/tech/2014/12/24/implementing-an-event-bus-with-rxjava-rxbus/
There is a simple way to implement communication between fragments of an activity using architectural components. Data can be passed between fragments of an activity using ViewModel and LiveData.
Fragments involved in communication need to use the same view model objects which is tied to activity life cycle. The view model object contains livedata object to which data is passed by one fragment and the second fragment listens for changes on LiveData and receives the data sent from fragment one.
For complete example see http://www.zoftino.com/passing-data-between-android-fragments-using-viewmodel
Since Fragment 1.3.0 we have available a new way to communicate between fragments.
As of Fragment 1.3.0, each FragmentManager implements FragmentResultOwner.
That means that a FragmentManager can act as a central storage for fragment results. This change allows components to communicate with each other by setting chunk results and listening to those results without those components having direct references to each other.
Fragment listener:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Use the Kotlin extension in the fragment-ktx artifact
setFragmentResultListener("requestKey") { requestKey, bundle ->
// We use a String here, but any type that can be put in a Bundle is supported
val result = bundle.getString("bundleKey")
// Do something with the result
}
}
Fragment emitter:
button.setOnClickListener {
val result = "result"
// Use the Kotlin extension in the fragment-ktx artifact
setFragmentResult("requestKey", bundleOf("bundleKey" to result))
}
Learn " setTargetFragment() "
Where " startActivityForResult() " establishes a relationship between 2 activities, " setTargetFragment() " defines the caller/called relationship between 2 fragments.
I give my activity an interface that all the fragments can then use. If you have have many fragments on the same activity, this saves a lot of code re-writing and is a cleaner solution / more modular than making an individual interface for each fragment with similar functions. I also like how it is modular. The downside, is that some fragments will have access to functions they don't need.
public class MyActivity extends AppCompatActivity
implements MyActivityInterface {
private List<String> mData;
#Override
public List<String> getData(){return mData;}
#Override
public void setData(List<String> data){mData = data;}
}
public interface MyActivityInterface {
List<String> getData();
void setData(List<String> data);
}
public class MyFragment extends Fragment {
private MyActivityInterface mActivity;
private List<String> activityData;
public void onButtonPress(){
activityData = mActivity.getData()
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof MyActivityInterface) {
mActivity = (MyActivityInterface) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement MyActivityInterface");
}
}
#Override
public void onDetach() {
super.onDetach();
mActivity = null;
}
}
You can user 2 approcach to communicate between 2 fragments:
1 )
You can use LiveData to observe data changes of one fragment in another
Create shared ViewModel
public class SharedViewModel extends ViewModel {
private MutableLiveData<String> name;
public void setNameData(String nameData) {
name.setValue(nameData);
}
public MutableLiveData<String> getNameData() {
if (name == null) {
name = new MutableLiveData<>();
}
return name;
}
}
Fragment One
private SharedViewModel sharedViewModel;
public FragmentOne() {
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sharedViewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
submitButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
sharedViewModel.setNameData(submitText.getText().toString());
}
});
}
Fragment Two
private SharedViewModel sharedViewModel;
public FragmentTwo() {
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sharedViewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
sharedViewModel.getNameData().observe(this, nameObserver);
}
Observer<String> nameObserver = new Observer<String>() {
#Override
public void onChanged(String name) {
receivedText.setText(name);
}
};
For more details on viewmodel you can refer to : mvvm-viewmodel-livedata , communicate fragments
2 )
You can use eventbus to achieve the same
implementation 'org.greenrobot:eventbus:3.2'
Define Event
public static class MessageEvent { /* Additional fields if needed */ }
Register/Unregister Subsciber
#Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
#Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
Listen To Events
#Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {/* Do something */};
Post Events
EventBus.getDefault().post(new MessageEvent());
Basically, following are the ways for communication between two fragments:
i) ViewModel
ii) Fragment Result API
iii) Interface
I use many fragments on tabs that need to share data between them, such as a ble scan tab that needs up update a device id on a settings tab.
The communication is a mess for something simple like one edittext.
My solution was to save data to sharedpreferences and use the fragment onResume to read and update.
I can extend the fields in Sharedpreferences later if I need to as well.
Update
Ignore this answer. Not that it doesn't work. But there are better methods available. Moreover, Android emphatically discourage direct communication between fragments. See official doc. Thanks user #Wahib Ul Haq for the tip.
Original Answer
Well, you can create a private variable and setter in Fragment B, and set the value from Fragment A itself,
FragmentB.java
private String inputString;
....
....
public void setInputString(String string){
inputString = string;
}
FragmentA.java
//go to fragment B
FragmentB frag = new FragmentB();
frag.setInputString(YOUR_STRING);
//create your fragment transaction object, set animation etc
fragTrans.replace(ITS_ARGUMENTS)
Or you can use Activity as you suggested in question..
I recently created a library that uses annotations to generate those type casting boilerplate code for you.
https://github.com/zeroarst/callbackfragment
Here is an example. Click a TextView on DialogFragment triggers a callback to MainActivity in onTextClicked then grab the MyFagment instance to interact with.
public class MainActivity extends AppCompatActivity implements MyFragment.FragmentCallback, MyDialogFragment.DialogListener {
private static final String MY_FRAGM = "MY_FRAGMENT";
private static final String MY_DIALOG_FRAGM = "MY_DIALOG_FRAGMENT";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportFragmentManager().beginTransaction()
.add(R.id.lo_fragm_container, MyFragmentCallbackable.create(), MY_FRAGM)
.commit();
findViewById(R.id.bt).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
MyDialogFragmentCallbackable.create().show(getSupportFragmentManager(), MY_DIALOG_FRAGM);
}
});
}
Toast mToast;
#Override
public void onClickButton(MyFragment fragment) {
if (mToast != null)
mToast.cancel();
mToast = Toast.makeText(this, "Callback from " + fragment.getTag() + " to " + this.getClass().getSimpleName(), Toast.LENGTH_SHORT);
mToast.show();
}
#Override
public void onTextClicked(MyDialogFragment fragment) {
MyFragment myFragm = (MyFragment) getSupportFragmentManager().findFragmentByTag(MY_FRAGM);
if (myFragm != null) {
myFragm.updateText("Callback from " + fragment.getTag() + " to " + myFragm.getTag());
}
}
}