best way to use context in fragment - android

i m using fragments in my application. i created one Parent Class called BaseFragment and all other fragment extends this Basefrgment below is the snippet of this Basefragment
BaseFragment.java
public class BaseFragment extends Fragment {
public MainActivity activity;
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (activity == null && context instanceof MainActivity) {
activity = (MainActivity) context;
}
}
}
public void replaceFragment(Fragment fragment, FragmentDetail last) {
fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
boolean push = true;
if (Validator.isNotNull(last)) {
push = false;
}
/*if(Validator.isNull(last)){
transaction.setCustomAnimations(R.anim.enter_from_left, R.anim.exit_to_right);
}else{
transaction.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left);
}*/
transaction.add(R.id.frame_container, fragment, fragment.getClass().getName());
if (Validator.isNull(last) && preferences.getFragmentStack().size() > 0) {
last = preferences.getFragmentStack().lastElement();
}
if (Validator.isNotNull(last)) {
Fragment f = fragmentManager.findFragmentByTag(last.className);
if (Validator.isNotNull(f)) {
f.onPause();
transaction.remove(f);
}
}
last = new FragmentDetail(fragment.getClass().getName(), getTitle().toString(), preferences.isBack());
if (preferences.isBack() || preferences.getFragmentStack().size() == 0) {
if (push) {
preferences.getFragmentStack().push(last);
}
} else {
while (preferences.getFragmentStack().size() > 1) {
preferences.getFragmentStack().pop();
}
if (!preferences.getFragmentStack().lastElement().className.equals(last.className)) {
preferences.getFragmentStack().push(last);
}
}
transaction.commitAllowingStateLoss();
changeNavigationIcon();
// HWUtil.showToast(this, fragmentManager.getBackStackEntryCount() + "");
}
and in all other fragment i m using activity as a context,my question is whether it is bad way to access context in this way or whether it creates memory leak.or any other approach for accessing context..any help is appriciated.

I think the way you are storing the context is really optimal as with that you will be able to use it within each of your sub fragment instances. Because MainActivity is an instance variable in your fragment, it will be garbage collected when your fragment gets destroyed. And if I'm not mistaken about Activity-Fragment lifecycle, when your activity gets rotated, new fragments will be created and the older fragment instances will get destroyed. So, we're good there too. However, you need to be careful with your context variable declaration:
public MainActivity activity;
This makes it accessible from anywhere. Any class can call something like context = fragIns.activity and save it there. This will be really bad for you because now it holds a reference to that context variable. Now, when your fragment is no longer needed it will not be garbage collected because some other class is holding a reference to one of its variable. You'd find yourself in "memory leak town".
Make sure you hold this variable dearly and its reference is not passed to other classes. Since, its in a super class, you may define it as:
protected MainActivity activity;
This should do the job.

the best way is to use getActivity() function inside fragment to access context,
because it will return the instance of the activity on which fragment is attached.

using getActivity() is easy and quick way to get parent activity's context but problem comes when that fragment is detached.
So using it like this inside fragment , imo, will suffice the need of doing it better way....
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mContext=activity;
}

To avoid memory problems it's recommended that whenever you use onAttach(Context context) you should use onDetach() as well:
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (activity == null && context instanceof MainActivity) {
activity = (MainActivity) context;
}
}
#Override
public void onDetach() {
this.activity = null;
super.onDetach();
}

My way:
public class AppManager extends Application {
private static AppManager mApp;
#Override
public void onCreate() {
super.onCreate();
mApp = this;
}
public static Context getContext() {
return mApp.getApplicationContext();
}
}
so, anyone wants to get a Context, just using the AppManager.getContext(), except starting a Activity. It is very simple.
In your way, if the activity restarted, the fragment will automatically create again. In you dont' handle the action of Activty restarted, it is posssible that the Activity have two same Fragment, and one who automatically creating didn't call OnAttch(), will causing NullPointerException.
My solution:
public abstract class BaseTabActivity extends BaseActivity {
#CallSuper
protected void initTabs(boolean isRestarted) {
if (isRestarted) {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
if (manager.getFragments() == null)
return;
Stream.of(manager.getFragments())
.forEach((fragment) -> {
if (fragment != null)
transaction.remove(fragment);
});
transaction.commit();
manager.executePendingTransactions();
}
}
public FragmentTransaction getSlideAnimTransaction() {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.setCustomAnimations(R.anim.slide_from_right, R.anim.slide_out_left);
return transaction;
}
}

Related

How to Update Current Fragment in ViewPager using Interfaces with ClassCastException?

