As the title says, I'm having trouble when updating the contents on my Textview. It all works well, until I change the orientation.
Here is the onCreate of the Activity that holds the Textview:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setContentView(R.layout.transacao);
screenTextView = (TextView) findViewById(R.id.lblStatusTrn);
screenTextView.setText(screenMessage);
//...
}
And here is the code that update the Textview:
private void setScreenMessage(String message) {
screenMessage = message;
screenTextView = (TextView) findViewById(R.id.lblStatusTrn);
screenTextView.setText(screenMessage);
}
Even though I keep getting no errors, the Textview doesn't update after I call setScreenMessage. This all happens after the orientation change. If I don't change the orientation, everything works fine.
I already looked into a number of similar questions here, but none of them seemed to work for me.
EDIT 1: I don't know if it is relevant, but the setScreenMessage is called from another thread, through an Handler.
Code for the Handler:
public void handleMessage(android.os.Message message) {
//...
// This command calls setScreenMessage
command.execute();
}
EDIT 2: Apparently, using Broadcast did the trick. But that makes me think if it is possible that I had an valid reference to an Activity, even though it is not this reference that is been shown. If that is the case, I might have a useless reference stealing away my memory. Is it possible? Is there a way to check this?
Related
Is there any way to call a listener manually from code?
More background information: I use a Spinner and a DatePicker. With the Spinner you can choose a reason for staying at home (maybe your ill, maybe you have vacation) and with the DatePicker you can choose the date how long you will not be available. With these two pieces of information I build up a string for a TextView and show the same data in a compact way. The building process for the string is set by some listeners which recognize changes on one of the two controls and build and set up the new string.
If I start the program and read some data from a server the string will not be build (clearly because nothing changed and no listener will called).
The workaround is to build the string in my own onLoaddata() method. But I think it would be smoother way to call one listener to build the string for me. I also can "call" a listener if I just do some fake .updateDate but I don't think it’s a good idea to create useless calls...
Maybe someone of you have a good hint for me?
Use the following pattern:
public class YourActivity extends Activity implements OnClickListener {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
// Some initialization here
findViewById(R.id.some_button).setOnClickListener(this);
...
// Here you want to update your view
updateTextView();
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.some_button:
// Here you also want to update your view
updateTextView();
break;
...
}
}
private void updateTextView() {
// Here you update your view
...
}
}
Right now, every time I want to access a widget I use:
mEditText1 = (EditText) findViewById(R.id.edittext1);
And then perform the action I would like to on the widget.
However, I am wondering if I am able to store a reference to a widget and use that reference multiple times in different methods within the activity, without having to always call findViewById...
I've tried the following:
public class MyActivity extends Activity {
public static String ACTIVITY_NAME = "MyActivity";
EditText mEditText1;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.v(this.APP_NAME, "In " + ACTIVITY_NAME);
mEditText1 = (EditText) findViewById(R.id.edittext1);
setContentView(R.layout.main);
prefillFieldsIfNecessary();
}
private void prefillFieldsIfNecessary(){
if(AppPreferences.checkExistence(MyActivity.this, AppPreferences.Name)) {
mEditText1.setText(AppPreference.Name);
}
However, my app just crashes and I get a NullPointerException, which I know speaks to the mEditText1.setText() line, because when I comment that out, my app runs fine.
Asha, this should work and is completely valid. How do you know it's not working? Are you getting an error? Is textToSave not being populated appropriately?
Check if you're declaring "EditText mEditText1;" after your import statements and before your class declaration. Also specify an access modifier, not specifying an access modifier to a class field is not good. It would be nice if you can post a full example of your Activity, so that we can see if you're declaring "EditText mEditText1;" as a class member or variable.
Edit:
Move
setContentView(R.layout.main);
right after
super.onCreate(savedInstanceState);
and you'll be fine. Generally try to have those 2 lines
on top of your onCreate() and do all logic, after those 2 lines.
public class Menu extends Activity {
/** Called when the activity is first created. */
public void onCreate(Bundle icicle) {
//myIntent.setClassName("hello.World", "hello.World.mybuttonclick");
// myIntent.putExtra("com.android.samples.SpecialValue", "Hello, Joe!"); // key/value pair, where key needs current package prefix.
//startActivity(myIntent);
//Button myButton = (Button) findViewById(R.id.my_button);
super.onCreate(icicle);
setContentView(R.layout.main);
}
public void updateLayout(){
Intent myIntent = new Intent(Menu.this, mybuttonclick.class);
startActivity(myIntent);
// TextView sayHello = (TextView) findViewById(R.id.Hello);
}
}
Hey guys, I am a new android java student and we have to develop a simple hello world app.. I am finding some difficulty getting my onClick() activity to work, using android:Onclick in xml.. what i am trying to do is change the content view do display a simply a different layout and saying hello.. i am using setContentLayout to do this, every time i click said button tho the android app crashes out.. am i doing something wrong?
regards,
Stefan
When you set a click listener in xml you must have the method defined inside the activity you are clicking in. Lets say you set the onClick in xml to be "buttonClicked", you must create a method looking exactly like the one below.
public void buttonClicked(View view)
{
//Your code here
}
The thing to notice is that the method is a public void with only a single parameter of type View. XML defined click listeners must be like this to work. The view object in the example above is the view that was clicked.
You update layout function needs to read
public void updateLayout(View view)
In response to your question, there are a number of things that are issues causing the complication that you described. Let it first be said, that you don't have to do anything any particular way, provided that you make concessions for certain things. Android is a very flexible platform and Java, as an OOP language allows you to do things that many non OOP languages do not.
Whenever you create a "clickable" item, like a Button, if you want to have your program respond, you must have something "listen" to it. This is known as a Listener. In your case, you are looking for an OnClickListener. The OnClickListener does not have to be a part of the Activity necessarily. It just has to be a class that implements View.OnClickListener. Then, you have tell the setOnClickListener() method of the Button who its listener is. The following example shows what is necessary without your declaration in XML (but it is important).
class Menu extends Activity implements View.OnClickListener
{
public void onCreate(Bundle icicle)
{ setContentView(R.layout.main);
Button btn = (Button)findViewById(R.id.BUTTON_ID_AS_DEFINED_BY_YOUR_XML);
btn.setOnClickListener(this);
}
public void onClick(View view)
{ int id = view.getId();
if (id == R.id.BUTTON_ID_AS_DEFINED_BY_YOUR_XML)
updateLayout()//Do your Click event here
}
public void updateLayout()
{ //updateLayout code...
}
}
Something that needs to be noted is the OnClick() above. Every OnClickListener must use the same signature as theOnClick() That means itmust have the same return and same arguments even if it has a different name. For what you are trying to do (in XML), you have set your android:OnClick to updateLayout. This means that `updateLayout() must be declared as follows:
public void updateLayout(View view)
Now, getting the update method to actually work: While you provide your code, we don't actually know what errors you are getting. It is always much easier to solve a problem if we have a copy of the Logcat output that includes the error you are receiving. Once, we have that we can target your error specifically and I can edit my answer to include what you may additionally need.
FuzzicalLogic
I've been working with AsyncTasks in Android and I am dealing with an issue.
Take a simple example, an Activity with one AsyncTask. The task on the background does not do anything spectacular, it just sleeps for 8 seconds.
At the end of the AsyncTask in the onPostExecute() method I am just setting a button visibility status to View.VISIBLE, only to verify my results.
Now, this works great until the user decides to change his phones orientation while the AsyncTask is working (within the 8 second sleep window).
I understand the Android activity life cycle and I know the activity gets destroyed and recreated.
This is where the problem comes in. The AsyncTask is referring to a button and apparently holds a reference to the context that started the AsyncTask in the first place.
I would expect, that this old context (since the user caused an orientation change) to either become null and the AsyncTask to throw an NPE for the reference to the button it is trying to make visible.
Instead, no NPE is thrown, the AsyncTask thinks that the button reference is not null, sets it to visible. The result? Nothing is happening on the screen!
Update: I have tackled this by keeping a WeakReference to the activity and switching when a configuration change happens. This is cumbersome.
Here's the code:
public class Main extends Activity {
private Button mButton = null;
private Button mTestButton = null;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mButton = (Button) findViewById(R.id.btnStart);
mButton.setOnClickListener(new OnClickListener () {
#Override
public void onClick(View v) {
new taskDoSomething().execute(0l);
}
});
mTestButton = (Button) findViewById(R.id.btnTest);
}
private class TaskDoSomething extends AsyncTask<Long, Integer, Integer>
{
#Override
protected Integer doInBackground(Long... params) {
Log.i("LOGGER", "Starting...");
try {
Thread.sleep(8000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 0;
}
#Override
protected void onPostExecute(Integer result) {
Log.i("LOGGER", "...Done");
mTestButton.setVisibility(View.VISIBLE);
}
}
}
Try executing it and while the AsyncTask is working change your phones orientation.
AsyncTask is not designed to be reused once an Activity has been torn down and restarted. The internal Handler object becomes stale, just like you stated. In the Shelves example by Romain Guy, he simple cancels any currently running AsyncTask's and then restarts new ones post-orientation change.
It is possible to hand off your Thread to the new Activity, but it adds a lot of plumbing. There is no generally agreed on way to do this, but you can read about my method here : http://foo.jasonhudgins.com/2010/03/simple-progressbar-tutorial.html
If you only need a context and won't use it for ui stuff you can simply pass the ApplicationContext to your AsyncTask.You often need the context for system resources, for example.
Don't try to update the UI from an AsyncTask and try to avoid handling configuration changes yourself as it can get messy. In order to update the UI you could register a Broadcast receiver and send a Broadcast.
You should also have the AsyncTask as a separate public class from the activity as mentioned above, it makes testing a lot easier. Unfortunately Android programming often reinforces bad practices and the official examples are not helping.
This is the type of thing that leads me to always prevent my Activity from being destroyed/recreated on orientation change.
To do so add this to your <Activity> tag in your manifest file:
android:configChanges="orientation|keyboardHidden"
And override onConfigurationChanged in your Activity class:
#Override
public void onConfigurationChanged(final Configuration newConfig)
{
// Ignore orientation change to keep activity from restarting
super.onConfigurationChanged(newConfig);
}
To avoid this you can use the answer givin here: https://stackoverflow.com/a/2124731/327011
But if you need to destroy the activity (different layouts for portrait and landscape) you can make the AsyncTask a public class (Read here why it shouldn't be private Android: AsyncTask recommendations: private class or public class?) and then create a method setActivity to set the reference to the current activity whenever it is destroyed/created.
You can see an example here: Android AsyncTask in external class
I am using Intents to switch between activities in my Android app. I am putting data in the Intent for use in the next activity. When I switch the phone between landscape and portrait modes, the values passed from the intent are lost and I get a NullPointerException.
Can someone please tell me what could be wrong.
There's a lot of code to post it entirely. But if someone needs to look at specific parts of code, I can post it here.
Edit
I solved the issue of state not being saved. But another problem I faced is that none of the buttons on the screen work after the orientation has been changed. On button press, I get this warning in LogCat
02-25 23:07:49.190: WARN/WindowManager(58): No window to dispatch pointer action 0
Please help.
When you switch orientation the activity is recreated and onCreate is recalled so you have to use the bundle to save your current state and restore after an orientation change. You can see this in action if you have just an app with a TextView and you enter text and change orientation. If you bundle your state for onCreate you can curb this. This is probably also why you have a NullPointer after the orientation changes. It is annoying as all hell but something we have to live with.
This link on a series of orientation tutorials and this first one in particular should help you understand exactly what is going on and how to successfully maintain your current state.
Update: There is also a post on SO Activity restart on rotation Android that deals with almost the same thing.
Edit for follow up question:
Did you re-attach your click handlers after the orientation change?
Write this in your manifest file..in which activity you want this--
android:configChanges="orientation|keyboardHidden"
Edited: Use this one for new APIs versions--
android:configChanges="orientation|keyboardHidden|screenSize"
Definitely it will work..
Try this:
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(SOME_KEY, "blah blah blah");
}
#Override
public void onCreate(Bundle savedInstanceState) {
...
somevalue = savedInstanceState.getString(SOME_KEY);
...
}
It possible to declare an attribute android:configChanges with the value of "orientation", this will prevent the activity from being restarted. Instead, the activity remains running and its onConfigurationChanged() method is called.
Declare < android:configChanges="orientation|keyboardHidden"/> in your manifest. This allows you manage the change of Orientation/Keyboard visibility by yourself. Of course, You don't need to override the callback method for manage it.
Hi I also encountered this problem.
what fixed it for me was:
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save the user's current game state
savedInstanceState.putString("Username", mUsername);
savedInstanceState.putString("Password", mPassword);
savedInstanceState.putString("UserID", mUserID);
// Always call the superclass so it can save the view hierarchy state
super.onSaveInstanceState(savedInstanceState);
}
and then in onCreate():
if (savedInstanceState == null) {
Bundle extras = getIntent().getExtras();
if(extras == null) {
mUsername = "?";
mPassword = "?";
mUserID = "?";
} else {
mUsername = extras.getString("Username");
mPassword = extras.getString("Password");
mUserID = extras.getString("UserID");
}
} else {
mUsername = (String) savedInstanceState.getSerializable("Username");
mPassword = (String) savedInstanceState.getSerializable("Password");
mUserID = (String) savedInstanceState.getSerializable("UserID");
}
then you can be sure the objects are not null.