UI Buttons must be recreated?


In trying to solve the issue with my implementation of the PAUSE button, I discovered some things:

onCreateView() is called when the fragment is re-attached.

This causes your code to recreate all of the UI buttons (nPlayButton, mStopButton, and mPauseButton), even though they are not destroyed.

So, I tried to add a check to only recreate them if the first button is null. This did not work. The app no longer responds to button presses. So, it looks like their onClickListener() methods are no longer connected to the code. This contradicts what you say in the following, but jibes with figure 14.3.

Then I tried to do the following, to restore the state of the PAUSE button, which worked:

boolean bPaused = false; CharSequence sPaused = null; if (mPauseButton != null) { bPaused = mPauseButton.isEnabled(); sPaused = mPauseButton.getText(); } mPauseButton = (Button) v.findViewById(R.id.hellomoon_pauseButton); mPauseButton.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { if (mPlayer.pause() == true) { mPauseButton.setText(R.string.hellomoon_resume); } else { mPauseButton.setText(R.string.hellomoon_pause); } } }); mPauseButton.setEnabled(bPaused); if (sPaused != null) { mPauseButton.setText(sPaused); }

So, you can use some of the state attributes of the previous widget but have to recreate them when you need callbacks? Those callbacks get linked to the Activity, not the Fragment itself?



It is true that the buttons are not garbage collected, which means that you can call getters and setters. They are, however, detached from the activity, which means that they don’t appear on the screen, and they don’t receive user events. So the original code is doing the right thing by reassigning those instance variables.

I would not recommend the technique you are using, because it uses the view object to store the state of your program. The view object should instead reflect the state of your program. In other words, you can have a method that asks whether you’re paused or not, and updates the button to reflect that state:

private void updatePauseButton() { if (mPaused) { mPauseButton.setText(R.string.hellomoon_resume); } else { mPauseButton.setText(R.string.hellomoon_pause); } }

And then you don’t need to worry about the state of the pause button at all.


Thanks! Are you saying that I shouldn’t use the mPauseButton in onCreateView(), if it is not null, to save the state of enabled/disabled and its current text and use that to set the state of the new Button? Or are you referring to what I am doing in the onClick() event for the button?


Yes to the first - don’t use the button to save state. Once you implement that you can clean up the click listener, too, but that wasn’t the main point.


OK, Thanks. What I did in Chapter 13 was have AudioPlayer keep track of its state with a boolean mPaused, since it has the callback for when the MediaPlayer ends the playback normally.

BTW, I couldn’t figure out how to get access to the Bundle in onPause() (instead of onSaveInstanceState(Bundle). I tried:

but it didn’t work, as if it didn’t give me the correct Bundle to use. If we want to put/get from the Bundle, must we do it within a method that passes it to the method?


EDIT: So, the [color=#BF0000]setRetainInstance(true);[/color] call does not work on View layout widgets then? If we were playing a video, it would have to be in a View widget, and this would not work?


1- Yes, that’s correct. You only have access to the bundle in those specific places. The proper solution to your problem is to pull out the values you need in onCreate or onCreateView, and then stash them in onSaveInstanceState.

2- Yes, setRetainInstance(true) will not save your view widgets. You’re correct to point out the issue with video - you must handle this the same way for VideoView as you do for WebView. Check our WebView chapter, or the “Cookie and window management” section in the Android WebView docs for more info: http://developer.android.com/reference/android/webkit/WebView.html