I have a Parent activity that sets a view on Resume based on some check like this :
public class AppLockActivity extends AppCompatActivity {
#BindView(R.id.btnSubmit)
Button submitButton;
private static final String TAG = "AppLockActivity";
private static TimeElapsed timeElapsedInstance;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
timeElapsedInstance = TimeElapsed.getInstance();
timeElapsedInstance.resetTime();
timeElapsedInstance.setStartTime();
}
#Override
protected void onResume() {
super.onResume();
//check if app has passed a time threshold
if(timeElapsedInstance.getStartTime() != 0){
timeElapsedInstance.setEndTime(Calendar.getInstance().getTimeInMillis());
long threshold = timeElapsedInstance.getEndTime()-timeElapsedInstance.getStartTime();
Log.d(TAG,"Threshold : "+threshold);
//Current timeout threshold set to 30s
if(threshold>30000){
setContentView(R.layout.activity_app_lock);
ButterKnife.bind(this);
}else{
}
}
}
#OnClick(R.id.btnSubmit) void onSubmit() {
//destroy current(Parent) view and show the previous
}
}
This activity is extended by other activities like MainAcitivty ,etc...
public class MainActivity extends AppLockActivity{
#Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_main);
}
}
When the app goes in background and is resumed the onResume function is called and based on the check the new View is set - R.layout.activity_app_lock. What I want to do is onClick of the submit button in this view I want to destroy the current view i.e (R.layout.activity_app_lock) and show the previous view that was in the child activity like MainActivity (R.layout.activiyt_main)...
Anybody have any idea how can I do this?
Thanks
You can actually call setContentView again with a different view. All your bindings need to be reset and your On_____Listeners need to be cleared or else you'll get a memory leak. Other than that, it'll be up and ready for you to go.
Though I suggest an alternative approach to changing the layout. Instead, create a new Activity that you start in replacement of the layout your currently submitting. Then, rather than worrying about leaks, you just call finish() on the lock Activity when the user submits. The effect would be the same and it would be more versatile (In my opinion).
Related
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
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
I do my android app , but I have a bug I don't know how to fix it.
My code is below :
This is my Main Activity :
public class MainActivity extends Activity {
private ImageView imgHot;
public final static String EXTRA_MESSAGE="com.cuonglm.KhoHinh.MESSAGE";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imgHot=(ImageView)findViewById(R.id.imageViewHot);
imgHot.setOnClickListener(toContentHot);
}
View.OnClickListener toContentHot=new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent content=new Intent(MainActivity.this,ContentActivity.class);
String signal="1";
content.putExtra(EXTRA_MESSAGE,signal);
startActivity(content);
}
};
And this is my second Activity :
public class ContentActivity extends Activity {
private TextView viewMessage;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent content=getIntent();
String messageReceive=content.getStringExtra(MainActivity.EXTRA_MESSAGE);
viewMessage=(TextView)findViewById(R.id.content_receive);
viewMessage.setText(messageReceive);
setContentView(R.layout.activity_content);
}
I want to click on the image on the Main Activity , string "1" or number "1" will send to the Second Activity via Intent and view on the TextView.
But my app will be close "Unfortunately..."
Thanks
Change to
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_content);
viewMessage=(TextView)findViewById(R.id.content_receive);
viewMessage.setText(messageReceive);
In your ContentActivity
findViewById looks for a view with the id for the current infalted layout. SO you need to set the content of your layout to the activity first and then initialize your views.
You are probably gettting NullPointerException coz your initialization fails.
You need to call setContentView() in your second Activity before trying to access any of the Views in that layout. Change it to
public class ContentActivity extends Activity {
private TextView viewMessage;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_content);
Intent content=getIntent();
String messageReceive=content.getStringExtra(MainActivity.EXTRA_MESSAGE);
viewMessage=(TextView)findViewById(R.id.content_receive);
viewMessage.setText(messageReceive);
}
If this doesn't fix your problem then please post your logcat so we can see the error. Also always post logcat in the future when your app crashes. They aren't always this easy to see.
Also, I'm not sure you understand how putExtra() works. It is a key, value pair so when you put EXTRA_MESSAGE as the key then that is what you would use to retrieve the value added in the second param. So the way you are doing it may work if the Activity gets destroyed but it looks really strange to me and probably not realy safe or efficient. I would change it to something like
content.putExtra("message",signal);
in your first Activity then get it with
String messageReceive = content.getStringExtra("message");
in your second Activity
You need to set the layout before trying to reference the Views associated with it.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent content=getIntent();
String messageReceive=content.getStringExtra(MainActivity.EXTRA_MESSAGE);
setContentView(R.layout.activity_content);
viewMessage=(TextView)findViewById(R.id.content_receive);
viewMessage.setText(messageReceive);
}
I want to hide a button in one of my activities if a global data structure does not exist (it's static, in public class Globals extends Application). Since I want to redraw the button whenever I resume the activity, but would rather not redraw the rest of the view, I put the initialization of the view in onCreate() and the button-hiding code in onResume():
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.myActivity);
}
#Override
protected void onResume() {
super.onResume();
if (Globals.datastructure == null) {
((Button) findViewById(R.id.myButton)).setVisibility(View.GONE);
}
}
When I allocate the data structure and then go back to the activity from a different activity, onResume is executed correctly but the button does not reappear.
The activity containing the button is probably not being recreated, which means that when you return to it from some other activity, the button is never being set (back) to be visible. You should probably change the onResume() to something like:
#Override
protected void onResume() {
super.onResume();
findViewById(R.id.myButton).setVisibility(Globals.datastructure == null ? View.GONE : View.VISIBLE);
}
So basically you just have to make sure that whenever Globals.datastructure != null, you also change the visibility appropriately. In other words: an else is required with the if.
I have a View that was created on runtime then I draw some canvas on that View(runtime) after that I rotated my screen.All data was gone(reset).So I put the some code in AndroidManifest.xml like this
android:configChanges="keyboardHidden|orientation"
in my <activity> then I put a #Override function
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
setContentView(R.layout.main);
LinearLayout layout = (LinearLayout) findViewById(R.id.myPaint);
layout.addView(mView);
}
but everything couldn't solved my problem.I want to keep my data from View(runtime) on every single rotation.
That's my onCreate function.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mView = new MyView(this);
setContentView(mView);
mView.requestFocus();
setContentView(R.layout.main);
LinearLayout layout = (LinearLayout) findViewById(R.id.myPaint);
layout.addView(mView);
}
You need to save and load the data you want to retain. Even though you're handling the screen rotation yourself when you modified the Manifest the way you did, you're still reloading the view yourself. Reread the reference document on Handling Runtime Changes. You need to store your data and reload it accordingly. Otherwise it will be lost when the application restarts or when you reload your ContentView.
http://developer.android.com/guide/topics/resources/runtime-changes.html
You could approach this a few ways.
I assume MyView is your own class which extends View. If so there are two methods which you may care to know, onSaveInstanceState() and onRestoreInstanceState(). When saving you create a parcelable that will contain enough data for you to re-render your view if it were to be destroyed and recreated.
class MyView extends View {
private String mString;
onDraw(Canvas v) { ... }
Parcelable onSaveInstanceState() {
Bundle b = new Bundle();
bundle.putString("STRING", mString);
return b;
void onRestoreInstanceState(Parcelable c) {
Bundle b = (Bundle) c;
mString = bundle.getString("STRING", null);
}
}
Activity has similar state saving mechanics allowed in onCreate and onSaveInstanceState() (inside Activity, not View in this case) which will allow the activity to reset the state of it's view to the state it desires.
This should solve most of your worries. If you are wanting to use the onConfigurationChanged method, then you should reclarify your question as it is not clear what the current behavior is that you aren't expecting in each situation (only using onConfigurationChanged, or only using onCreate, or using both, etc).
I've just used my data-class as singleton (java-pattern).
And it works fine.
--> Application is a Stop-Timer for Racing, where i can stop time from different opponents on the track, so i need the data for longer time, also if the view is repainted.
regz
public class Drivers {
// this is my singleton data-class for timing
private static Drivers instance = null;
public static Drivers getInstance() {
if (instance == null) {
instance = new Drivers();
}
return instance;
}
// some Timer-Definitions.......
}
Then in MainActivity:
// now the class is static, and will alive during application is running
private Drivers drivers = Drivers.getInstance();
#Override
public void onClick(View v) {
if (v == runButton1) {
drivers.startTimer1();
// do some other crazy stuff ...
}
}
// here i put out the current timing every second
private myUpdateFunction(){
time01.setText(drivers.getTimer1());
// update other timers etc ...
}