I'm trying to work with Tabhost, I have 2 tabs, each one loads one Activity, each Activity has an aSync method to parse some data. If I do setCurrentTab(0) when creating the Tabmenu Activity everything works fine, but if I try to start the app on the second tab(setCurrentTab(1)) it tries to load the first tab, and does the aSync method of the first tab and crashes because some data is lacking for method to work, which wasn't supposed to be called at all.
Here's the TabMenu.Activity:
public class TabMenu extends TabActivity {
TabHost tabHost = null;
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
setResult(resultCode);
this.finish();
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.menu);
Intent intentToGet = getIntent();
tabHost = getTabHost();
TabHost.TabSpec spec;
Intent intent;
TabHost.TabSpec spec2;
Intent intent2;
intent = new Intent().setClass(this, HoursActivity.class);
intent.putExtra("student", intentToGet.getStringExtra("student"));
intent.putExtra("pass", intentToGet.getStringExtra("pass"));
intent.putExtra("un", intentToGet.getStringExtra("un"));
spec = tabHost
.newTabSpec("hours")
.setIndicator("Hours")
.setContent(intent);
tabHost.addTab(spec);
intent2 = new Intent().setClass(this, NotesActivity.class);
intent2.putExtra("student", intentToGet.getStringExtra("student"));
intent2.putExtra("pass", intentToGet.getStringExtra("pass"));
intent2.putExtra("un", intentToGet.getStringExtra("un"));
spec2 = tabHost
.newTabSpec("notes")
.setIndicator("Notes")
.setContent(intent2);
tabHost.addTab(spec2);
tabHost.setCurrentTab(1);
}
}
Any ideas?
In the past I have had to do something similar and solved it by doing the following:
Create a static boolean variable CAN_LOAD = false and HAS_LOADED = false in your HoursActivity class.
Once whatever needs to happen in the NotesActivity that will allow your HoursActivity class to be loaded, set CAN_LOAD to true.
In HoursActivity, on your onCreate and onResume call the loading function you want to be run, and within that function test for CAN_LOAD == true && HAS_LOADED == false and and load only if both pass, setting HAS_LOADED = true in the function so that it does not get run multiple times.
So when you click on the tab, the onResume will be called and the Activity can do whatever set up is needed. There may be a problem with performance if the load function is slow to perform, in which case you will need to modify it slightly to not wait until the onResume to load (keeping it async). But that should at least get you started off.
Related
New to Android Studio. I'm creating an app project for practice and I am trying to create a Menu Activity. I want to test to see if I can mute sounds and hide the display of text (score) via a Menu UI. I get that I can use Intent to pass values back and forth between activities and that I can use those values to turn features on and off across the app.
I cannot figure out with a button and onClick how to get a variable to change so that I can pass it via Intent. I've only seen it done INSIDE the onClick. I'm trying to change the variable OUTSIDE the onClick.
Example Code:
public class Menu extends AppCompatActivity {
private boolean soundOn = true;
private Button isSoundOn;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_menu);
isSoundOn = findViewById(R.id.isSoundOn_button);
isSoundOn.setOnClickListener(v -> {
soundOn = false;
});
Now when I attempt to access the soundOn parameter and pass it on via Intent to another activity the value remains true, it never changes after the button is clicked.
I did figure out one trick, I can use intent and pass the value to the same activity, like so:
soundOff.setOnClickListener(v -> {
Intent intent = new Intent(Menu.this, Menu.class);
intent.putExtra("soundOn", false);
startActivity(intent);
This reloads the Activity after the button is clicked, it makes it appear as though a glitch happened as it is run, but I think that issue could be resolved via altering the transition animation...I think. However, this seems like a clumsy approach, especially in a Menu Activity that could have numerous settings to edit (sound, score, language, timer, color, background, etc.).
I get that I can have the onClick go back to the original Activity with the change, but I want to create a menu where I can have multiple selections made and then pass them all back to the original Activity.
I hope this makes sense, I know this is rather basic, but I'm new to this and my searching hasn't been able to yield a solution. Thanks.
If you are doing an intent to the same Activity you should retreive your intent on the onCreate method:
isSoundOn = intent.getBooleanExtra("soundOn", true) //true is the default parameter in case intent does not contain that key;
That way you are always checking your intent in case you need it.
You also need to use your variable in the intent; right now you are always setting it to false:
soundOff.setOnClickListener(v -> {
Intent intent = new Intent(Menu.this, Menu.class);
intent.putExtra("soundOn", soundOn);
startActivity(intent);
}
There are other solutions, for example: you can use SharedPreferences for persisting your values and then call this.recreate to recreate Activity again and avoid using intents. Then you can retreive your SharedPreferencesvalues on the onCreate method to do whatever you want.
Now when I attempt to access the soundOn parameter and pass it on via
Intent to another activity the value remains true, it never changes
after the button is clicked.
Lets start with keeping track of soundOn
When Menu activity is first launched soundOn = true
When isSound button is clicked soundOn = false
Intent intent = new Intent(Menu.this, Menu.class);
intent.putExtra("soundOn", soundOn); // false is the value that is extra.
startActivity(intent);
When MenuActivity is again launched due to the intent soundOn = true this is because of this line
private boolean soundOn = true; //<---
You are passing Extra in intent but you arent storing the intents extra value in soundOn
Thats why it is true always
to solve this use need to Get the Intent Extra that you have passed and we do it in onCreate method
private boolean soundOn = true;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_menu);
Intent intent = getIntent();
if(intent != null) { // we need to get this cause when we first start our app we dont start it with any intents
soundOn = intent.getExtra("soundOn");
}
}
I'm creating an app where the user can view their score histories and it works fine except that whenever I click on the fragment directly a new arraylist list is automatically created with the previous custom items. When I use the savebutton intent to get to the fragment no such issues arise and the recyclerview is updated as I want it to.
This is how I save scores using a navigation Menu item
case R.id.save_game:
Intent save = new Intent(getApplicationContext(), ScoreActivity.class);
Bundle extras = new Bundle();
extras.putString("EXTRA_DATE", format);
save.putExtras(extras);
startActivity(save);
Toast.makeText(this, "Saved", Toast.LENGTH_SHORT).show();
break;
I receive the intent in my ScoreFragment
public void saveScore(){
Intent intent = requireActivity().getIntent();
Bundle extras = intent.getExtras();
if (extras != null) {
String format = extras.getString("EXTRA_DATE");
mScoreList.add(new ScoreItem(TeamAName, TeamBName, scoreTeamA, scoreTeamB, format));
saveData(); //Saves Arraylist to sharedpreferences
}
}
And then I call saveScore() in my oncreateView method
My Score Fragment is attached to ScoreActivity like this
public class ScoreActivity extends AppCompatActivity {
#Override
protected void onCreate( Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_category);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction().replace(R.id.container,
new ScoreFragment()).commit();
}
}
}
I've tried putting my saveScore() method in onViewCreated() as well as onResume and onSaveInstanceState but it doesn't change anything.
Changing replace() to add() in my ScoreActivity also doesn't do anything.
Right after I inflate my rootView I call loadData() and I've been trying to call it somewhere else but it causes crahes.
private void loadData() {
SharedPreferences sharedPreferences = requireActivity().getSharedPreferences("shared preferences", Context.MODE_PRIVATE);
Gson gson = new Gson();
String json = sharedPreferences.getString("task list", null);
Type type = new TypeToken<ArrayList<ScoreItem>>() {
}.getType();
mScoreList = gson.fromJson(json, type);
if (mScoreList == null) {
mScoreList = new ArrayList<>();
}
}
How do I go about resolving this issue?
getIntent() always returns the Intent you used to create that Activity - so if you use that menu item to start a new one and add that extra
Intent save = new Intent(getApplicationContext(), ScoreActivity.class);
Bundle extras = new Bundle();
extras.putString("EXTRA_DATE", format);
save.putExtras(extras);
startActivity(save);
then that Activity will always return this Intent + Extras bundle when you call getIntent. So if your fragment has this in its onViewCreated:
Intent intent = requireActivity().getIntent();
Bundle extras = intent.getExtras();
and its parent activity was originally created with that Intent+Extras combo, then it will run your adding code
Honestly it depends on how your app is set up, so I can only give you ideas of things to look at
If you have an activity where fragments get swapped, so ScoreFragment can get re-added (you mentioned "using the navigation drawer to navigate to the fragment") then onViewCreated will run again with that Intent.
If it's off-screen and the system destroys its views to save memory, when it's recreated it will run onViewCreated again with that Intent.
If you call startActivity in different places, but an existing Activity gets reused (say your Activity is launched as singleTop or singleInstance) then it will have the Intent+Extras it was originally created with. So if you first create it through that menu item with the extras, and then call startActivity() without the extras from somewhere else, if the original activity gets used it will still have that Intent+Extras it was created with. (It'll receive the new intent throughonNewIntent() but it won't change the value of getIntent)
Also you're not actually checking if the extras Bundle has the EXTRA_DATE item, just if it has some extras, and then you're adding a new ScoreItem with whatever extras.getString("EXTRA_DATE") returns (which might be `null``). So if you're launching with other bundles of extras in different situations, you might still get the extra score added
I know that's a lot and it's not a solution but hope it helps! Honestly I'd recommend setting a breakpoint in saveScore, debug the app and use the debugger to see what getIntent is giving you, and if it looks right.
So I managed to solve the issue but I have no idea what I actually did. I decided to create an easy splash screen for my app and the problem just disappeared. I'm so confused right now and I would like some help in figuring it out so I can correct it in the future.
I created a new Activity called SplashScreenActivity
public class SplashScreenActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EasySplashScreen config = new EasySplashScreen(SplashScreenActivity.this)
.withFullScreen()
.withTargetActivity(MainActivity.class)
.withSplashTimeOut(5000)
.withBackgroundColor(Color.parseColor("#FF9800"))
.withHeaderText("Header")
.withFooterText("Footer")
.withBeforeLogoText("Before Logo Text")
.withAfterLogoText("After Logo Text")
.withLogo(R.mipmap.ic_launcher_round);
config.getHeaderTextView().setTextColor(Color.WHITE);
config.getFooterTextView().setTextColor(Color.WHITE);
config.getBeforeLogoTextView().setTextColor(Color.WHITE);
config.getAfterLogoTextView().setTextColor(Color.WHITE);
View easySplashScreen = config.create();
setContentView(easySplashScreen);
}
}
Then I moved MainActivity's intent-filter to the Splash Screen Activity in my Manifest.xml
file and this is what I think changed things.
<activity android:name=".SplashScreenActivity"
android:theme="#style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
I moved it back to the MainActivity a couple of times and I realized the problem came back each time the intent filter was in MainActivity. Now I keep it in .SplashScreenActivity in the Manifest and I have no such issues. I really need some explanation for this.
I have one activity where the user writes in an EditText a number, then I am supposed to convert it to a string and then to an int, but I want to save that value because I am going to use it for another activity, it will be the max number of times that a botton can be pressed but I don't know how to save it from the EditText in a activity to an int in another activity.
Here the EditText of the first activity:
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="number"
android:ems="10"
android:id="#+id/texthome"
android:layout_marginLeft="30dp"
android:layout_marginRight="30dp"
android:hint="Ejemplo:15"
android:layout_weight="1"
android:textSize="20sp" />
Here the textview into int:
EditText conttext = (EditText) findViewById ( R.id.texthome );
String maxicont = conttext.getText().toString();
int maxcont = Integer.parseInt(maxicont);
UPDATE:
i get errors in:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Button buttsave = (Button) findViewById(R.id.buttsave);
buttsave.setOnClickListener(new View.OnClickListener"here" {
public void onClick "here" {
Intent intent = new Intent(getApplicationContext(), GuessOne.class);
intent.putExtra("maxNumberPressed", conttext.getText());
startActivity(intent);
}
});
I get errors where i placed the "here" obviously it is not part of the code. It is only to see where the errors are.
You need to use Intents for that. A similar question was answered here:
Passing Values To Another Activity
When starting the other activity, make a bundle.
In the current activity, try:
Intent intent = new Intent(getApplicationContext(), <next activity>.class);
intent.putExtra("maxNumberPressed", editText.getText());
startActivity(intent);
Then in the next activity where you are using it, put this in onCreate method:
Bundle bundle = this.getIntent().getExtras();
String maxPressed = bundle.getString("maxNumberPressed");
Create a button with text as 'save' or something. then do:
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new OnClickListener{
public void onClick() {
Intent intent = new Intent(getApplicationContext(), <next activity>.class);
intent.putExtra("maxNumberPressed", editText.getText());
startActivity(intent);
}
});
How to pass data from an Activity to another can vary a lot, depending on how the Activities are related.
If you are passing data from a parent to a child Activity, you pass the information through an Intent when starting the new child Activity. Here's how you send the information:
itt = new Intent(this, ChildActivityName.class); // Create new Intent
itt.putExtra("MAX", maxcont); // Fill Intent with data
startActivity(itt, SOME_NUMBER_IDENTIFYING_THE_PARENT); // Start Activity
And here's how the receiving child Activity retrieves it:
void onCreate(Bundle savedInstanceState) {
...
Intent itt = getIntent(); // Grab the Intent created with this Activity
int maxcont = itt.getIntExtra("MAX", -1); // Retrieve data, default is -1
}
If you are passing data the other way, from a child to a parent Activity, the simplest way to pass through using startActivityForResult. The parent starts the child activity using this command:
startActivityForResult(itt, SOME_NUMBER_IDENTIFYING_THE_PARENT);
The Child then passes the information back to the parent just before it exits:
Intent itt = new Intent(); // Create new Intent
itt.putExtra("MAX_RETURN", maxcont); // Add data to the Intent
setResult(RESULT_OK, itt); // Signal to parent valid result
finish(); // End this Activity
Once the child calls finish(), the parent's onActivityResult() method will be invoked:
protected void onActivityResult(int requestCode, int resultCode, Intent itt) {
maxcont = data.getIntExtra("MAX_RETURN", -1); // defaults to -1
...
}
The Strings used in putExtra and getIntExtra are identifying keys for associated data, which are used all over the place in Android programming and beyond the scope of this question. Just make sure that the sender and the receiver of the data through the Intents use the EXACT same string!
If you need to pass information between two sibling Activities that are both active, the easiest way is through static public variables in each Activity. That's pretty basic; you should have no problem with these.
If you need to pass information between two sibling Activities that may or may not be Active, then you need to use a Broadcast/Receiver paradigm. But I think that method is overkill for what you are talking about.
In an activity, I have created a AsyncTask after hiding the activity:
this.moveTaskToBack(true);
(new MyTask(this)).execute();
To show a dialog in the task (in onPostExcecute), I want to bring the activity to front:
alertDialog.show();
Intent intent = new Intent(mainActivity, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mainActivity.getBaseContext().startActivity(intent);
But a new instance of the main activity is created and shown on top of the dialog, although the application was still running (the activity has also a dialog style Theme.Dialog). How should I fix this?
Edit: According to javadoc, this code always recreates the activity and doesn't bring its previous instance to front, since startActivity is called from outside of an Activity Context.
How about adding a new piece of information to that intent, and catching it in onCreate()?
What I mean is something like this:
public class MainActivity extends Activity {
public static final String WANT_DIALOG_EXTRA = "WANT_DIALOG_EXTRA";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getIntent().hasExtra(WANT_DIALOG_EXTRA)) {
// create and show dialog
}
}
}
Then when you create your intent, add one more line like this:
intent.putExtra(MainActivity.WANT_DIALOG_EXTRA, true);
This is my first issue with the Android lifecycleand I feel somewhat helpless:
In Activity A there's onCreate. That's the spot where I create an ArrayList called playerNames and ArrayList called moves. Also there's some more stuff happening in oncreate. In A's onStart I create a flag so I know which Activity is running in case I'd like to close all at once. In onDestroy the flag is set back to null.
Eventually I make an intent to get to Activity B where I take the moves list along. Works fine.
Now I'd like to make an intent from B to get back to A. What happens in the lifecycle when I attempt that? Obviously onCreate of A is called and leads to a NullPointerException regrding the playerNames list.
I'd like to store this ArrayList while B is running and get it back when I come back to A. Which method is the right one (onResume? onRestart?) and how do I store it? Do I really need SharedPreferences?
Thanks in advance for your kind help
On Activity A:
-Create an Intent, specifying wich activity you want to start
-Put data on that intent to be received by activity B.
-Start Activity for a result.
-On onActivityResult() verify that you have data to receive and do what you want with it.
On Activity B:
-On the onCreate() receive the data from Activity A;
-Modify the data as you wish;
-Create a new Intent;
-Put the data in the Intent;
-Set the activity result and the intent data.
-Finish activity B
Below is a sample of the steps I describe, what it does is have activity A start an Activity B, and passing it an empty ArrayList. Then on Activity B, the arrayList is populated and sent back to Activity A where the contents of the arrayList, are displayed on screen.
Code:
Activity A:
private static final int REQUEST_LIST = 1;
ArrayList<String> myList;
TextView listText;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listText = (TextView) findViewById(R.id.mylist_text);
myList = new ArrayList<String>();
Intent i = new Intent(MainActivity.this, ActivityB.class);
i.putExtra(ActivityB.EXTRA_ARRAY, myList);
startActivityForResult(i, REQUEST_LIST);
}
#Override
protected void
onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode != Activity.RESULT_OK) return;
if (requestCode == REQUEST_LIST) {
myList = (ArrayList<String>) data.getSerializableExtra(ActivityB.EXTRA_ARRAY);
StringBuilder text = new StringBuilder();
for (int j = 0; j < myList.size(); j++) {
text.append(myList.get(j) + " ");
}
listText.setText(text.toString());
}
}
Activty B:
public static final String EXTRA_ARRAY = "com.example.androidtest.mainactivity.array";
ArrayList<String> myList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activityb_layout);
myList = (ArrayList<String>) getIntent().getSerializableExtra(EXTRA_ARRAY);
for (int i = 0; i < 10; i++) {
myList.add(String.valueOf(i));
}
Intent data = new Intent();
data.putExtra(EXTRA_ARRAY, myList);
setResult(Activity.RESULT_OK, data);
finish();
}
Attention: You cannot forget to declare your Activity B in the manifest File. Also pay attention on how the activities know what data to send and collect, through constants created in the classes, wich must be consistent, so avoid using literal strings, and use defined constants.
From Activity B you can come back to the previous A instance using:
Intent intent = new Intent(this, A.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
This will clear all current stack and go back to the A instance ( http://developer.android.com/reference/android/content/Intent.html#FLAG_ACTIVITY_CLEAR_TOP ). As long as your Activity doesn't get destroyed, you'll have all data there.
Anyway, you should consider use Activity's save instance state capability ( http://developer.android.com/reference/android/app/Activity.html#onSaveInstanceState%28android.os.Bundle%29 ).