how to access button of fragment A from fragment B - android

I have two Fragments in my Activity: fragment A with button X and fragment B with button Y.
How can I change button X's background image when I click button B? Is it possible?

From the documentation,
Because each fragment defines its own layout and its own behavior with its own lifecycle callbacks, you can include one fragment in multiple activities, so you should design for reuse and avoid directly manipulating one fragment from another fragment.
That being said, what you want to do is create event callbacks to the activity. A good way to do that is to define a callback interface inside the fragment and require that the host activity implement it. When the activity receives a callback through the interface, it can share the information with other fragments in the layout as necessary. This is the recommended way to share events between two separate Fragments--that is, sharing the event through the activity.
Check out the link above... it provides a couple nice examples. If you are still having trouble, let me know and maybe I can be more explicit.
Edit #1:
Let's say you click a button in fragment A and you want this to cause changes to a button in fragment B. Here's some sample code illustrating the concept:
The callback interface:
public interface OnButtonClickedListener {
public void onButtonClicked();
}
The activity:
public class SampleActivity extends Activity implements OnButtonClickedListener {
/* Implementation goes here */
public void onButtonClicked() {
// This method is called from fragment A, and when it is called,
// it will send information to fragment B. Remember to first
// check to see if fragment B is non-null.
/* Make call to a method in fragment B that will update its display */
}
}
Fragment A:
public class FragmentA extends Fragment {
OnButtonClickedListener mListener;
/* Implementation goes here */
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (OnButtonClickedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnButtonClickedListener ");
}
}
public void clickButton() {
// When the button is clicked, notify the activity.
// The activity will then pass the information to fragment
// B (if it has been created).
mListener.onButtonClicked();
}
}
Edit #2:
Now, you might be wondering, "Why would anyone ever go through all of this trouble? What's the point of creating a separate activity callback method when you could just have fragment A directly manipulate fragment B?"
The main reason you want to do this is to ensure that each fragment is designed as a modular and reusable activity component. This is especially important because a modular fragment allows you to change your fragment combinations for different screen sizes. When designing your application to support both tablets and handsets, you can reuse your fragments in different layout configurations to optimize the user experience based on the available screen space. For example, on a handset, it might be necessary to separate fragments to provide a single-pane UI when more than one cannot fit within the same activity. Making use of activity callbacks ensures that you will easily be able to reuse your fragments in situations where fragment B is not visible on the screen. For example, if you are on a handheld device and there is not enough room to display fragment B, then you can easily have your activity check to see if fragment B is currently being shown on the screen.
Sorry if this isn't clear... I'm finding it difficult to describe :P. Working your way through this tutorial might help... Activity callbacks make your life especially easier as a developer when you are working with interactive multi-pane layouts.

