So i have this code in android (interface method implementation in the activity) The app runs perfectly well.
public void Clicks(int clickCounter) {
FragmentManager fragmentManager = getFragmentManager();
AnotherFragment another_fragment = (AnotherFragment) fragmentManager.findFragmentById(R.id.another_fragment);
another_fragment.showClicks(clickCounter);
}
Now, when i try to declare fragmentManager and another_fragment as class variables like so:
public class MainActivity extends AppCompatActivity implements FragmentInterface{
FragmentManager fragmentManager;
AnotherFragment anotherFragment;
fragmentManager = getFragmentManager();
anotherFragment = (AnotherFragment) fragmentManager.findFragmentById(R.id.another_fragment);
...
}
it causes the app to crash. Why is it acting this way?
fragmentManager = getFragmentManager();
anotherFragment = (AnotherFragment) fragmentManager.findFragmentById(R.id.another_fragment);
is not in any method block. You are trying to fetch those values before Activity is ready. Move this inside a method which is called after loading of AnotherFragment.
I bet you get a NullPointerException, right?
That's because you run your code before your Activity "is ready".
Your Activtiy is ready in onCreate(), for example. So put our code there.
public class MainActivity extends AppCompatActivity implements FragmentInterface {
FragmentManager fragmentManager;
AnotherFragment anotherFragment;
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.myLayout);
fragmentManager = getFragmentManager();
anotherFragment = (AnotherFragment) fragmentManager.findFragmentById(R.id.another_fragment);
}
}
You can find out more about the Activity Lifecycle here.
Related
The android documentation says that The onAttach() callback is invoked when the fragment has been added to a FragmentManager and is attached to its host activity.
A piece of my code in the activity:
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentById(R.id.fragment_container);
if(fragment == null){
fragment = new CrimeFragment();
fm.beginTransaction().add(R.id.fragment_container, fragment).commit();
}
Log.d(MAIN_ACTIVITY_LOG_TAG, "onCreate(Bundle)");
}
...
A piece of my code in the fragment:
...
#Override
public void onAttach(Context context) {
super.onAttach(context);
Log.d(CRIME_FRAGMENT_LOG_TAG, "onAttach(Context)");
}
...
The log:
...
2022-08-03 12:36:42.512 26035-26035/com.bignerdranch.android.criminalintent D/main_activity_log_tag: onCreate(Bundle)
2022-08-03 12:36:42.514 26035-26035/com.bignerdranch.android.criminalintent D/crime_fragment_log_tag: onAttach(Context)
...
As you can see onAttach() isn't invoked when the fragment is added to the FragmentManager.
So when actually is onAttach() invoked?
Fragment transactions are asynchronous by default. They are not executed immediately but just scheduled for execution at a later point.
If you want to make your fragment transaction synchronous, change commit() to commitNow(). Most of the cases you don't really need that.
How do you update a fragment from onPostExecute(), My onPostExcute is not in an activity class?
I've inlcluded my onPostExcute method below. getSupportFragmentManager() has a can't resolve error, I've also tried getFragmentManger() which I've seen as a possible solution, this also shows a can't resolve error.
#Override
protected void onPostExecute(String result) {
ExtractRecipeData data = new ExtractRecipeData();
data.recipeName(result);
RecipeListFragment mRecipeFragment = new RecipeListFragment();
FragmentManager mFragmentManager = getSupportFragmentManager();
FragmentTransaction mFragmentTransaction = mFragmentManager.beginTransaction();
mFragmentTransaction
.add(R.id.activity_main_container, mRecipeFragment)
.commit();
}
You have to add a FragmentManager to the Constructor of your AsyncTask Class. Like:
class MyTask(FragmentManager fm) extends AsyncTask { ... }
then you can use it
I read Android documentation on how to add a Fragment to an activty. It says that to add a Fragment to an Activty I should write this code inside the Activity class:
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();
but this is for android.app.Fragment class.
For android.support.v4.Fragment, documentation say that instead of getFragmentManager() I should call getSupportFragmentManager() and that Activity must extends FragmentActivty.
So I did this change, and now this is my activity code:
public class ExampleActivity extends AppCompactActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment);
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction =
fragmentManager.beginTransaction();
ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();
}
}
AppCompactActivity is a subclass of FragmentActivity so I respected the constraints.
The problem is that if I run my application I get this error:
java.lang.RuntimeException:
com.myapplication.ExampleActivity#13388c6 must implement OnFragmentInteractionListener
I typed OnFragmentInteractionListener on Android doc web search and this is what I get:
Immediately below there is the link
https://developer.android.com/training/basics/fragments/communicating.html
that shows me a guide to communication between fragments. It speaks about ListFragment and I don't care it.
I'm very very confuse because every time I read documentation there are always things that force me to make internet search to find workaround or fix to problems.
Is there a persone that can explain me first how to fix this problem.
From the tutorial, you can read the following:
In order to receive event callbacks from the fragment, the activity
that hosts it must implement the interface defined in the fragment
class.
So, implement it in your Activity with something like this in your fragment:
public static class MainActivity extends Activity
implements HeadlinesFragment.OnHeadlineSelectedListener{
...
public void onArticleSelected(int position) {
// The user selected the headline of an article from the HeadlinesFragment
// Do something here to display that article
}
}
Why did you need it? Because you've forced the host activity to implement it with something like this:
#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 = (OnHeadlineSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnHeadlineSelectedListener");
}
}
Here is my main activity. I followed this guide about Fragments correctly. When I click "Back" button, my application is closed instead of returning to the MainScreenFragment. Why is this happening and why addToBackStack() doesn't work?
public class MainScreenActivity extends ActionBarActivity implements MainScreenFragment.OnFrameChoiced {
private MainScreenFragment mainScreenFragment;
private AddWordsFragment addWordsFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_screen);
mainScreenFragment = new MainScreenFragment();
addWordsFragment = new AddWordsFragment();
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.container, mainScreenFragment).addToBackStack(null).commit();
}
#Override
public void choiceFrame(int id) {
switch (id) {
case R.id.add_new_words_frame:
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.container, addWordsFragment).addToBackStack(null).commit();
fm.executePendingTransactions();
break;
}
}
P.S. I tried to use a solution from this topic, but It still doesn't work.
did you try overriding the back like below:
#overide
public void onBackPressed(){
FragmentManager fm = getSupportFragmentManager();
if (fm.getBackStackEntryCount() > 1) {
fm.popBackStack();
} else {
finish();
}
}
(I know you have picked up your desirable answer, but I have found a little more against this problem)
Though as Android official site has documented:
By calling addToBackStack(), the replace transaction is saved to the back stack so the user can reverse the transaction and bring back the previous fragment by pressing the Backbutton.
But as a matter of fact, this is in a precondition that you are using the standard android activity, specifically, the android.app.Activity. Because this methods in android.app.Activity will work when Backbutton is pressed:
public void onBackPressed() {
if (mActionBar != null && mActionBar.collapseActionView()) {
return;
}
if (!mFragments.getFragmentManager().popBackStackImmediate()) {
finishAfterTransition();
}
}
But, if you are extending your xxxxActivity from someone else, for example, the AppCompatActivity, FragmentActivity, ActionBarActivity, it will be another story, because in FragmentActivity, onBackPressed() method is totally overrode:
public void onBackPressed() {
if (!mFragments.getSupportFragmentManager().popBackStackImmediate()) {
supportFinishAfterTransition();
}
}
Note that mFragments.getFragmentManager() is replaced by mFragments.getSupportFragmentManager(), so in cases like this, you should begin your FragmentTransaction using getSupportFragmentManager() of the Activity. and as a consequence of that, you don't have to override onBackPressed method in your Activity.
BTW, ActionBarActivity extends AppCompatActivity extends FragmentActivity,they all come from the support library, you know what I mean, remember to use getSupportFragmentManager() instead of getFragmentManager() when using support library in order to get the compatible behavior.
Working with Robolectric , I'm very new to android. I made a first test class using Activity. It worked nicely.
Now I want make a test for fragment.
#RunWith(RobolectricTestRunner.class)
public class LoginFragmentTest {
private LoginFragment fragment;
#Before
public void setup() {
fragment = new LoginFragment();
startFragment(fragment);
assertThat(fragment, notNullValue());
assertThat(fragment.getActivity(), notNullValue());
}
private void startFragment(LoginFragment fragment) {
FragmentManager fragmentManager = new FragmentActivity().getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(fragment, null);
fragmentTransaction.commit();
}
#Test
public void login() {
EditText idEditText = (EditText)fragment.getActivity().findViewById(R.id.main_id);
assertThat(idEditText, notNullValue());
}
}
This is my first test class for Fragment class. It throws
"java.lang.IllegalStateException: Activity has been destroyed" in startFragment#fragmentTransaction.commit().
Anyone knows how to fix this ?
You can find whole source from https://github.com/msbaek/frame-test
Thanks in advance !!
In my case, specifically, my problem was when creating the activity.
I was using
activity = Robolectric.buildActivity(MyActivity.class).get();
And it should be
activity = Robolectric.buildActivity(MyActivity.class).create().get();
Hope it helps someone :D
#RunWith(RobolectricTestRunner.class)
public class LoginFragmentTest {
private LoginFragment fragment;
#Before
public void setup() {
fragment = new LoginFragment();
startFragment();
assertThat(fragment, notNullValue());
assertThat(fragment.getActivity(), notNullValue());
}
private void startFragment() {
FragmentActivity activity = new FragmentActivity();
shadowOf(activity).callOnCreate(null);
shadowOf(activity).callOnStart();
shadowOf(activity).callOnResume();
FragmentManager fragmentManager = activity.getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(fragment, null);
fragmentTransaction.commit();
}
#Test
public void login() {
EditText idEditText = (EditText) fragment.getView().findViewById(R.id.main_id);
assertThat(idEditText, notNullValue());
}
}
This is working version. Following 3 lines are important(it's from robolectric source - DialogFragmentTest).
shadowOf(activity).callOnCreate(null);
shadowOf(activity).callOnStart();
shadowOf(activity).callOnResume();
The fragments are supposed to be displayed from an Activity. The flow should be:
allocate a new fragment object in a FragmentActivity class
get the fragment manager to add the newly allocated fragment
In your case you do not have a connection to a real activity. You allocate a FragmentActivity with new FragmentActivity() and try to get the support manager. While this compiles there is no "real" activity able to manage your fragment. Fragments can be added on activities already displayed and here it's not the case.
I recommend reading this page as it explains these things very well: http://developer.android.com/guide/components/fragments.html
That happened for me when I used fragmentTransaction.commitAllowingStateLoss(); from sub Fragment whose parent fragment had setRetainInstance(true); I had activity as property what lead to leaking activity on rotation.