diff --git a/samples/ApiDemos/AndroidManifest.xml b/samples/ApiDemos/AndroidManifest.xml index bbeed2089..0d0023d49 100644 --- a/samples/ApiDemos/AndroidManifest.xml +++ b/samples/ApiDemos/AndroidManifest.xml @@ -209,14 +209,21 @@ - + - + + + + + + + + diff --git a/samples/ApiDemos/res/layout/fragment_retain_instance.xml b/samples/ApiDemos/res/layout/fragment_retain_instance.xml new file mode 100644 index 000000000..2b936f3d9 --- /dev/null +++ b/samples/ApiDemos/res/layout/fragment_retain_instance.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + diff --git a/samples/ApiDemos/res/layout/labeled_text_edit.xml b/samples/ApiDemos/res/layout/labeled_text_edit.xml new file mode 100644 index 000000000..27568afbb --- /dev/null +++ b/samples/ApiDemos/res/layout/labeled_text_edit.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + diff --git a/samples/ApiDemos/res/values/strings.xml b/samples/ApiDemos/res/values/strings.xml index 615d5cb3d..392e7b35d 100644 --- a/samples/ApiDemos/res/values/strings.xml +++ b/samples/ApiDemos/res/values/strings.xml @@ -83,11 +83,16 @@ Enter the text that will be used by the main activity. Press back to cancel. Apply - App/Fragment/Stack + App/Fragment/Stack Next - App/Fragment/Layout + App/Fragment/Layout + App/Fragment/Retain Instance + Current progress of retained fragment; + restarts if fragment is re-created. + Restart + App/Activity/Menu Open menu Close menu diff --git a/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java b/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java index 3163b89a7..fdf8e1d68 100644 --- a/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java +++ b/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java @@ -44,12 +44,32 @@ public class FragmentLayout extends Activity { } static class FirstFragment extends Fragment { + TextView mTextView; + // Explicit constructor needed for inflation. public FirstFragment() { } - public View onCreateView(LayoutInflater inflater, ViewGroup container) { - return inflater.inflate(R.layout.hello_world, container, false); + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.labeled_text_edit, container, false); + View tv = v.findViewById(R.id.msg); + ((TextView)tv).setText("The fragment saves and restores this text."); + + // Retrieve the text editor, and restore the last saved state if needed. + mTextView = (TextView)v.findViewById(R.id.saved); + if (savedInstanceState != null) { + mTextView.setText(savedInstanceState.getCharSequence("text")); + } + return v; + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + + // Remember the current text, to restore if we later restart. + outState.putCharSequence("text", mTextView.getText()); } } @@ -58,10 +78,17 @@ public class FragmentLayout extends Activity { public SecondFragment() { } - public View onCreateView(LayoutInflater inflater, ViewGroup container) { - View v = inflater.inflate(R.layout.hello_world, container, false); - View tv = v.findViewById(R.id.text); - ((TextView)tv).setText("Second Hello World!"); + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.labeled_text_edit, container, false); + View tv = v.findViewById(R.id.msg); + ((TextView)tv).setText("The TextView saves and restores this text."); + + // Retrieve the text editor and tell it to save and restore its state. + // Note that you will often set this in the layout XML, but since + // we are sharing our layout with the other fragment we will customize + // it here. + ((TextView)v.findViewById(R.id.saved)).setSaveEnabled(true); return v; } } diff --git a/samples/ApiDemos/src/com/example/android/apis/app/FragmentRetainInstance.java b/samples/ApiDemos/src/com/example/android/apis/app/FragmentRetainInstance.java new file mode 100644 index 000000000..da0d47bc4 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/apis/app/FragmentRetainInstance.java @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.apis.app; + +import com.example.android.apis.R; + +import android.app.Activity; +import android.app.Fragment; +import android.app.FragmentTransaction; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.ProgressBar; +import android.widget.TextView; + +/** + * This example shows how you can use a Fragment to easily propagate state + * (such as threads) across activity instances when an activity needs to be + * restarted due to, for example, a configuration change. This is a lot + * easier than using the raw Activity.onRetainNonConfiguratinInstance() API. + */ +public class FragmentRetainInstance extends Activity { + RetainedFragment mRetainedFragment; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.fragment_retain_instance); + + // Watch for button clicks. + Button button = (Button)findViewById(R.id.restart); + button.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + mRetainedFragment.restart(); + } + }); + + // Check to see if we retained the fragment. + mRetainedFragment = (RetainedFragment)findFragmentByTag("retained"); + + // If not retained (or first time running), we need to re-create it. + if (mRetainedFragment == null) { + mRetainedFragment = new RetainedFragment(); + openFragmentTransaction().add(mRetainedFragment, "retained").commit(); + } + } + + /** + * This is the Fragment implementation that will be retained across + * activity instances. It represents some ongoing work, here a thread + * we have that sits around incrementing a progress indicator. + */ + static class RetainedFragment extends Fragment { + ProgressBar mProgressBar; + int mPosition; + boolean mReady = false; + boolean mQuiting = false; + + /** + * This is the thread that will do our work. It sits in a loop running + * the progress up until it has reached the top, then stops and waits. + */ + final Thread mThread = new Thread() { + @Override + public void run() { + // We'll figure the real value out later. + int max = 10000; + + // This thread runs almost forever. + while (true) { + + // Update our shared state with the UI. + synchronized (this) { + // Our thread is stopped if the UI is not ready + // or it has completed its work. + while (!mReady || mPosition >= max) { + if (mQuiting) { + return; + } + try { + wait(); + } catch (InterruptedException e) { + } + } + + // Now update the progress. Note it is important that + // we touch the progress bar with the lock held, so it + // doesn't disappear on us. + mPosition++; + max = mProgressBar.getMax(); + mProgressBar.setProgress(mPosition); + } + + // Normally we would be doing some work, but put a kludge + // here to pretend like we are. + synchronized (this) { + try { + wait(50); + } catch (InterruptedException e) { + } + } + } + } + }; + + /** + * Fragment initialization. We way we want to be retained and + * start our thread. + */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Tell the framework to try to keep this fragment around + // during a configuration change. + setRetainInstance(true); + + // Start up the worker thread. + mThread.start(); + } + + /** + * This is called when the Fragment's Activity is ready to go, after + * its content view has been installed; it is called both after + * the initial fragment creation and after the fragment is re-attached + * to a new activity. + */ + @Override + public void onReady(Bundle savedInstanceState) { + super.onReady(savedInstanceState); + + // Retrieve the progress bar from the current activity. + mProgressBar = (ProgressBar)getActivity().findViewById( + R.id.progress_horizontal); + + // We are ready for our thread to go. + synchronized (mThread) { + mReady = true; + mThread.notify(); + } + } + + /** + * This is called when the fragment is going away. It is NOT called + * when the fragment is being propagated between activity instances. + */ + @Override + public void onDestroy() { + // Make the thread go away. + synchronized (mThread) { + mReady = false; + mQuiting = true; + mThread.notify(); + } + + super.onDestroy(); + } + + /** + * This is called right before the fragment is detached from its + * current activity instance. + */ + @Override + public void onDetach() { + // This fragment is being detached from its activity. We need + // to make sure its thread is not going to touch any activity + // state after returning from this function. + synchronized (mThread) { + mProgressBar = null; + mReady = false; + mThread.notify(); + } + + super.onDetach(); + } + + /** + * API for our UI to restart the progress thread. + */ + public void restart() { + synchronized (mThread) { + mPosition = 0; + mThread.notify(); + } + } + } +} diff --git a/samples/ApiDemos/src/com/example/android/apis/app/FragmentStack.java b/samples/ApiDemos/src/com/example/android/apis/app/FragmentStack.java index 586b4b91b..48d367e94 100644 --- a/samples/ApiDemos/src/com/example/android/apis/app/FragmentStack.java +++ b/samples/ApiDemos/src/com/example/android/apis/app/FragmentStack.java @@ -37,11 +37,6 @@ public class FragmentStack extends Activity { super.onCreate(savedInstanceState); setContentView(R.layout.fragment_stack); - // Add initial fragment. - Fragment newFragment = new CountingFragment(mStackLevel); - FragmentTransaction ft = openFragmentTransaction(); - ft.add(newFragment, R.id.simple_fragment).commit(); - // Watch for button clicks. Button button = (Button)findViewById(R.id.next); button.setOnClickListener(new OnClickListener() { @@ -49,8 +44,23 @@ public class FragmentStack extends Activity { addFragmentToStack(); } }); + + if (savedInstanceState == null) { + // Do first time initialization -- add initial fragment. + Fragment newFragment = new CountingFragment(mStackLevel); + FragmentTransaction ft = openFragmentTransaction(); + ft.add(newFragment, R.id.simple_fragment).commit(); + } else { + mStackLevel = savedInstanceState.getInt("level"); + } } + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putInt("level", mStackLevel); + } + void addFragmentToStack() { mStackLevel++; Fragment newFragment = new CountingFragment(mStackLevel); @@ -61,14 +71,33 @@ public class FragmentStack extends Activity { ft.commit(); } - class CountingFragment extends Fragment { - final int mNum; + static class CountingFragment extends Fragment { + int mNum; + + public CountingFragment() { + mNum = 0; + } public CountingFragment(int num) { mNum = num; } - public View onCreateView(LayoutInflater inflater, ViewGroup container) { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (savedInstanceState != null) { + mNum = savedInstanceState.getInt("num"); + } + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putInt("num", mNum); + } + + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { View v = inflater.inflate(R.layout.hello_world, container, false); View tv = v.findViewById(R.id.text); ((TextView)tv).setText("Fragment #" + mNum);