Android Parent and Child Activity onCreate question - android

I have a parent activity, and a child activity that extends the parent activity. When the parent starts the child activity,
Which onCreate gets executed first? The child's or parent's?
There is a particular variable I am setting in the Child activity's onCreate method, and right now, it looks like it takes a while to get to the Child activity's onCreate, and so the methods in the Parent are reporting an empty variable. Whereas when I make the Parent sleep for a while, it reports the correct variable.
Thanks
Chris
Parent Activity:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mylayout);
goButton = (ImageButton) this.findViewById(R.id.goButton);
goButton.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
Intent childIntent = new Intent("com.example.Child");
String newValue = "Child Value";
Bundle bun = new Bundle();
bun.putString("value", newValue); // add two parameters: a string and a boolean
childIntent.putExtras(bun);
startActivity(childIntent);
}
});
this.doTheWork("Parent Value");
}
private void doTheWork(String value) {
new MyNewThread(value).start();
}
public String getTheValue(String value) {
return "My Value is: " + value;
}
private class MyNewThread extends Thread {
String value;
public LoadThread(String v) {
this.value = v;
}
#Override
public void run() {
String str = getTheValue(this.value);
}
}
Child Activity:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle bun = getIntent().getExtras();
childValue = bun.getString("value");
}
public String getTheValue(String value) {
return "My Value is: " + value;
}
So, basically, even after the Parent starts the Child, it still returns "Parent Value", but when I have the thread sleep, it return "Child Value".

Your extended class's method (the child class) is called first, but your super class's method is called immediately after since the first line in your child class's method is
Child Activity
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
This is an explicit call to your super class's onCreate Method.

First answer about the sequence. First is call parent and next one is child. This information you can read from your source code.
Next information is about why you have different result when you are using sleep.
First of all the code of Child Activiry onCreate is not completed because is not show how you are using field (or local variable) childValue. But I expect that you are creating new thread in child object too. Please remember that thread are not run in the moment when you are creating the thread but in the moment when the runtime find time for this. The method sleep inform runtime that you can run the thread because your main thread is sleeping and this is the reason of different result. Important is that you are creating two thread one in child and one in parent.

Related

Is it possible to make a loop pause waiting for activity inside it to finish?

An idea I'm trying to implement is: loop through elements of a list, displaying and waiting to hear back from different activities depending on a particular value an element has.
What I thought would work:
for (Model item:questionBank) {
if (item.getTaskType() == "taskType1") {
Intent intent = new Intent(MainActivity.this, 2ndactivity.class);
intent.putExtra("word", item.getWord());
startActivityForResult(intent,REQUEST_CODE);
}}
plus an override method onActivityResult later in the code.
What happens is application starts the activity but it also continues looping through elements without waiting for started activity to finish...
Is it possible to make it wait for started activity to finish before moving on?
In your case for loop will not wait for result from other activity. So, I have another logic to solve this problem.
Declare a iterator variable in the class level like int i = 0;
When you want to start another activity for result, get data from the list at the i index questionBank[i] and start activityForResult.
In the onActivityResult method increment i.
i++
String word = questionBank[i];
startActivityForResult(intent,REQUEST_CODE);
In this way, you can achieve your desired results.
There is a solution no need startforResult or changes in Child activity!! , you can use startActivity(intent) on Parent Activity:
Using the activity lifecycle ,
to stop after call child Activiy and resume after child activity finish.
I think its an elegant solution.
You have a sequence of activities to start inside Parent Class Activty:
.....
startActiviy(intent1)
startActiviy(intent2)
startActiviy(intent3)
.....
It can be a loop like in my case
...
While( ) {
startActiviy(intent)
}
...
the solution is split the sequence in two:
the first startactivity(...) in the end
of the method of Parent activity , so the parent will pause after that;
the other startactivities(...) inside onResume method of Parent activity
in the end of onResume to make sure the Parent activity will stop due the
lifecycle.
You need a class variable to flow control as in loop , defined before call the first startActiviy
and increment it onResume method for each call of startActivity.
The last thing: you need to be able to define intents onResume method , some local variables of "method" should be in Class scope (defined on "method" and accessible in "onResume".
Class Parent Activity {
int r = 0; // control flow of child activities
// variables to define intent inside onResume
// the first call of startactivity goes here
private void method ( )){
.....
// just an example decreasing for each activity call,
r = 3; // call 4 times startActivity I guess, in this example
startActivity (intent) // in the to acitvity stop
}
// all subsequent startactivity goes here
protected void onResume(){
super.onResume();
....
if (r > 0) {
r--;
startActivity(intent);
}
}//end onResum
}//Class end
it works fine for me!
my code I created a method to call each Child activity "callinsertn"
public class TableActivity extends AppCompatActivity implements MainFragment.OnFragmentInteractionListener{
private Iterator< Pair<Integer, Integer> > iter; // control flow
private String[] startup_tcolnames; //need to define my intents
private Set<Pair<Integer, Integer>> selection;//need to define my intents
protected void onCreate ( ) {
....
....
mUpdateButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Toast.makeText(getApplicationContext(), "chamou callback na tabela col " + mrowid, Toast.LENGTH_LONG).show();
....
....
iter = selection.iterator();
if (iter.hasNext()) {
Pair<Integer, Integer> pair = iter.next();
callInsertn(pair);
}
}
}
}
protected void onResume(){
super.onResume();
MainFragment.class.getSimpleName();
if(iter !=null) {
if (iter.hasNext()) {
Pair<Integer, Integer> cell_coord = iter.next();
callInsertn(cell_coord);
} else {
//its over. all child activities ran
selection.clear();
selectedcell = false;
Log.i("DEBUG", "final final do update button");
iter =null;
}
}
}
//start my Child activity
//no flow control variables only accessing class vars to define intent
private void callInsertn(Pair<Integer, Integer> cell_coord){
mfrag_ind = cell_coord.second;
mrowid = (long) cell_coord.first;
Intent i = new Intent(TableActivity.this, Insertn.class);
i.putExtra("operation", "update");
// i.putExtra
// i.putExtra
startActivity (i);//start the Child activity
}
The Child activity doesnt even wonder who is its Parent , when it finishes the lifecycle returns to Parent so no communication from Child to Parent. All code goes on Parent Activity!
I hope you enjoy the solution!

