I'm learning Android development by creating a test project: Tic-tac-toe. My Tic-tac-toe app starts with a Main Menu activity with a Button that says New Game. After clicking New Game, an activity with a fragment containing a Tic-Tac-Toe game (in a GridLayout) launches. The user can play the game, but when they go back to the Main Menu, the game state is not saved.
I want to change this so that when the user goes back to the Main Menu, they will see a new Button called "Continue" (in addition to the "New Game" Button). Once the user clicks "Continue", the game they were playing before continues. If the click the "New Game" button, a new game will be launched like before.
By using the onSaveInstanceState method and a Bundle savedInstanceState, I was able to preserve the game state on orientation changes (by saving the data from the underlying TicTacToe class). My code below shows how I did that. I would like to do something similar this time again - I have read about it, but I don't quite understand the best way to start. Can anyone show me the right steps?
Note that I'm doing this programmatically (i.e. designing the layout outside the xml files)!
My MainMenu class:
public class MainMenu extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_menu);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main_menu, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
public void startGame(View view) {
Intent intent = new Intent(this,MainGame.class);
startActivity(intent);
}
}
with a corresponding xml file activity_main_game:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/new_game"
android:onClick="startGame"
/>
</RelativeLayout>
Now my MainGame.class looks like this:
public class MainGame extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_game);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new BoardFragment()).commit();
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main_game, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
public static class BoardFragment extends Fragment {
public TicTacToe t = new TicTacToe(); //A class I wrote that launches a simple TicTacToe game
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (savedInstanceState != null) {
t = new TicTacToe(savedInstanceState.getInt("TicTacToeData"),
);
}
//Graphics stuff here: variable rootView which contains the TicTacToe grid is defined
//and onClickListeners are added to the ImageViews in the GridLayout which makes corresponding
//changes to t.
return rootView;
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putInt("TicTacToeData", t.getGameData());
}
}
}
Thanks for any help.
You have two options
SharedPreferences: you can use shaerdPreferences to store the state of the game. It would be stored also after the restart of the application.
start your game activity with startActivityForResult() and implementing onActivityResult on your parent activity.
There are 2 things you have to do:
Get notified within Main class when your MainGame Activity is closed
Update the UI to change the button text
To get notified within Main class:
You can start MainGame Activity using startActivityForResult instead of startActivity. Then override the onActivityResult method on Main class to be notified when the user close the MainGame.
Also you will have to modify MainGame to invoke setResult(int resultCode, Intent data) or setResult(int resultCode) method when the user click the back button (depending on whether you want to pass additional data back to the Activity.
Override the onBackPressed() method in your MainGame class to invoke the setResult method. E.g.
public void onBackPressed() {
setResult(RESULT_OK);
super.onBackPressed(); // otherwise your Activity won't be closed
}
Have a look at http://developer.android.com/reference/android/app/Activity.html#StartingActivities for a more detailed example
To Update the UI:
Save the button as a local variable (in your onCreate method)
Update the Button text MainGame Activity
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
if (requestCode == PICK_CONTACT_REQUEST) {
if (resultCode == RESULT_OK) {
// Update the button text to "Continue"
} else {
// Change the button text back to "New Game"
}
}
}
One possible solution is to have your MainGame Activity return the state of your game when it finishes. Then your MainMenu Activity can save that value and use it to start up a game in the same state.
For starters, you will want to start your game Activity in a way that enables it to return a value:
public class MainMenu extends ActionBarActivity {
...
public void startGame() {
Intent intent = new Intent(this, MainGame.class);
startActivityForResult(intent, 1);
}
}
Next you will want to make sure that your board Fragment returns the correct state whenever it is closed:
public class BoardFragment extends Fragment {
...
#Override
public void onPause() {
Intent result = new Intent();
result.putExtra("TicTacToeData", t.getGameData());
getActivity().setResult(Activity.RESULT_OK, result);
}
}
Afterwards you will want your menu Activity record the result:
public class MainMenu extends ActionBarActivity {
private int mPreviousGameState;
...
#Override
protected void onActivityResult (int requestCode, int resultCode, Intent data) {
if (requestCode == 1 && resultCode == Activity.RESULT_OK) {
mPreviousGameState = 1;
}
}
}
Now you can pass in this state when you start your game Activity:
public class MainMenu extends ActionBarActivity {
...
public void continueGame() {
Intent intent = new Intent(this, MainGame.class);
intent.putExtra("TicTacToeData", mPreviousGameState);
startActivityForResult(intent, 1);
}
}
Now you will need a way to create a board that is already in a specific game state:
public class BoardFragment extends Fragment {
public TicTacToe t = null
...
public BoardFragment (int gameState) {
t = new TicTacToe(gameState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (savedInstanceState != null) {
// recreate the board after an orientation change
t = new TicTacToe(savedInstanceState.getInt("TicTacToeData"));
} else if (t == null) {
// create a new board if this is a new game
// if this is a continued game, the board is already setup
t = new TicTacToe();
}
}
And lastly you will need to call the correct version of BoardFragment from your game:
public class MainGame extends ActionBarActivity {
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_game);
if (savedInstanceState == null) {
Intent intent = getIntent();
int gameState = intent.getIntExtra("TicTacToeData", -1);
if (gameState == -1) {
getSupportFragmentManager().beginTransaction().add(R.id.container, new BoardFragment()).commit();
} else {
getSupportFragmentManager().beginTransaction().add(R.id.container, new BoardFragment(gameState)).commit();
}
}
}
}
Another solution is to redesign your app to have only a single Activity (or ActionBarActivity) subclass and multiple Fragment subclasses for each screen you want to show. You can keep a reference to both fragments in your Activity. This will implicitly retain the state of the fragment which displays the game board.
Related
I have a fragment with a view and an options menu:
public class OfferingsFragment extends Fragment
{
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState)
{
setHasOptionsMenu(true);
View view = inflater.inflate(R.layout.offering_tiles, null);
...
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
Intent intent = new Intent(getActivity(), SettingsActivity.class);
startActivity(intent);
return true;
}
return super.onOptionsItemSelected(item);
}
}
From the options menu, the user opens this preference fragment, which is hosted by the SettingsActivity:
public class SettingsActivity extends Activity {
private SettingsFragment settingsFragment = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
settingsFragment = new SettingsFragment();
getFragmentManager().beginTransaction()
.replace(android.R.id.content, settingsFragment)
.commit();
}
The view of the OfferingsFragment depends on one of the preferences. That is, after this preference has changed, the OfferingsFragment must be refreshed by calling onCreateView again. What I do is this:
Open preference screen from OfferingsFragment's option menu
Change preference
Return to OfferingsFragment
If I return to the OfferingsFragment via the Home Button (left arrow in ActionBar), then the OfferingsFragment gets refreshed by calling its onCreateView (which is the desired effect). However, if I return to the OfferingsFragment via the Back Button (on the device), onCreateView is NOT CALLED and thus the view is NOT re-created. What I want is that the view is also re-created when the user presses the Back Button. Any ideas how to achieve this?
What Happens
When you press Up button parent activity is called via startActivity which means a new instance is created by default.
When you press Back button current activity is finished and you're back in the previous activity and its already existing instance (it was in stopped state).
How To Deal With It
What I want is that the view is also re-created when the user presses the Back Button. Any ideas how to achieve this?
Start the settings activity via startActivityForResult:
public static final int RC_SETTINGS = 1;
private void startSettingsActivity() {
Intent i = new Intent(this, SettingsActivity.class);
startActivityForResult(i, RC_SETTINGS);
}
When the result comes back reattach the fragment. This will recreate its view hierarchy.
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// Call to super if you value your life. And want proper lifecycle handling.
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RC_SETTINGS) {
// If we just came back from SettingsActivity...
// ...reattach fragment and trigger view recreation.
final FragmentManager fm = getFragmentManager();
final f = fm.findFragmentById(android.R.id.content);
fm.beginTransaction().detach(f).attach(f).commit();
}
}
Replace the fragment ID with whatever you used.
Pro tip
If your fragment is not misconfigured you should be able to call
public class SettingsActivity extends Activity {
private SettingsFragment settingsFragment = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
settingsFragment = new SettingsFragment();
getFragmentManager().beginTransaction()
.replace(android.R.id.content, settingsFragment)
.commit();
} else {
settingsFragment = (SettingsFragment) getFragmentManager().findFragmentById(android.R.id.content);
}
}
}
This is both resourceful and practical as your original code would lose state (for example scroll position) on configuration change.
Whenever I run my activity in the emulator, it always takes me to MainActivity (if not logged into facebook then goes through FacebookLoginFragment) and then ProfileActivity where ProfileActivity is created twice (still trying to figure this one out). I am still trying to understand the SharedPreferences class, so would appreciate someone explaining that to me!
Ideal Scenario
If app is launched first time MainActivity launches, user is logged into facebook and directed to ProfileActivity
MainActivity is killed in case user hits back button on android
If user exits app and resumes at a later time it should launch at ProfileActivity
Problem
Is my code setup correctly?
How would I set it up correctly, so that login page is only visited once?
Would implementing an onResume() method in MainActivity help?
Code is here:
First activity MainActivity:
public class MainActivity extends FragmentActivity {
private FacebookLoginFragment mainFragment;
protected LoginButton fbLogin;
private ImageView splashLogo;
protected Animation anim;
private static final String FIRST_LAUNCH = "first_launch";
private SharedPreferences prefs;
private SharedPreferences.Editor editor;
#Override
protected void onCreate(Bundle savedInstanceState) {
prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
editor = prefs.edit();
Intent i;
//Assume false if the key does not yet exist
if (prefs.getBoolean(FIRST_LAUNCH, false)) {
editor.putBoolean(FIRST_LAUNCH, true);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null){
//Add the fragment on initial activity setup
mainFragment = new FacebookLoginFragment();
getSupportFragmentManager()
.beginTransaction()
.add(android.R.id.content, mainFragment)
.commit();
} else {
//Or set the fragment from restored state info
mainFragment = (FacebookLoginFragment) getSupportFragmentManager()
.findFragmentById(android.R.id.content);
}
splashLogo = (ImageView)findViewById(R.id.appLogoFixed);
anim = AnimationUtils.loadAnimation(this, R.anim.fade_in);
} else {
editor.putBoolean(FIRST_LAUNCH, true);
editor.commit();
//Go to profile page
i = new Intent(MainActivity.this, ProfileActivity.class);
startActivity(i);
finish();
}
}
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus)
splashLogo.startAnimation(anim);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
Second Activity: ProfileActivity:
public class ProfileActivity extends Activity {
protected ProfilePictureView profilePicture;
private TextView userInfoTextView;
private String userId;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.profile_facebook);
Intent i = getIntent(); //Grab the intent from previous activity
userId = i.getStringExtra("Fb_id");
profilePicture = (ProfilePictureView)findViewById(R.id.profilePicture);
profilePicture.setProfileId(userId);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
public void onResume() {
super.onResume();
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data){
super.onActivityResult(requestCode, resultCode, data);
}
#Override
public void onPause(){
super.onPause();
}
#Override
public void onDestroy(){
super.onDestroy();
}
#Override
public void onSaveInstanceState(Bundle outState){
super.onSaveInstanceState(outState);
}
}
your FIRST_LAUNCH variable is little bit confusing for me as I expected it to be true on first start, because it's not yet set. And later set it to false, because it's not "first launch" anymore.
Anyway what I see, in first block of your code in MainActivity > onCreate, you set
editor.putBoolean(FIRST_LAUNCH, true);
but you probably forget to execute editor.commit(), so your new preferences are never saved.
EDIT: just very short explanation. Seems you understand SharedPreferences correctly. It's just important to keep in mind, that till you call editor.commit() all changes are not saved!! If you do not call commit, you changes will be lost.
I have my main activity actionbaractivity One where you can screenslide through some fragmets, on each fragment you have an imageView and a ListView where you can click any item and the image will change. Also in the menu options you have a button where you change to an almost exact activity: actiobbaractivity Two which also have this button to change to activity One
What I'm able to do is to keep the image when sliding the fragments, but unable to keep the fragments state's through the change of activities.
For example
I'm in activity One on fragment 3 with the image: "something". I click on the button to change to activity Two, I do things here and then, I click on the button to change to activity One and I want to see my fragment 3 with the image: "something" and not the default fragment 1 and default image
Im using ActionBarActivity, FragmentStatePagerAdapter and Fragment for each activity
Thanks for the help
According to the Activity and Fragment lifecycles (http://developer.android.com/reference/android/app/Activity.html#ActivityLifecycle and http://developer.android.com/guide/components/fragments.html#Lifecycle), the most reliable way of persisting states between activity/fragment changes is to use the default API for saving and restoring states:
When the activity/fragment is being dismissed (either because of a configuration change such as screen rotation or because the user requested to go to another activity/fragment), you can save its state in a Bundle object. When it is being created, you can restore its saved state, thus recreating a new instance exactly like the one the user left - so the user feels nothing has changed. This does not depend on the specific subclass of activity/fragment you are using.
I have implemented something like what you want: in my case, a fragment containing a menu with buttons that would each lead the user to another fragment containing a submenu with a "back" button. So if the user went from menu to submenu 1, then back to menu, then to submenu 2, then back to menu and finally again to submenu 1, I wanted that submenu 1 to appear just like the user has left it in the first time.
For that I have created:
1) an interface defining my submenu types, implemented by my activities so they could change between my submenus
2) a master generic class, which all my submenus would extend, that had a Bundle object to store their state
3) in my activities, I had an array of Bundle capable of storing one instance of each of my submenus (because I am only interested in restoring the last state, so I don't need more than one)
The interface (item 1):
public interface SubmenusManager {
public static enum Submenus {
ROOTMENU,
SUBMENU1,
SUBMENU2;
private static final int size = Submenus.values().length;
public static int size() {
return size;
}
public static int getId(Submenus test) {
switch(test) {
case SUBMENU1:
return 1;
case SUBMENU2:
return 2;
case ROOTMENU:
default:
return 0;
}
}
}
public void cloneCurrentSubmenuState(Parcelable toOverwrite);
public Bundle getLastStoredSubmenuState(Submenus submenu);
public void setCurrentSubmenuTo(Submenus submenu);
}
The generic class (item 2):
public class MenuFragment extends Fragment {
private Bundle menuData = new Bundle();
public static String RESTORE_MAIN_OBJECT = "restore_main";
public Bundle getMenuData() {
return menuData;
}
public Bundle cloneMenuData() {
return new Bundle(menuData);
}
public void setMenuData(Bundle menuData) {
this.menuData = menuData;
}
}
One of the activities (item 3):
public class ExampleAct extends FragmentActivity implements SubmenusManager {
/**
* instance variables
*/
private MenuFragment mMenu;
private Bundle [] menuData; // the Array of Bundles!
private static final String CONTAINER = "parcelable_container";
private static final String SUBMENU = "saved_submenu";
private Submenus curSubmenu = Submenus.ROOTMENU; // the default state is the ROOTMENU
private boolean restoreLastSavedState = false;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) { // first time creating this activity
menuData = new Bundle[Submenus.size()];
} else { // this activity has a saved state from before
// restore all the data from all the submenus
menuData = (Bundle[]) savedInstanceState.getParcelableArray(CONTAINER);
// restore the info about which is the current active submenu
curSubmenu = (Submenus) savedInstanceState.getSerializable(SUBMENU);
}
buildMenuFragment(true);
//(...) stuff
}
private void buildMenuFragment(boolean restoreState) {
// (re)builds fragment inside menu.
// restoreState flags whether activity should look for
// saved state data and restore it
restoreLastSavedState = restoreState;
switch(curSubmenu) {
// Eclipse warns you about which are the constants in your enum
case ROOTMENU:
mMenu = new FragmentRootMenu();
break;
case SUBMENU1:
mMenu = new FragmentSubmenu1();
break;
case SUBMENU2:
mMenu = new FragmentSubmenu2();
break;
}
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.menu_frame, mMenu)
.commit();
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable(SUBMENU, curSubmenu);
cloneCurrentSubmenuState(mMenu.getMenuData().
getParcelable(MenuFragment.RESTORE_MAIN_OBJECT));
outState.putParcelableArray(CONTAINER, menuData);
// (...) stuff
}
#Override
public void cloneCurrentSubmenuState(Parcelable toOverwrite) {
if (menuData == null) menuData = new Bundle[Submenus.size()];
if (toOverwrite != null)
mMenu.getMenuData().putParcelable(MenuFragment.RESTORE_MAIN_OBJECT, toOverwrite);
menuData[Submenus.getId(curSubmenu)] = mMenu.cloneMenuData();
}
#Override
public Bundle getLastStoredSubmenuState(Submenus forThisSubmenu) {
return
(menuData == null || !restoreLastSavedState) ? new Bundle() : menuData[Submenus.getId(forThisSubmenu)];
}
#Override
public void setCurrentSubmenuTo(Submenus toThisSubmenu) {
if (mMenu != null) {
cloneCurrentSubmenuState(mMenu.getMenuData().
getParcelable(MenuFragment.RESTORE_MAIN_OBJECT));
}
curSubmenu = toThisSubmenu;
buildMenuFragment(true);
}
One of the submenus (extension of item 2):
public class FragmentSubmenu1 extends MenuFragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_submenu1, null);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
init();
}
public void init() {
// (...) stuff
MyParcelableObject tmp = null; // MyParcelableObject is a class
// that implements Parcelable and stores
// relevant info to rebuild this menu
// from a saved state
SubmenusManager m = (SubmenusManager) getActivity(); // remember activity implements SubmenusManager
Bundle bnd = m.getLastStoredSubmenuState(SubmenusManager.Submenus.SUBMENU1);
if (bnd != null) tmp = bnd.getParcelable(MenuFragment.RESTORE_MAIN_OBJECT);
if (tmp == null) {
tmp = new MyParcelableObject();
tmp.buildFromScratch(); // initializes with default data
}
// back button
Button backToMainMenu = (Button) getView().findViewById(R.id.submenu1_back);
backToMainMenu.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
((SubmenusManager) getActivity()).
setCurrentSubmenuTo(SubmenusManager.Submenus.ROOTMENU);
}
});
// (...) stuff
}
}
The Root menu (extension of item 2):
public class FragmentRootMenu extends MenuFragment {
View myView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
myView = inflater.inflate(R.layout.fragment_rootmenu, null);
return myView;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
init();
}
public void init() {
Button btnSubmenu1 = (Button) myView.findViewById(R.id.btn_call_submenu1);
btnSubmenu1.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
((SubmenusManager) getActivity()).
setCurrentSubmenuTo(SubmenusManager.Submenus.SUBMENU1);
}
});
Button btnSubmenu2 = (Button) myView.findViewById(R.id.btn_call_submenu2);
btnSubmenu2.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
((SubmenusManager) getActivity()).
setCurrentSubmenuTo(SubmenusManager.Submenus.SUBMENU2);
}
});
}
}
For that to work between activities, all you need to do is pass that object that stores the last state of all fragments (in my case, that would be Bundle [] menuData) to the activity that is being called through its Intent; you would recover it the same way as my ExampleAct did in its onCreate(). You could also wrap that Bundle [] inside a custom Parcelable object (very similar to my example MyParcelableObject; inside that one I had stuff like HashMap) if using an array is a problem.
Here how to pass a Parcelable between activities:
How to send an object from one Android Activity to another using Intents?
I got a situation here .
I have FragmentActivty which holds a Fragment . when i click on a button in Fragment i am going to a Activty . when i reach the activity i do something which will affect the data displayed in Fragment where i came from. So in order to bring that changes in fragment i would like to give a callback from the Activity to Fragment. First i thought of implementing onActivityResult. But i realized it's not what i needed.
Is my approach is wrong?? Please guide me
MyActivity extends FragmentActivity
MyActivity holds
MyFragment extends Fragment
From here i'm going to
SecondActivity extends Activity
from SecondActivity i need to get a callback to MyFragment . Is there anything wrong with my approach ??
EDIT:
public class MainActivity extends FragmentActivity {
private FrameLayout frameLayout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
frameLayout = (FrameLayout) findViewById(R.id.framelayout);
loadFragment();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
private void loadFragment() {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager
.beginTransaction();
MyFragment myFragment = new MyFragment();
fragmentTransaction.add(R.id.framelayout, myFragment);
fragmentTransaction.commit();
}
}
MyFragment.java
public class MyFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_view, container, false);
Button button = (Button) view.findViewById(R.id.button);
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
callActivity();
}
});
return view;
}
private void callActivity() {
Intent intent = new Intent(getActivity(), SecondActivity.class);
startActivityForResult(intent, 10);
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// super.onActivityResult(requestCode, resultCode, data);
Log.e("MyFragment Inside", "Onresultttt");
if (requestCode == 10) {
if (resultCode == Activity.RESULT_OK) {
Log.e("Result code", Activity.RESULT_OK + " okkk");
}
if (resultCode == Activity.RESULT_CANCELED) {
Log.e("Result code", Activity.RESULT_CANCELED + "cancelll inside fragment");
}
}
}
}
SecondActivity.java
public class SecondActivity extends Activity {
private static final String TAG = "second activity";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.secondactivity_view);
}
#Override
public void onBackPressed() {
// super.onBackPressed();
Intent intent = new Intent();
setResult(RESULT_CANCELED, intent);
Log.e(TAG, "result setted");
finish();
}
}
Refer this question it will help you understand what is happening:
onActivityResult is not being called in Fragment
If I understand your situation correctly, then the correct way of handling the communication is to have SecondActivity pass back information to MyActivity, which will in turn configure it's instance of MyFragment directly. You don't want to be accessing Fragments from Activities unless the Fragment is attached to the Activity.
As for how to do the communication, as you suggested, one way of doing it would be through the use of startActivityForResult(). See this answer for more details: How to manage `startActivityForResult` on Android?
Just a note about startActivityForResult(). If you are calling it from the Fragment then your Fragment will receive the result, not your Activity. There are also some other issues with calling startActivityForResult() from a Fragment, so I would generally recommend that you instead call it from the Activity and therefore handle the result from the Activity.
Intent i = new Intent(this, SecondActivity.class);
startActivityForResult(i, INTENT_CODE);
The best practice way of communicating from a Fragment to an Activity is by defining an Interface in the Fragment, which the Activity implements.
public class MyFragment extends Fragment {
public interface MyFragmentListener() {
public void onMyFragmentEvent();
}
public void startTheActivityForResult() {
((MyFragmentListener)getActivity()).onMyFragmentEvent();
}
}
public class MainActivity extends FragmentActivity implements MyFragmentListener{
#Override public void onMyFragmentEvent() {
}
}
The Fragment then simply casts the reference to the Activity it is attached to, knowing that the Activity must implement the Listener, thus allowing you to reuse the Fragment in other Activities.
I have a popup activity window (using the inflater) and an array
public class PopUp extends Activity{
ArrayList<String> nameSave = new ArrayList<String>();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.popup_layout);
getActionBar().setDisplayHomeAsUpEnabled(true);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.popup_layout, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
NavUtils.navigateUpFromSameTask(this);
return true;
}
return super.onOptionsItemSelected(item);
}
public void upload(View view)
{
EditText editText = (EditText) findViewById(R.id.nameBox);
String name = editText.getText().toString();
nameSave.add(name);
}
First of all I'm not even sure if I got the array/list working right. Basically when the person hits the button 'upload' I want it to grab the name and add it to the array. Then I want that array sent back to the previous activity (Launch) which has 2 fragments. One fragment is a listview and I want to put the array's content into that fragment. How do I go about passing this array? Is it as simple as putting it in an intent?
Yes you can put putStringArrayListExtra(name, value) in your intent pass the intent by startActivityForResult(intent, requestCode) and when you done this Activity. You can get the intent by the method described below because the method will be called when your current Activity finished. So get the required data and do what ever you want to do with this data. Or you can also put the array in the intent.
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// TODO Auto-generated method stub
if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {
get your data here and do what ever you want to do with it
}
}
in previous Activity. My be this will help you.