What I am basicaly trying is to access a variable inside a Fragment and get rid of it in my activity.
It worked to get the variable of the activity in my fragment but not the other way around:
what I did:
// get method of MainActivity
final MainActivity activity = (MainActivity) getActivity();
is it even possible to make this "the other way around"?
(Access variable of Fragment in my Activity)?
You need to implement listeners.
You can read more about here:Communicating with Other Fragments
Here is a code example how to pass data (or null) from Activity to a Fragment:
public class FragmentA extends Fragment implements FragmentCommunicator{
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
((MainActivity)getActivity()).fragmentCommunicator = this;
}
#Override
public void passDataToFragment(String str) {
//str is the string variable you pass from the Activity, it can be null...
}
}}
Next the FragmentCommunicator Class:
public interface FragmentCommunicator{
public void passDataToFragment(String str);}
And the Activity:
public class MainActivity extends FragmentActivity{
public FragmentCommunicator fragmentCommunicator;
public void someMethod(String someString) {
fragmentCommunicator.passDataToFragment(someString);
}}
When you call passDataToFragment() from the Activity it will pass the string (or any other variable) to the fragment method passDataToFragment().
You can get your fragment by id/tag from fragment manager and do whatever you want with them.
that's super easy.
if you are adding your fragments in runtime (using FragmentManager)
you are creating objects of that fragment...just keep the reference with you and you are good to call any function or access any variable of the fragment.
e.g. you fragment is MyFragment
MyFragment mf = new MyFragment();
getSupportFragmentManger().beginTrans......you know the code to add a fragment
then simply call any method...for say... change() by mf.change();
or you can do something like
MyFragment mf = (MyFragment) findFragmentById(R.id.container);
and then again mf.change();
Related
I am building an app with 2 fragments. The 1st fragment has an ImageView and 2 TextViews and the 2nd fragment has a set of buttons and EditTexts. In the 2nd fragment, I have a button called "Save". When this button is clicked, I want to download the image inside my 1st fragment to my device folder (The ImageView, the URI, the bitmap and canvas objects are all in the 1st fragment).
Because fragments can't communicate with one another directly, I was thinking of doing this with an interface. What I did was:
I've declared my interface in the 2nd fragment
Applied the logic of the interface's method in the MainActivity (which is the shared activity between the 2 fragments)
Fed the necessary parameters for the method in the 1st fragment.
Didn't work
But I don't think that this was the correct order so it's no surprise that it didn't work. How do I apply the interface in a way that a button click in the 2nd fragment downloads the image in the 1st fragment?
You could try one of these three options:
1.) Using callbacks to communicate via the activity
As shown in this article, you can define an interface in fragment 2 which is then called when the button is clicked. Your activity (which holds fragment 2) provides an implementation for that interface in which the activity calls a method in fragment 1 for downloading the image. For example:
Fragment 1 providing the download method
public class Fragment1 extends Fragment {
OnButtonClickedListener mCallback;
public void startImageDownload() {
// Download the image
}
// ...
}
Fragment 2 defining and calling the interface
public class Fragment2 extends Fragment {
OnButtonClickedListener mCallback;
// Some kind of init method called by onCreate etc.
private void init() {
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Call the listener if present
if(mCallback != null) {
mCallback.onButtonClicked();
}
}
});
}
public void setOnButtonClickedListener(Activity activity) {
mCallback = activity;
}
// Container Activity must implement this interface
public interface OnButtonClickedListener {
public void onButtonClicked();
}
// ...
}
The Activity reacting on the Button click and calling the download method
public static class MainActivity extends Activity implements Fragment2.OnButtonClickedListener {
Fragment1 mFragment1;
#Override
public void onAttachFragment(Fragment fragment) {
if (fragment instanceof Fragment2) {
// Register the listener
Fragment2 fragment2 = (Fragment2) fragment;
fragment2.setOnButtonClickedListener(this);
} else if (fragment instanceof Fragment1) {
// Keep a reference to fragment 1 for calling the "download" method
mFragment1 = (Fragment1) fragment;
}
}
#Override
public void onButtonClicked() {
// Handle the button click
mFragment1.startImageDownload();
}
}
This way you avoid linking the fragments together, instead you have the activity beeing a loose "connection" between fragment 1 and fragment 2.
This is just an exmple, i did not had time to test it. But i hope it helps.
2.) Using a local broadcast
I would recommend using the LocalBroadcastManager for sending a broadcast in fragment 1 (that the button was clicked) and receiving it in fragment 2 (downloading the image). Here is a great article about local broadcasts.
3.) Another option is to use ViewModel
The ViewModel was recently introduced by the new Android Jetpack and "is designed to store and manage UI-related data in a lifecycle conscious way. The ViewModel class allows data to survive configuration changes such as screen rotations." (from ViewModel Overview).
It can also be used to share data between two fragments: Your activity basically holds the ViewModel and the fragments (inside that activity) can get access to it by calling: ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
I think your scenario you could use Observers or some kind of LiveData to react to the button-click via a ViewModel.
Thanks to #Elletlar for helping me improve my answer.
My app has a bottomNavigationBar with three fragments. My MainActivity contains a CollapsingToolbar with a RadioGroup inside. Now I get the value of the selected RadioButton in the MainActivity, but I need the value to work with it inside my first fragment. How do I do that? Does every fragment contains its own CollapsingToolbar or is the data passed between the activities?
You can use the fragment's setArguments(Bundle) method where the Bundle has key-value pairs that you've set. For example, your fragment object is yourFragment then you have
Bundle bundle = new Bundle();
bundle.putString("paramKey", "paramVal");
yourFragment.setArguments(bundle);
In the fragment's onCreateView(LayoutInflater, ViewGroup, Bundle) you can access the information with the getArguments() method.
String value = getArguments().getString("paramKey"); // value = "paramVal"
// inflate, return
Have a look at the documentation for more information on setting bundle values.
You can use put the values you want to pass in SharedPreferences
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
SharedPreferences.Editor edit = prefs.edit();
edit.putString("some_key",someValue); //someValue is a var that containns the value that you want to pass
edit.commit();
Then in your fragment, access the value:
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
String value = prefs.getString("some_key","default_value");
Another rather less efficient way
Create a Utility class that will hold all your static variables. You will be able to set and get the values of these variables in all instances of that class
This can also be achieved using Java interfaces like this
Have an Interface defined in your Activity class. Capture the interface instance while committing your fragment which will later be used to send data to fragment
.
class ExampleActivity extends Activity {
//Data listener to be implemented by the fragment class
public interface OnDataListerner{
public void sendData(ArrayList<String> data);
}
//DataListener instance to be captured while committing fragment
OnDataListener fragment;
//commit your fragment and type cast it to OnDataListener
private void commit Fragment(){
fragment = new ExampleFragment();
FragmentTransaction transaction =
getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, exampleFragment);
transaction.addToBackStack(null);
transaction.commit();
}
//used to send data through interface methods which will be defined in fragment
public void sendDataToFragment(){
fragment.sendData(Your data to be send);
}
}
Have this interface implemented in your Fragment class and once Acitivity calls any method on this interface it will be called in this Fragment
public class ExampleFragment extends Fragment implements ExampleActivity.OnDataListerner {
//interface callback which is called when Activity call its method.
public void sendData(ArrayList<String> data){
//Here is your data which can be consumed
}
}
Hope this helps.
You can create a method in the fragments visible to the activity class. Then using this method, you can pass the values. And in implementation of this function, you can do required changes such as UI updates in the fragment.
For example -
MainActivity.java
// in member declarations
private MyFragment frag;
private CentralFragment cFrag;
// initialize the fragment
frag = new MyFragment(args);
cFrag = new CentralFragment(otherArgs);
// onRadioButtonClicked -> assuming selected value = v
frag.onChoice(v);
cFrag.onChoice(v);
MyFragment.java
public void onChoice(Type arg) {
// use the value
someTextView.setText(arg);
}
CentralFragment.java
public void onChoice(Type arg) {
// use the value
otherTextView.set(arg);
}
In the following link https://github.com/iPaulPro/Android-ItemTouchHelper-Demo/blob/master/app/src/main/java/co/paulburke/android/itemtouchhelperdemo/MainFragment.java , Activity object is casted to an inner interface named OnListItemClickListener.
mItemClickListener.onListItemClick(position);
The whole source code of the MainFragment class is as the following :
public class MainFragment extends ListFragment {
public interface OnListItemClickListener {
void onListItemClick(int position);
}
private OnListItemClickListener mItemClickListener;
public MainFragment() {
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mItemClickListener = (OnListItemClickListener) activity;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
final String[] items = getResources().getStringArray(R.array.main_items);
final ArrayAdapter<String> adapter = new ArrayAdapter<>(getActivity(),
android.R.layout.simple_list_item_1, items);
setListAdapter(adapter);
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
mItemClickListener.onListItemClick(position);
}
}
Why does activity object casted to an interface?
There is similar question at stackoverflow How is it possible to cast an Android Activity to an interface? but it is not satisfactory for me, it does not reply my questions.
To expand on #CommonsWare answer on why you have to cast it into OnListItemClickListener. The onAttach lifecycle method of the Fragment accepts any Activity - and an Activity does not have MainFragment.OnListItemClickListener implemented as default.
So the compiler does not know that, but you know that MainActivity has implemented it and with the cast you tell the compiler that MainActivity implements MainFragment.OnListItemClickListener.
This interface is not implemented in anywhere in the codes or i cannot see it
See this line:
public class MainActivity extends ActionBarActivity implements MainFragment.OnListItemClickListener {
but i cannot see any other implementation of this method..
See these lines:
#Override
public void onListItemClick(int position) {
Fragment fragment = null;
switch (position) {
case 0:
fragment = new RecyclerListFragment();
break;
case 1:
fragment = new RecyclerGridFragment();
break;
}
getSupportFragmentManager().beginTransaction()
.replace(R.id.content, fragment)
.addToBackStack(null)
.commit();
}
Why does activity object casted to an interface?
This is a typical implementation of the contract pattern. The fragment requires that its hosting activity implement a certain interface. That way, the fragment does not care what specific activity class is the one that hosts it.
You have to make your activity implements MainFragment.OnListItemClickListener.
Besides, in your Activity, you will ned to implement the function void onListItemClick(int position).
I have One Activity and Three Fragments. The application launches and Fragment 1 is visible. I click on a button. The Fragment communicates with the Activity through the following Interface and launches Fragment 2:
public OnClickedListener listener;
static interface OnClickedListener{
public void buttonClicked(View v);
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
Activity a;
if (context instanceof Activity){
a=(Activity) context;
this.listener = (OnClickedListener)a;
}
}
...
playBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
listener.buttonClicked(v);
}
});
My MainActivity implements Fragment1.OnClickedListener, receives the data and launches Fragment 2.
Now I'm in Fragment 2. I want to click on a button and launch Fragment 3. I know that I can do that by implementing YET ANOTHER interface in Fragment 2 to then communicate to the Activity and say "Hey Launch Fragment 3". So now My Activity looks like this
MainActivity implements Fragment1.OnClickedListener,Fragment2.OnClickedListener
That's all fine but let's say that I have 20 Fragments. I don't want to have 20 interfaces implemented in my Main Activity. Is there a way to create and use a single interface to communicate between each individual Fragment and the Activity. How would that be implemented? Thank you.
You can. Create only an interface (I recommend you to create it in a separate file):
interface OnClickedListener {
void buttonClicked(Fragment fragment, View v);
}
The onButtonClicked() method accepts also a Fragment instance when a Button is clicked:
playBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// You can't use just "this", pass the class instance.
listener.buttonClicked(MyFragmentClassName.this, v);
}
});
In your Activity you will have so:
public class MainActivity extends Activity implements MainActivity.OnClickedListener {
#Override
public void buttonClicked(Fragment fragment, View v) {
// Check if the Fragment instance, or tag, or the info that you use to recognize it.
}
}
Create one nested interface in Activity or in separate file instead of nested interfaces in Fragments.
I have a fragment as follows:
public class F1 extends Fragment {
...
public void function1 (){
activity = getActivity();
}
}
When trying to call this function in the activity which has this fragment,
public class Activity1 extends Activity {
private F1 f1 = new F1();
f1.function1();
}
it returns null.
How can I solve this problem? Can anyone provide a working code example?
It returns null because the fragment didn't attach to the Activity. so use Fragment Manager to access Fragments and also you can use Activity Context in onAttach(Activity activity) , and it would be for sure not Null.
#Override
public void onAttach(Activity activity)
{
super.onAttach(activity);
this.activityContext = activity; //then use activity context
}
and for getting your Fragment Instance :
F1 f1 = (F1) getFragmentManager().findFragmentByTag(F1.TAG_FRAGMENT);