int variable does not save with screen rotation

I put an imageview to load an image of a url, it is when I click on a button it passes to the next url, thus changing the image.
the problem is that when I rotate the screen it returns to the first image displayed.
I tried to do an if with incremented counter but it deletes the variable and returns to the first one again.
someone knows how to save the value of the "next" variable so when the screen rotates it keeps the value saved, or knows another way to keep the last image saved.
api picasso
code complete
private SmartImageView smartImage;
private Button btn;
private int proxima = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_redacao_nota_1000);
if (proxima == 0) {
smartImage = (SmartImageView) findViewById(R.id.meuSmartImage);
smartImage.setImageUrl("http://gabrielmartins70.000webhostapp.com/bao.png");
proxima++;
}
btn = (Button) findViewById(R.id.button18);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
if (proxima == 1) {
smartImage.setImageUrl("http://gabrielmartins70.000webhostapp.com/2.png");
}
}
});
}}
When your activity is recreated after it was previously destroyed, you can recover your saved state from the Bundle that the system passes your activity. Both the onCreate() and onRestoreInstanceState() callback methods receive the same Bundle that contains the instance state information.
Because the onCreate() method is called whether the system is creating a new instance of your activity or recreating a previous one, you must check whether the state Bundle is null before you attempt to read it. If it is null, then the system is creating a new instance of the activity, instead of restoring a previous one that was destroyed.
Save your int variable like following example does:
static final String STATE_USER = "user";
private String mUser;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Check whether we're recreating a previously destroyed instance
if (savedInstanceState != null) {
// Restore value of members from saved state
mUser = savedInstanceState.getString(STATE_USER);
} else {
// Probably initialize members with default values for a new instance
mUser = "NewUser";
}
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putString(STATE_USER, mUser);
// Always call the superclass so it can save the view hierarchy state
super.onSaveInstanceState(savedInstanceState);
}

Why do I get a NullPointerException here?