I am trying to refresh a currently selected fragment inside of a ViewPager. I am using one MainActivity as a way to communicate between multiple fragments via interface calls. The fragment I am working with is implementing an interface call to the ViewPagerAdapter, however, when attaching the listener to the fragment I receive a ClassCastException error "Activity cannot be cast to Fragment". This is the code below:
#Override
public void onAttach(Context context) {
super.onAttach(context);
mListener = (UpdateableFragment) context; **//ERROR HERE**
}
I am trying to follow the answer posted here https://stackoverflow.com/a/17855730, however i cannot figure out where the interface call should be handled. Currently i have it implemented in the ViewPagerAdapter, so when i need to update i can just notifyDataSetChange(), but it returns the ClassCastException error.
I have tried to handle the interface call in the MainActivity i am using to communicate with the rest of my fragments and this eliminates the ClassCastException error, however, i have no way to call the ViewPagerAdapter and notifyDatasetChange. Below is my code:
CurrentFragment (in ViewPager)
private UpdateableFragment mListener;
#Override
public void onAttach(Context context) {
super.onAttach(context);
mListener = (UpdateableFragment) context; **//ERROR HERE**
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
public void click(){
mListener.update();
}
public interface UpdateableFragment {
void update();
}
ViewPagerAdapter
public class ViewPagerAdapter extends FragmentPagerAdapter implements CurrentFragment.UpdateableFragment{
public ViewPagerAdapter(FragmentManager fm) {
super(fm);}
//Note: unnecessary code left out.
public void update() {
notifyDataSetChanged();
}
#Override
public int getItemPosition(Object object) {
if (object instanceof CurrentFragment.UpdateableFragment) {
((CurrentFragment.UpdateableFragment) object).update();
}
return super.getItemPosition(object);
}
Any feedback would be appreciated. Thanks.
In onAttach(Context context) , the context is means of Activity, so cast context to your interface will throw the cast exception, if you want to communication with each fragments, i think you can try Event Bus library.
Try to use onAttachFragment() instead of onAttach():
#Override
public void onAttachFragment(Fragment childFragment) {
confirmListener = (ConfirmListener) childFragment;
}

How does the next code for passing a string from fragment_1 to fragment_2 works?

i've been development a android project, i had the necesity of pass information from one fragment to another and i found this code, but i've been having problems understanding how it works.
The next code pass information from fragment_1 to fragment_2 using interfaces,
I not particularly understand the part when the object mCallbacks gets the Activity. (mCallbacks = (Callbacks)getActivity();)
And how the object mCallbacks that Fragment_1 has, calls the interface on MainActivity.
Note: im going to put all the code, maybe it helps someone.
private Callbacks mCallbacks = null;
public class frament_1 extends Fragment{
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mCallbacks = (Callbacks)getActivity();//****************************
}
Public interface Callbacks{
public void callMethodOnMainActivity(String folio);
}
Public void passInformationToFragment2(){
mCallbacks.callMethodOnMainActivity("This String is passed using interfaces"); //****************************+
}
}
This is the MainActivity that implements the interface that frament_1 has.
public class MainActivity extends Activity implements fragment_1.Callbacks{
#Override
public void callMethodOnMainActivity(String folio) {//*********************
//here you call the fragment_2 and pass the String has parameter.
// no problem wthis part
FragmentManager fragmentManager = getSupportFragmentManager();
Fragment module = fragment_2.newInstance(this, folio);
fragmentManager.beginTransaction().replace(R.id.MainContent, module).commit();
}
}
Fragment_2
public class Fragment_2 extends Fragment{
public static Fragment_2 newInstance (Context c, String folio){
//folio has the String passed
Fragment_2 fragment = new Fragment_2();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, folio);
fragment.setArguments(args);
parent = c;
return fragment;
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
//Here you get the parameter that ARG_PARAM1
String folio = getArguments().getString(ARG_PARAM1);
}
mCallbacks = (Callbacks) this.getActivity();
}
The first step in understanding how a block of code works is to read it, and in order to read it you must adhere to the standards of the language; some of the things your code is missing is the following:
Indentation
Class names should not use underscores(_)
Class names are typically not in plural (Callback not Callbacks)
Class names must be capitalized (Fragment1 not fragment1)
Fields must be defined inside the class (mCallback)
Interfaces must not specify the member access qualifier (i.e. no private, public, protected declarations)
Let's see how your code will look now:
public class Fragment1 extends Fragment {
private MyCallback mCallback = null;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mCallback = (MyCallback) getActivity();
}
public interface MyCallback{
void callMethodOnMainActivity(String folio);
}
public void passInformationToFragment2(){
mCallback.callMethodOnMainActivity("This String is passed using interfaces");
}
}
public class Fragment2 extends Fragment {
public static final String ARG_PARAM_FOLIO = "param_folio";
public static Fragment2 newInstance(String folio) {
Fragment2 fragment = new Fragment2();
Bundle args = new Bundle()
args.putString(ARG_PARAM_FOLIO, folio);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
String folio = getArguments().getString(ARG_PARAM_FOLIO);
}
}
}
Important notes:
There is no need to store the context in a parent field; you can always access the parent context by using the getActivity() method.
I'm removing the mCallback in Fragment2 because if you want to communicate back to the MainActivity you must specify its own interface in Fragment2 (i.e. each fragment will have its own interface to communicate back to the activity).
The best practice is to initialize the mCallback in the onAttach(Context context) method by casting the context to a callback type, and setting the callback to null on onDetach() in the following way:
#Override
public void onAttach(Context context) {
super(context);
try {
mCallback = (MyCallback) context;
} catch (ClassCastException e) {
throw new IllegalArgumentException(this.getClassName() + " must implement MyCallback";
}
}
#Override
public void onDetach() {
super();
mCallback = null;
}
Now, let's take a look at the MainActivity code and see what's actually doing:
public class MainActivity extends Activity implements Fragment1.MyCallback {
#Override
public void callMethodOnMainActivity(String folio) {
FragmentManager fragmentManager = getSupportFragmentManager();
Fragment fragment = Fragment2.newInstance(this, folio);
fragmentManager.beginTransaction().replace(R.id.MainContent, fragment).commit();
}
}
The callMethodOnMainActivity(String folio) is basically replacing the current fragment on screen (Fragment1) with a Fragment2 in which we pass a folio as parameter. So, nothing magic about that; but how is it triggered? Well, the Fragment2 has a field mCallback of type MyCallback that is initialized to be the MainActivity. So, when you call mCallback.callMethodOfMainActivity(String folio) on the Fragment1 you're, in a way, telling the program to execute the method callMethodOfMainActivity defined in the MainActivity.
It might be useful to get more familiar with the Observable/Observer pattern (you can google this or look at the book by the Gang of Four.

call a method from another fragment by view pager

I have two fragment class (#1 and #2) in my fragment activity.The fragment activity is used in view pager.
How can i call a method of fragment#2 in fragment#1?
Please, notice the attached picture.
http://upload7.ir/imgs/2014-09/92109715706282282966.jpg
The preferred way is to let all communication between Fragments go trough their common parent Activity:
public class MyActivity implements WantsACalledListener {
private Fragment fragment2;
#Override
public onWantsACalled() {
fragment2.a();
}
}
And, according to the Android documentation at: http://developer.android.com/guide/components/fragments.html
public static class Fragment1 {
WantsACalledListener mListener;
...
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (WantsACalledListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement WantsACalledListener");
}
}
...
private void someMethod() {
mListener.onWantsACalled();
}
}
So, the Activity can easily keep a reference to all its child Fragments and the child Fragments get the reference to their parent Activity in their onAttach method.

Call an fragment method from a fragment

Current my fragment A opens a new dialog fragment B. Is there any way to call method from a fragment A method from a dialog fragment B?
Regarding to android web site, you shouldn't call a fragment from another fragment, instead you should let activity to handle switching fragments.
To do that,
You can create an interface and implement it in activity,
public interface MyListener{
public void switchFragment(Fragment f);
}
public class MyActivity extends Activity implements MyListener{
#Override
public void switchFragement(Fragment f){
getSupportFragmentManager().beginTransaction
.replace(R.id.container, f)
.commit();
}
}
and in fragment classes,
public class MyFragment extends Fragment{
private MyListener listener;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
listener = (MyListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement MyListener");
}
}
#Override
public void onDetach() {
super.onDetach();
listener = null;
}
}
and simply use
listener.switchFragment(fragment);
You will need to one of 2 things:
Wire a reference to each Fragment in your Activity
Use an Enterprise Service Bus (Otto is an open-source option) to communicate directly between Fragments
There is NO way to talk direct Fragment to Fragment in the standard framework. You must manually create this linkage.

Reset Listener to parent Activity for recreated Fragment

I am using a pattern where the activity is a listener to its fragments. The original way I have been creating attaching these fragments is like this:
MyFragment myFragment = new MyFragment(param1, param2)
myFragment.setListener(this);
getSupportFragmentManager().beginTransaction().replace(R.id.fragmentContainer, myFragment).commit()
But I have learned that this is wrong and that I should be creating the fragment with a static newInstance() method, in case the fragment is destroyed then it can recreate from a bundle. This will let me retain the parameters, but my question is how can I set the listener of a fragment to be its parent activity, after is has been destroyed then recreated?
Is it as simple as calling:
setListener((MyActivity) getActivity());
within the fragment? This seems wrong to me because it strongly couples the fragment to its parent activity.
This is definitely the wrong route to go. You should have your Activity implement the Fragment's interface (it looks as though you already are, but I'll show an example anyway). Then, within the Fragment, cast the Activity to a listener in onAttach() and release it in onDetach(). A quick example would be:
The Fragment
public class ExampleFragment extends Fragment {
private MyFragmentInterface mInterface;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (activity instanceof MyFragmentInterface) {
mInterface = (MyFragmentInterface) activity;
} else {
throw new IllegalArgumentException("Parent Activity must implement MyFragmentInterface.");
}
}
#Override
public void onDetach() {
super.onDetach();
mInterface = null;
}
private void fireOnSomethingHappened () {
if (mInterface != null) {
mInterface.onSomethingHappened();
}
}
public static interface MyFragmentInterface {
void onSomethingHappened();
}
}
The Activity
public class ExampleActivity extends Activity implements ExampleFragment.MyFragmentInterface {
#Override
public void onSomethingHappened() {
//Handle the event
}
}
newInstance() method should return a fragment instance so you can do something like this:
MyFragment myFragment = MyFragment.newInstance(param1, param2)
myFragment.setListener(this);
getSupportFragmentManager().beginTransaction().replace(R.id.fragmentContainer, myFragment).commit();

Categories

Resources