Update fragment API demos for new fragment APIs:

- Correctly work with state saving and restoring.
- Add new sample showing a fragment that is retained across activities.

Change-Id: I7b1e02135177e69e9fff23fcc55e86d93a75c7e0
This commit is contained in:
Dianne Hackborn
2010-05-12 18:58:18 -07:00
parent 5b4766d4a2
commit edb8628b41
7 changed files with 373 additions and 18 deletions

View File

@@ -209,14 +209,21 @@
<!-- Fragment Samples --> <!-- Fragment Samples -->
<activity android:name=".app.FragmentStack" android:label="@string/simple_fragment"> <activity android:name=".app.FragmentStack" android:label="@string/fragment_simple">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.SAMPLE_CODE" /> <category android:name="android.intent.category.SAMPLE_CODE" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".app.FragmentLayout" android:label="@string/layout_fragment"> <activity android:name=".app.FragmentLayout" android:label="@string/fragment_layout">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.SAMPLE_CODE" />
</intent-filter>
</activity>
<activity android:name=".app.FragmentRetainInstance" android:label="@string/fragment_retain_instance">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.SAMPLE_CODE" /> <category android:name="android.intent.category.SAMPLE_CODE" />

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2007 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/fragment_retain_instance_msg" />
<ProgressBar android:id="@+id/progress_horizontal"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="200dip"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:padding="6dp"
android:max="500" />
<Button android:id="@+id/restart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/restart">
<requestFocus />
</Button>
</LinearLayout>

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<!-- Content for a fragment with a text editor. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:padding="4dip"
android:layout_width="match_parent" android:layout_height="wrap_content">
<TextView android:id="@+id/msg"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:layout_weight="0"
android:paddingBottom="4dip" />
<EditText android:id="@+id/saved"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/green"
android:text="@string/initial_text"
android:freezesText="true">
<requestFocus />
</EditText>
</LinearLayout>

View File

@@ -83,11 +83,16 @@
<string name="redirect_getter">Enter the text that will be used by the main activity. Press back to cancel.</string> <string name="redirect_getter">Enter the text that will be used by the main activity. Press back to cancel.</string>
<string name="apply">Apply</string> <string name="apply">Apply</string>
<string name="simple_fragment">App/Fragment/Stack</string> <string name="fragment_simple">App/Fragment/Stack</string>
<string name="next">Next</string> <string name="next">Next</string>
<string name="layout_fragment">App/Fragment/Layout</string> <string name="fragment_layout">App/Fragment/Layout</string>
<string name="fragment_retain_instance">App/Fragment/Retain Instance</string>
<string name="fragment_retain_instance_msg">Current progress of retained fragment;
restarts if fragment is re-created.</string>
<string name="restart">Restart</string>
<string name="activity_menu">App/Activity/Menu</string> <string name="activity_menu">App/Activity/Menu</string>
<string name="open_menu">Open menu</string> <string name="open_menu">Open menu</string>
<string name="close_menu">Close menu</string> <string name="close_menu">Close menu</string>

View File

@@ -44,12 +44,32 @@ public class FragmentLayout extends Activity {
} }
static class FirstFragment extends Fragment { static class FirstFragment extends Fragment {
TextView mTextView;
// Explicit constructor needed for inflation. // Explicit constructor needed for inflation.
public FirstFragment() { public FirstFragment() {
} }
public View onCreateView(LayoutInflater inflater, ViewGroup container) { public View onCreateView(LayoutInflater inflater, ViewGroup container,
return inflater.inflate(R.layout.hello_world, container, false); 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 SecondFragment() {
} }
public View onCreateView(LayoutInflater inflater, ViewGroup container) { public View onCreateView(LayoutInflater inflater, ViewGroup container,
View v = inflater.inflate(R.layout.hello_world, container, false); Bundle savedInstanceState) {
View tv = v.findViewById(R.id.text); View v = inflater.inflate(R.layout.labeled_text_edit, container, false);
((TextView)tv).setText("Second Hello World!"); 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; return v;
} }
} }

View File

@@ -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();
}
}
}
}

View File

@@ -37,11 +37,6 @@ public class FragmentStack extends Activity {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_stack); 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. // Watch for button clicks.
Button button = (Button)findViewById(R.id.next); Button button = (Button)findViewById(R.id.next);
button.setOnClickListener(new OnClickListener() { button.setOnClickListener(new OnClickListener() {
@@ -49,8 +44,23 @@ public class FragmentStack extends Activity {
addFragmentToStack(); 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() { void addFragmentToStack() {
mStackLevel++; mStackLevel++;
Fragment newFragment = new CountingFragment(mStackLevel); Fragment newFragment = new CountingFragment(mStackLevel);
@@ -61,14 +71,33 @@ public class FragmentStack extends Activity {
ft.commit(); ft.commit();
} }
class CountingFragment extends Fragment { static class CountingFragment extends Fragment {
final int mNum; int mNum;
public CountingFragment() {
mNum = 0;
}
public CountingFragment(int num) { public CountingFragment(int num) {
mNum = 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 v = inflater.inflate(R.layout.hello_world, container, false);
View tv = v.findViewById(R.id.text); View tv = v.findViewById(R.id.text);
((TextView)tv).setText("Fragment #" + mNum); ((TextView)tv).setText("Fragment #" + mNum);