I have my code defined the way below. There are two crucial activities. Activity (1) shows some images in a ViewFlipper. It uses methods to load desired image directly. The onOptionsItemSelected() method fetches data from a menu defined within linked XML layout R.layout.browse. The other method, displaySelectedFlag(), gets a tag parameter passed from a different activity, let's call it activity (2).
Activity (1):
public class BrowserActivity extends AppCompatActivity implements SimpleGestureListener, View.OnClickListener {
public ViewFlipper vFlipper;
(...)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.browse);
vFlipper = (ViewFlipper) findViewById(R.id.viewFlipperBrowser);
(...)
} // onCreate() ends here
// this method below works fine:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
vFlipper.setDisplayedChild(item.getOrder());
return true;
}
// and this one doesn't:
public void displaySelectedFlag(int orderTag) {
vFlipper.setDisplayedChild(orderTag); // crashes here
}
}
Activity (2):
public class ListActivity extends Activity implements View.OnClickListener {
private BrowserActivity browserActivity = new BrowserActivity();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list);
ImageButton imageA = (ImageButton) findViewById(R.id.img_a);
imageA.setOnClickListener(this);
ImageButton imageB = (ImageButton) findViewById(R.id.img_b);
imageB.setOnClickListener(this);
}
public void displayImageInfo(View view) {
String tagValue = (String) view.getTag();
int tagId = Integer.parseInt(tagValue);
Intent intent = new Intent(this, BrowserActivity.class);
startActivity(intent);
browserActivity.displaySelectedImage(imageId);
}
#Override
public void onClick(View view) {
displayImageInfo(view);
}
}
As I checked, the method onClick() called in activity (2) fetches an ID of an ImageButton and passes it to activity (1). Unfortunately, I get a NullPointerException when calling the ViewFlipper (the line is marked in the code above, activity (1)).
Any idea why it happens?
You cannot reference one Activity from another activity. You must let the Android OS create the Activity object via the call to "startActivity". Allocating a local variable as an instance of an Activity doesn't actually mean anything (like your instantiation of the BrowserActivity). Apoorv's comment links to a decent article on the subject.
If you want to pass data from one Activity to another, you need to pass extras within the Intent's bundle. This post goes into detail: https://stackoverflow.com/a/819427/504252

How to get fragment tag or ID?

I have 2 fragments which are instantiated from the same class as the layouts are identical like so:
getSupportFragmentManager().beginTransaction().
add(R.id.leftContainer,new LeftFragmentClass(),"leftFrag").commit();
getSupportFragmentManager().beginTransaction().
add(R.id.rightFrag,new LeftFragmentClass(),"rightFrag").commit();
Within LeftFragmentClass there is a callback method which is called when the button within the fragment is pressed. After this some processing is done and data is displayed, however, right now the callback cannot distinguish which button was pressed. Is there a function which can return which fragment button was pressed?
For this type of condition i create a function inside fragment which will return me the instance of fragment and make the fragment constructor private something like:-
public class LeftFragmentClass extends Fragment{
private String fragmentTag = null;
public LeftFragmentClass(){}
public static LeftFragmentClass newInstance(String tag){
LeftFragmentClass mLeftFragmentClass = new LeftFragmentClass();
Bundle bundle = new Bundle();
bundle.putString ("tag",tag);
mLeftFragmentClass.setArgument(bundle);
return mLeftFragmentClass;
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
tag = getArguments().getString("tag")
}
}
So i used newInstance function to create instance of LeftFragmentClass and pass the tag to it which i m setting to Fragment argument using bundle and inside onCreate get bundle using getArguments and from it the tag value. Pass this tag value as one of the parameter to your callback method to identify which button was clicked.
So from activity for getting instance of LeftFragmentClass you can write as
LeftFragmentClass mLeftFragmentClassLeft = LeftFragmentClass.newInstance("left")
LeftFragmentClass mLeftFragmentClassRight = LeftFragmentClass.newInstance("Right")
==== Edit ====
keep the fragment class constructors always public don't make it private as i suggested above in my sample code. Making it private will cause application to crash with exception
java.lang.RuntimeException: Unable to start activity
ComponentInfo{MainActivity}:
android.support.v4.app.Fragment$InstantiationException: Unable to
instantiate fragment com.thatswhy.AppAlertDialog: make sure class name
exists, is public, and has an empty constructor that is public
Fragment fragment = getFragmentManager().findFragmentByTag("Tag")
As per provided the info you can do something like this, in your callback method pass the button object and check accordingly,
Some code snippet to explain the same :
Suppose your callback method is onButtonClick() then you can pass button object like :
public void onButtonClick(Button button){
// check here with button id
if(button.getId() == R.id.button1) {
} else if(button.getId() == R.id.button1) {
}
}
Hope this makes things clear..
The cleanest way of doing this I've seen is to create two distinct View.OnClickListener(s) in the Activity.
Have a getter() for each. public View.OnClickListener getLeftButtonPressed(), public View.OnClickListener getRightButtonPressed()
Then when you instantiate your left and right instances of your fragment, just pass in the appropriate 'View.OnClickListener' to the constructor of the Fragment. This not only reduces the code in the Fragment(s), it also centralizes the 'logic' of what to do when buttons are pressed.
public class MyActivity extends Activity {
// create the two listeners
View.OnClickListener leftButtonListener = new View.OnClickListener() {
public void onClick(View v) {
leftButtonClicked(v);
}
});
View.OnClickListener rightButtonListener = new View.OnClickListener() {
public void onClick(View v) {
rightButtonClicked(v);
}
});
// 2 getters
public View.OnClickListener getLeftListener() { return this.leftButtonListener; }
public View.OnClickListener getRightListener() { return this.rightButtonListener; }
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.content_layout_id);
}
// actual logic of what to do when each button is pressed.
private void leftButtonClicked(View v){
// some logic here
}
private void rightButtonClicked(View v){
// some logic here
}
}
This removes you later having to keep track of which button was pressed by making use of strings and if/then/else blocks, etc.
Add a parameter to interface callback function in your fragment;
interface Interfacecallback{
public void callbackfunction(int fragid);
}
Interfacecallback interfacecallback;
//in your button click
//pass 1 for fragment right
//pass 2 for fragment left
interfacecallback.callbackfunction(1);
You can check the fragment tag using this line of code if it exists:-
Fragment mapFragment = getFragmentManager().findFragmentByTag("MapRestaurantFragment");