Base on Alex Lockwood's answer:
The activity:
public class SampleActivity extends Activity{
public interface OnButtonClickedListener {
public void onButtonClicked();
}
private OnButtonClickedListener onButtonClickedListener = null;
public OnButtonClickedListener getOnButtonClickedListener () {
return onButtonClickedListener
}
public void setOnButtonClickedListener (
OnButtonClickedListener onButtonClickedListener {
this.onButtonClickedListener = onButtonClickedListener;
}
}
Fragment A:
public class FragmentA extends Fragment {
private OnButtonClickedListener onButtonClickedListener = null;
private OnClickListener actionBarClickListener = new OnClickListener() {
#Override
public void onClick(View view) {
if (onButtonClickedListener == null){
onButtonClickedListener = ((SampleActivity) getActivity()).onButtonClickedListener ();
}
if (onButtonClickedListener != null) {
onButtonClickedListener
.onButtonClicked();
}
}
};
}
Fragment B:
public class FragmentB extends Fragment {
private OnButtonClickedListener onButtonClickedListener = new OnButtonClickedListener() {
#Override
public void onButtonClicked() {
Toast.makeText(getActivity(), "Button clicked", Toast.LENGTH_SHORT).show();
}
};
#Override
public void onResume() {
super.onResume();
SampleActivity sampleActivity = (SampleActivity) getActivity();
sampleActivity.setSearchBoxTextChangedListener(onButtonClickedListener);
}
}
Hope can help someone.

Setting the onClick attribute for a button in your layout, even your fragment's layout, will call the appropriate method on your Activity.
Your app can then send this signal from your Activity to fragment B.

Related

How to synchronize two buttons in two seperate fragments

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

One Interface for Multiple Fragments

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

Suggested way for common fragment listener

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

Android managing fragments from activity elegantly

Description of what I'm trying to accomplish:
I have an app that uses a FragmentActivity with a LinearLayout as a container for the fragments. I click different buttons on the FragmentActivity UI and add and remove Fragments to the container in the FragmentActivity. In addition to clicking buttons on the FragmentActivity UI, each Fragment also has buttons that can be clicked which will remove the current fragment and add a different fragment in its place.
The Android way of doing things as I understand it:
I have been reading up on how to do this and as I understand it, the 'proper' way of doing things is to use the FragmentActivity as sort of a relay station and have each fragment do callbacks to the FragmentActivity to communicate events and deal with them.
Scenario:
So let's say that the FragmentActivity is displaying Fragment A and when the user clicks a button in FragmentA I want to stop showing FragmentA and start showing FragmentB. To do this I have created an interface in FragmentA called AListener. In the onAttach() method of FragmentA I use the suggested method of checking that the FragmentActivity implements AListener. When the button in FragmentA is clicked I use one of the callback methods from AListener to communicate the click event to the FragmentActivity. In the FragmentActivity I create an instance of FragmentB and add it to the container in FragmentActivity. Then if some event happens in FragmentB I use the same scheme to communicate the event to the FragmentActivity and do something interesting.
So what's the problem?
For my application I have found this scheme of having Fragments call back to the FragmentActivity and then having the FragmentActivity create a new fragment or call forward to and existing fragment very cumbersome. I have many fragments that need to be displayed by the FragmentActivity and therefore I am implementing an interface for every type of fragment that needs to be displayed (Each fragment is different so they each have their own interface). This causes clashes when I have two interfaces that have the same method signatures and I'm forced to rename one of the methods.
For instance, if I want to attach a listener to a fragment using the onAttach() method of the fragment, then my FragmentActivity must implement the interface. I have found several instances where I have callback methods that have the same name (or I'm forced to name them something similar but different because of a namespace collision). One solution to this would be to use an anonymous classes as callbacks instead of having the FragmentActivity implement the interface. This seems to work well enough, but goes against what the Android documentation says about using the onAttach() method to set the listener.
Are there any elegant ways to approach this problem? It seems to me the tradeoff is that you either force the FragmentActivity to implement an interface for each Fragment that you want to display in it and have the fun problem of watching out for method signature collisions, or you go against the Android documentation and use Anonymous classes to handle the callbacks (not sure of the implications of this).
I am fairly new to Java and feel like I could be missing a concept here that would solve my problem. Can anyone set me straight on how to solve this problem elegantly?
I completely understand your problem since i was dealing it for a long time. Here is the solution i came up right now! It may need some modification based on your need but i it works well.
first of all to to make communicating of event easier in your app use an EventBus! here is the most famous one https://goo.gl/nAEW6
event bus allows you to send event from anywhere to anywhere without need to worry about implementing interfaces, broadcast receivers, threading, etc.
Then add FragmentOrganizer to your app. It's a base class for all of your Fragment Organizers. basically you need one for each activity. Here is the code
public abstract class FragmentOrganizer {
protected FragmentManager fragmentManager;
public FragmentOrganizer(FragmentManager fragmentManager) {
this.fragmentManager = fragmentManager;
openFragment(getInitialFragment());
EventBus.getDefault().register(this);
}
protected abstract Fragment getInitialFragment();
protected abstract void onEvent(Object event);
public abstract boolean handleBackNavigation();
public void freeUpResources(){
EventBus.getDefault().unregister(this);
}
protected Fragment getOpenFragment(){
String tag = fragmentManager.getBackStackEntryAt(fragmentManager.getBackStackEntryCount() -1).getName();
return fragmentManager.findFragmentByTag(tag);
}
protected boolean isFragmentOpen(Fragment fragment){
return isFragmentOpen(fragment, true);
}
protected boolean isFragmentOpen(Fragment fragment, boolean useArgs){
String fragmentTag = createFragmentTag(fragment, useArgs);
if (fragmentManager.getBackStackEntryCount() != 0) {
String name = fragmentManager.getBackStackEntryAt(fragmentManager.getBackStackEntryCount() - 1).getName();
if(!useArgs)
name = name.substring(0, name.indexOf("-"));
return name.equals(fragmentTag);
}
return false;
}
private String createFragmentTag(Fragment fragment, boolean addArgs) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(fragment.getClass().getSimpleName());
if(addArgs) {
stringBuilder.append("-");
if (fragment.getArguments() != null)
stringBuilder.append(fragment.getArguments().toString());
}
return stringBuilder.toString();
}
public void openFragment(Fragment fragment) {
if(isFragmentOpen(fragment))
return;
String fragmentTag = createFragmentTag(fragment, true);
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.activity_main_fragment_container, fragment, fragmentTag);
transaction.addToBackStack(fragmentTag).commit();
}
}
Now you need to create your fragment organizer that inherit from FragmentOrganizer and implements 3 required methods. here the sample
public class MainFragmentOrganizer extends FragmentOrganizer {
public MainFragmentOrganizer(FragmentManager fragmentManager) {
super(fragmentManager);
}
#Override
protected Fragment getInitialFragment() {
return HomeFragment.newInstance();
}
#Override
public void onEvent(Object event){
if(event instanceof ClickedOnPhotoEvent){
String photoCode = ((ClickedOnPhotoEvent) event).photoCode;
openFragment(PhotoFragment.newInstance(photoCode));
}
}
#Override
public boolean handleBackNavigation(){
Fragment fragment = getOpenFragment();
if (fragment instanceof HomeFragment){
return false;
} else {
fragmentManager.popBackStack();
return true;
}
}
}
And in your activity you just need to insatiate your FragmentManager and let it do the magic!
fragmentManager = getSupportFragmentManager();
fragmentOrganizer = new MainFragmentOrganizer(getSupportFragmentManager());
#Override
public void onBackPressed() {
//first let fragment organizer handle back. If it does not activity takes cares of it!
if(!fragmentOrganizer.handleBackNavigation()){
finish();
}
}
#Override
protected void onDestroy() {
fragmentOrganizer.freeUpResources();
super.onDestroy();
}
It may seem a lot of code but as you see most of the code encapsulated in FragmentOrganizer base class and it does all the general works so you just have to copy this file from one project to another.
As i said in the beginning i just came up with this solution right now, so it may not be perfect. I Plan to use this in my next project i hope you do to. And if you do i really appritiate if you share your though. have a good time
A co-worker of mine came up with what I consider an elegant solution to this problem.
Remember, what we're trying to achieve is a way for fragments to callback to the parent activity without having the activity implement the interface. Also, we need to be able to automatically set the listener again if the activity is destroyed and then recreated.
Activities have a lifecycle callback called onAttachFragment(Fragment fragment) which is called whenever a fragment is being attached to the activity. So, for instance, when a new fragment is created within the activity, this gets called. It also gets called if an activity that was previously destroyed gets recreated. What you can do is use an interface or an anonymous class to set a listener on the new fragment in onAttachFragment like this:
#Override
public void onAttachFragment(Fragment fragment) {
super.onAttachFragment(fragment);
//Determine which fragment this is by checking its tag
if(fragment.getTag().contains(TextFrag.FRAG_TAG)){
//set a listener on this fragment using an anonymous class or interface
((TextFrag)fragment).setListener(new TextFragButtonListener() {
#Override
public void onButtonClicked() {
count++;
counterTV.setText(String.valueOf(count));
}
});
}
}
Using this technique we are able to avoid the activity having to implement an interface for the callback and thus we avoid any naming conflicts with our callback methods. Also, if the activity is destroyed, once it is recreated the listener will be automatically reset so our callbacks will still work.
There are probably many other ways to do this and I'd love to here anyone's criticisms of this technique and suggestions for any other techniques.

