In my main Activity, I have a DialogFragment that contains a FragmentTabHost. I have two tabs, one that is a DialogFragment and one that is a ListFragment. When either the 'OK' button is pressed in the inner DialogFragment or when an element in the ListFragment is pressed, I want to pass two Strings (that are entered in two TextView's in the inner DialogFragment and are displayed in each element in the ListFragment) back to the Activity, but I am unsure of how to do this with multiple levels of Fragments.
Any help is appreciated!
There's no magic.
You can achieve with two approaches.
Use callback.
Create interface and class to pass the data through child Fragment to Activity. You don't need to modify bridged TabHostFragment as Fragment always rely on its mother Context (Activity) no matter how many fragments wrap the fragment.
public class TwoStrings {
public TwoStrings(String one, String two){
this.one = one;
this.two = two;
}
public String one;
public String two;
}
First, declare interface.
public interface DataPassListener {
void dataPassed(TwoStrings data);
}
And, implement interface in Activity.
public class MainActivity extends Activity implements DataPassListener {
#Override
public void dataPassed(TwoStrings data) {
// do something with data.
Log.d("string one", data.one);
Log.d("string two", data.two);
}
}
Finally, let child Fragment acknowlege that mother Activity has the callback listener.
public class DialogFragment1 extends DialogFragment {
DataPassListener listener;
#Override
public void onAttach(Activity activity) {
if (activity instanceOf DataPassListener)
listener = (DataPassListener) activity;
}
public void setDataPassListener(DataPassListener listener){
listener = ((DataPassListener) listener);
}
public void doSomeThing(){
if(listener != null) // important to prevent NullPointerException
listener.dataPassed("a", "b");
}
}
Use EventBus.
I prefer to use Otto in order to publish and subscribe data.
To subscribe event for listening in Activity,
public class MainActivity extends Activity {
#Override
public void onResume() {
super.onResume();
BusProvider.getInstance().register(this);
}
#Override
public void onPause() {
super.onPause();
BusProvider.getInstance().unregister(this);
}
#Subscribe
public void onUpdateTwoStrings(TwoStrings event) {
// do something with data.
Log.d("string one", data.one);
Log.d("string two", data.two);
}
}
And, publish event in anywhere in Fragment.
bus.post(new TwoStrings("a", "b"));
Take a look at setTargetFragment() and getTargetFragment() methods. You could connect fragments with each other through it without any additional callbacks and libs.
Related
Is it possible for two (switch) buttons to have synchronized behavior between fragments? i.e: When button A is switched on in fragment A, I want button B's appearance in fragment B to also appear switched on.
How would I do that? The end goal is to have a global button on either fragment.
You can use a boolean flag in your activity and set it false by default and when any of switch is pressed on in either fragment then set its value to true, and when you navigate to another fragment then check flag value and if its true then switch it on or else off.
That would depend on whether both fragments are on same activity. If they are, then all you need is a boolean flag on the said activity and synchronise depend on that.
If not, then maybe create an boolean preference and synchronise based on that. Hope this helps. If you need code examples, let me know.
public interface Listener {
public boolean getFlag();
public void setFlag(boolean enable);
}
public class SomeActivity extends AppCompatActivity implements Listener {
// getFlag, setFlag implementation
}
public class FragmentA {
private boolean state;
private Listener listener;
private Switch switchBtn;
public void onAttach(Context ctx){
listener = (Listener) this.getActivity();
// check for ClassCast Exception
}
public void onActivityCreated() {
state = listener.getFlag();
switchBtn.setChecked(state);
}
}
For details view this page
Sample of communication between Fragments. Example below is modified from Communicating with Other Fragments
Step 1. Create an interface
public interface ButtonCallback{
void onClick(boolean val)
}
Step 2. In the HostActivity which hosts both fragment A and B, make HostActivity implements interface ButtonCallback.
public class HostActivity extends AppCompatActivity implements ButtonCallback{
void onClick(boolean val){
}
}
Step 3. In fragment A & B, initialize the callback with casting the activity
class FragmentA extends Fragment{
ButtonCallback callback;
#Override
public void onAttach(Context context) {
super.onAttach(context);
//Make sure activity host implement ButtonCallback interface
try {
callback= (ButtonCallback ) context;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement ButtonCallback");
}
}
//public method to update fragment's button state
public void setGlobalButtonState(boolean val){
//globalButton has been initialized in onCreateView function
globalButton.setEnabled(val);
}
}
class FragmentB extends Fragment{
ButtonCallback callback;
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
callback= (ButtonCallback ) context;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement ButtonCallback");
}
}
public void setGlobalButtonState(boolean val){
//globalButton has been initialized in onCreateView function
globalButton.setEnabled(val);
}
}
Step 4. In Fragment A, call ButtonCallback.onClick(boolean) when user click on the button
globalButton.setOnClickListener(new View.OnClickListener(View v){
Boolean value = !v.isEnabled();
callback.onClick(value)
});
Step 5. In HostActivity's onClick function, find a way to get Fragment B that suis your context, and update the button in Fragment B via the setGlobalButtonState function
void onClick(boolean val){
//get Fragment B.
fragmentB.setGlobalButtonState(val);
}
Additional note, if the button meant to be global, it might worth to consider to put the button on the host Activity instead if that suits your context.
Here is the developer guide on communicating with other fragments: https://developer.android.com/training/basics/fragments/communicating .
In the activity have the shared attribute. In each fragment, go to the parent activity to get that attribute on button click.
To reduce dependency, use an interface to obtain the data from the activity, as shown in the link provided
For Fragment(put data to activity)
m=(MainActivity)getActivity();
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
Intent in=new Intent(getActivity(),MainActivity.class)
in.putExtra("test",test);
startActivty(in)
}
},10);
For Activity (get data from fragment )
{
String get_data=getIntent.getStringExtra("test");
}
//it will return always null...any body help me?
startActivty(in) will start the same activity.
Instead of this, you can make use of Interface. It's the easiest way to pass the data.
in your fragment, you can have an interface like,
SubmitData mSubmitData;
public interface SubmitData{
void DataListener(String s);
}
mSubmitData.DataListener("data to be sent");
In your activity, implement the SubmitData interface. It will make you override the DataListener method, where you can get the data.
public class MyActivity extends AppCompatActivity implements YourFragment.SubmitData{
#Override
public void DataListener(String s) {
// Data from the fragment
}
This questions has been asked and answered multiple times. You can find a valid reply here https://stackoverflow.com/a/9977370/5828132
Basically, it consists of creating an interface in the Fragment (for example) including a simple method. The Fragment has to declare a field of that type, and the Activity hosting the Fragment has to implement (implements) that interface. Both entities are usually connected using a explicit cast, in the onAttach() callback of the Fragment life-cycle, i.e.:
#Override
public void onAttach(Context context) {
super.onAttach(context);
// fragmentField_interfaceType = (interfaceType) context;
}
Hope it helps!
Can someone please tell me if I'm solving this correctly or if I should go another route?
This is a simplified example: I have 1 Activity and 2 Fragments. Each Fragment has a button that when clicked, relays the click back to the Activity and a Toast pops up within the Activity.
I know that a Fragment communicates with an Activity through an interface. But what If I have multiple Fragments that have a similar Interface. For example, here both Fragments use an onClick type of interface to communicate back to the Activity
static interface OnClickedListener{
public void buttonClicked(View v);
}
Is it better to
A) Create a separate Interface class and attach it within both Fragments. For example Fragment 1:
public class Fragment1 extends Fragment implements OnClickedListener{
private OnClickedListener clickedInterface;
public Fragment1() {
// Required empty public constructor
}
#Override
public void buttonClicked(View v) {
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
this.clickedInterface = (OnClickedListener)activity;
}}
Fragment 2:
public class Fragment2 extends Fragment implements OnClickedListener{
private OnClickedListener clickedInterface;
public Fragment2() {
// Required empty public constructor
}
#Override
public void buttonClicked(View v) {
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
this.clickedInterface = (OnClickedListener)activity;
}
OR
B) Create individual Interfaces unique to the specific Fragment and implement those in the MainActivity instead of the one Interface like mentioned above. Thank you.
First Create your custom fragment which is in implement interface.
public class CustomFragment extends Fragment implements OnClickedListener{
public OnClickedListener clickedInterface;
#Override
public void buttonClicked(View v) {
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
this.clickedInterface = (OnClickedListener)activity;
}
}
Now, you can add in every fragment
(i) Fragment 1
public class Fragment1 extends CustomFragment {
......
}
(ii) Fragment 2
public class Fragment2 extends CustomFragment {
......
}
I wouldn't let the details of any Views contained in a Fragment leak out into the Activity.
Better interfaces would be based on the semantic action involved with the button press, for example createThing() or deleteThing().
The Activity shouldn't really care which View was clicked to initiate the action just that the action needs to happen. When happens to your interfaces if you move the button to the menu, or somewhere else not associated with a View.
If you create interfaces like this, the question of creating copies for different Fragments disappears.
There is a code principal called SOLID. "I" states for https://en.wikipedia.org/wiki/Interface_segregation_principle. It is a good practice to
You should not make universal interface or large ones, you need to create interface that can be "readable" and "understandable" for everyone by interface name and its method names like "articleSelected" or "loginProcessing(String loginName)" etc
Please please don't minus my question i confused when googling.
I used Android Tab Layout with Swipeable Views in my code for when user pressed setting button on an activity.
now I need send message from TopRatedFragment.java that extends from fragment to the activity that call the mainActivity of "Android Tab Layout with Swipeable Views".
You can do this by implementing a call back
create an interface first
public interface CommunicationInterface {
public void onSuccess();
public void onFailed();
}
then in your activity implement the interface
public class YourActivity extends ActionBarActivity implements CommunicationInterface {
//default functions
#Override
public void onSuccess() {
//stuff you want to do in the acivity
}
#Override
public void onFailed() {
//stuff you want to do in the acivity
}
}
Now in the fragment
public class yourfragment extends Fragment {
CommunicationInterface callback;
//stuffs that usually come in yor fragment and like OncreateView etc
#Override
public void onActivityCreated(#Nullable Bundle outState) {
super.onActivityCreated(outState);
//after all the stuff you want to do in your fragment then implement //call back function to communicate with the activity
callback= (CommunicationInterface) getActivity();
callback.onSuccess();//according to your purpose use where ever you like
callback.onFailed();//according to your purpose use where ever you like
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
callback= (CommunicationInterface) activity;
}
}
Take a close look on this reference:
Creating event callbacks to the activity
The android docs recommend using this pattern of having the parent activity implement an interface of the fragment (Basically calling methods on it)
class MyFragment extends Fragment {
interface Listener {
public void onSomeEvent();
}
private void somethingHappeninInTheFragment() {
// let the activity know
((Listener) getActivity()).onSomeEVent();
}
}
class MyActivity extends Activity implements MyFragment.Listener {
// etc
#Override
public void onSomeEvent() {
// handle the message from the fragment
}
}
Explained with a more concrete example here: http://developer.android.com/guide/components/fragments.html#EventCallbacks
Here's the solution:
Step 1 : From your fragment.
Intent i = new Intent(getActivity(), YourActivity.class);
i.putExtra("key", "Your value1");
i.putExtra("key2", "Your value2");
i.putExtra("key3", "Your value3");
getActivity().startActivity(i);
Step 2 : In your Activity where you want the result
Intent getResults = getIntent();
String firstValue = getResults.getStringExtra("key1");
String secondValue = getResults.getStringExtra("key2");
String thirdValue = getResults.getStringExtra("key3");
Use those values your needs are.
Hope this helps.. :)
For Fragment-Activity communication, this is the suggested way of doing it, by using a listener.
In my case I have two fragments and a button at each and I would like them to do the exact same thing when pressed.
Should I create a separate listener class that the Activity implements and then instantiate a listener in each fragment or there is a better design that I am not aware of?
EDIT
I am sorry, I probably didn't communicate that properly. I am not looking for communication between fragments. I have a Fragment A with a buttonA and a Fragment B with a buttonB. When I click on buttonA, there is a listener in my Activity and method doSomething() is called. Now I want buttonB calling doSomething() too. Should I A) create a second listener and have the activity implement that too, B) create one separate listener class and use this one for both or C) a better choice ??
For communication between fragment to frament or activity to fragment communication via events. There are few alternatives are there e.g. this otto eventbus I know. and the tutorial about this can be found Here or just google it.
As from the documentation :
Two Fragments should never communicate directly.
So you best follow the pattern explained in the article and communicate thru the activity on which the fragments are attached.
When a listener is called from fragment A then get the fragment B from the fragmentManger
YourFragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_b);
fragment.doSomething();
I'm using this pattern and it works well for me:
public class Fragment1 extends Fragment {
FragmentListener mCallback;
public interface FragmentListener {
public void onAction1();
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (activity instanceof FragmentListener) {
mCallback = (FragmentListener) activity;
}
}
public void onAction2() {
// do your stuff...
}
}
public class Fragment2 extends Fragment {
FragmentListener mCallback;
public interface FragmentListener {
public void onAction2();
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (activity instanceof FragmentListener) {
mCallback = (FragmentListener) activity;
}
}
public void onAction1() {
// do your stuff...
}
}
public class MainActivity implements Fragment1.FragmentListener, Fragment2.FragmentListener {
private Fragment1 fragment1;
private Fragment2 fragment2;
/**
* Listening to events from first fragment and forwarding to second fragment
*/
#Override
public void onAction1() {
fragment2.onAction1();
}
/**
* Listening to events from second fragment and forwarding to first fragment
*/
#Override
public void onAction2() {
fragment1.onAction2();
}
}
The Activity listens to "events" from the fragments and if needed forward it to the other fragment(s).