How do I display public text in Activity A after it has been changed by Activity B

This probably demonstrates the most appalling lack of understanding of the activity life cycle, but please be sympathetic. I am ultimately going to want to invoke Activity B from Activity A a number of times, each time passing a different parameter to Activity B which is then responded to by the user and stores/sets various public variables. As a precursor to this, I just want to get my head round how Activity A sees the change to a public variable that Activity B has changed.
I have three very simple classes: Common.java that holds the public variables, the main activity MainActivity.java and the child activity Child.java. There is only one public variable right now; it's the string mess1 which is initialized to "***". All the code does at the moment is when mainbutton is clicked in MainActivity, it invokes Child. In Child, we immediately set mess1 to "Child here" then set the text in a Child-based TextView to mess1. On clicking the childbtn button in Child, we finish() the child activity (and of course the system returns us to MainActivity.
When this app is run, wee see the three stars displayed in MainActivity. When mainbutton is pressed we go to Child and see "Child here" displayed. When the childbtn is pressed, we return to MainActivity BUT, the three stars are still there although we know for sure that mess1 now holds "Child here".
My questions are:
1. Why, when we know mess1 has been changed, does MainActivity still display "***" on return from the Child activity?
2. What do I need to change in the code to get "Child here" to display?
Relevant code extracts follow. Thanks in advance for your help.
Common.java
public class Common
{
public static String mess1 = "***";
}
MainActivity.java
public class MainActivity extends AppCompatActivity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button mainbutton = (Button)findViewById(R.id.mainbutton);
TextView maintop = (TextView)findViewById(R.id.maintop);
mainbutton.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View view)
{
startActivity(new Intent(MainActivity.this, Child.class));
}
});
maintop.setText(Common.mess1);
}
Child.java
public class Child extends AppCompatActivity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_child);
TextView childtext = (TextView) findViewById(R.id.childtext);
final Button childbtn = (Button) findViewById(R.id.childbtn);
Common.mess1 = "Child here";
childtext.setText(Common.mess1);
childbtn.setOnClickListener
(new View.OnClickListener()
{public void onClick(View v)
{finish();
}
}
);
}
Likely you are moving back on the back stack history and you are resuming the previous activity that was placed in a paused state and therefore the onCreate isn't being called but the onResume (of the initial activity)..
Using global state this way isn't advised but this should work if you place the appropriate code in the onResume method.
You should set the text in onResume() of MainActivity. When you get back from Child.java onResume() (not onCreate()) is invoked and, since maintop's text is set in onCerate() only, nothing changes it on return.
protected void onResume() {
super.onResume();
maintop.setText(Common.mess1);
}
Reference: Activity Lifecycle and Implementing the lifecycle callbacks

Categories

Resources