Quiz android app using ViewPager

I've created an Activity that contains a ViewPager with n number of Fragments. I have also added a Previous, Next and Finish buttons. Fragments are either multiple choice ListViews or just a single answer type.
How should I get the data from each fragment when the user hits Finish? Should I get each answer on the onPageSelected event from the ViewPager?
Communications between a Fragment and the activity that contains it is done via Interfaces.
The fragment should expose an interface listener and the activity should implement it and get notified by the fragment once an event happens.
You can use my example, and you can also change so the activity doesn't implement, but you can create the listener in run time anonymously (like you do for buttons on click listener many times).
class MyFragment {
// Container Activity must implement this interface
public interface OnNextPageListener {
public void onNextPageSelected(String DataString);
}
// define listener in fragment
OnNextPageListener mNextPageListener;
.... class code
#Override
public void onAttach(Activity activity) {
try {
mNextPageListener = (OnNextPageListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnNextPageListener");
}
.... class code...
...button press onclick listener method...
mNextPageListener.onNextPageSelected("My Name is Slim Shaddy");
}
class MainActivity extends Activity implements MyFragment.OnNextPageListener
public void onNextPageSelected(String StringData) {
}
...
read more here http://developer.android.com/training/basics/fragments/communicating.html
Either way I suggest you do this when someone pressed the button in the fragment, or hooking the ViewPager.OnPageChangeListener, starting the saving the data on onPageScrollStateChanged and completing it on the OnPageSelected so you ensure you have the data even if the fragment is not available anymore.

Categories

Resources