• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

development


Commit MetaInfo

Révisionbe6b6b494f094eea0abcd83eb8770bc4b9f7e34e (tree)
l'heure2011-06-14 08:27:17
AuteurDianne Hackborn <hackbod@goog...>
CommiterDianne Hackborn

Message de Log

New API demos showing use of tabs with fragments.

Also various cleanup in other demos.

Change-Id: I4f5669117e28312bcd4b28795c5eca5f4ab6dcfb

Change Summary

Modification

--- a/samples/ApiDemos/AndroidManifest.xml
+++ b/samples/ApiDemos/AndroidManifest.xml
@@ -38,7 +38,10 @@
3838
3939 <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="11" />
4040
41- <!-- This app has not been optimized for large screens. -->
41+ <!-- The smallest screen this app works on is a phone. The app will
42+ scale its UI to larger screens but doesn't make good use of them
43+ so allow the compatibility mode button to be shown (mostly because
44+ this is just convenient for testing). -->
4245 <supports-screens android:requiresSmallestWidthDp="320"
4346 android:compatibleWidthLimitDp="480" />
4447
@@ -361,6 +364,15 @@
361364 </intent-filter>
362365 </activity>
363366
367+ <activity android:name=".app.FragmentTabs"
368+ android:label="@string/fragment_tabs"
369+ android:enabled="@bool/atLeastHoneycomb">
370+ <intent-filter>
371+ <action android:name="android.intent.action.MAIN" />
372+ <category android:name="android.intent.category.SAMPLE_CODE" />
373+ </intent-filter>
374+ </activity>
375+
364376 <!-- Loader Samples -->
365377
366378 <activity android:name=".app.LoaderCursor"
--- a/samples/ApiDemos/res/values/strings.xml
+++ b/samples/ApiDemos/res/values/strings.xml
@@ -150,6 +150,8 @@
150150 <string name="fragment_stack">App/Fragment/Stack</string>
151151 <string name="new_fragment">New fragment</string>
152152
153+ <string name="fragment_tabs">App/Fragment/Tabs</string>
154+
153155 <string name="loader_cursor">App/Loader/Cursor</string>
154156
155157 <string name="loader_custom">App/Loader/Custom</string>
--- a/samples/ApiDemos/src/com/example/android/apis/app/ActionBarTabs.java
+++ b/samples/ApiDemos/src/com/example/android/apis/app/ActionBarTabs.java
@@ -78,6 +78,11 @@ public class ActionBarTabs extends Activity {
7878 * to it, it will be committed at the end of the full tab switch operation.
7979 * This lets tab switches be atomic without the app needing to track
8080 * the interactions between different tabs.
81+ *
82+ * NOTE: This is a very simple implementation that does not retain
83+ * fragment state of the non-visible tabs across activity instances.
84+ * Look at the FragmentTabs example for how to do a more complete
85+ * implementation.
8186 */
8287 private class TabListener implements ActionBar.TabListener {
8388 private TabContentFragment mFragment;
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/app/FragmentTabs.java
@@ -0,0 +1,116 @@
1+/*
2+ * Copyright (C) 2011 The Android Open Source Project
3+ *
4+ * Licensed under the Apache License, Version 2.0 (the "License");
5+ * you may not use this file except in compliance with the License.
6+ * You may obtain a copy of the License at
7+ *
8+ * http://www.apache.org/licenses/LICENSE-2.0
9+ *
10+ * Unless required by applicable law or agreed to in writing, software
11+ * distributed under the License is distributed on an "AS IS" BASIS,
12+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ * See the License for the specific language governing permissions and
14+ * limitations under the License.
15+ */
16+package com.example.android.apis.app;
17+
18+import com.example.android.apis.R;
19+
20+import android.app.ActionBar;
21+import android.app.ActionBar.Tab;
22+import android.app.Activity;
23+import android.app.Fragment;
24+import android.app.FragmentTransaction;
25+import android.os.Bundle;
26+import android.widget.Toast;
27+
28+/**
29+ * This demonstrates the use of action bar tabs and how they interact
30+ * with other action bar features.
31+ */
32+public class FragmentTabs extends Activity {
33+ @Override
34+ protected void onCreate(Bundle savedInstanceState) {
35+ super.onCreate(savedInstanceState);
36+
37+ final ActionBar bar = getActionBar();
38+ bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
39+ bar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE);
40+
41+ bar.addTab(bar.newTab()
42+ .setText("Simple")
43+ .setTabListener(new TabListener<FragmentStack.CountingFragment>(
44+ this, "simple", FragmentStack.CountingFragment.class)));
45+ bar.addTab(bar.newTab()
46+ .setText("Contacts")
47+ .setTabListener(new TabListener<LoaderCursor.CursorLoaderListFragment>(
48+ this, "contacts", LoaderCursor.CursorLoaderListFragment.class)));
49+ bar.addTab(bar.newTab()
50+ .setText("Apps")
51+ .setTabListener(new TabListener<LoaderCustom.AppListFragment>(
52+ this, "apps", LoaderCustom.AppListFragment.class)));
53+ bar.addTab(bar.newTab()
54+ .setText("Throttle")
55+ .setTabListener(new TabListener<LoaderThrottle.ThrottledLoaderListFragment>(
56+ this, "throttle", LoaderThrottle.ThrottledLoaderListFragment.class)));
57+
58+ if (savedInstanceState != null) {
59+ bar.setSelectedNavigationItem(savedInstanceState.getInt("tab", 0));
60+ }
61+ }
62+
63+ @Override
64+ protected void onSaveInstanceState(Bundle outState) {
65+ super.onSaveInstanceState(outState);
66+ outState.putInt("tab", getActionBar().getSelectedNavigationIndex());
67+ }
68+
69+ public static class TabListener<T extends Fragment> implements ActionBar.TabListener {
70+ private final Activity mActivity;
71+ private final String mTag;
72+ private final Class<T> mClass;
73+ private final Bundle mArgs;
74+ private Fragment mFragment;
75+
76+ public TabListener(Activity activity, String tag, Class<T> clz) {
77+ this(activity, tag, clz, null);
78+ }
79+
80+ public TabListener(Activity activity, String tag, Class<T> clz, Bundle args) {
81+ mActivity = activity;
82+ mTag = tag;
83+ mClass = clz;
84+ mArgs = args;
85+
86+ // Check to see if we already have a fragment for this tab, probably
87+ // from a previously saved state. If so, deactivate it, because our
88+ // initial state is that a tab isn't shown.
89+ mFragment = mActivity.getFragmentManager().findFragmentByTag(mTag);
90+ if (mFragment != null && !mFragment.isDetached()) {
91+ FragmentTransaction ft = mActivity.getFragmentManager().beginTransaction();
92+ ft.detach(mFragment);
93+ ft.commit();
94+ }
95+ }
96+
97+ public void onTabSelected(Tab tab, FragmentTransaction ft) {
98+ if (mFragment == null) {
99+ mFragment = Fragment.instantiate(mActivity, mClass.getName(), mArgs);
100+ ft.add(android.R.id.content, mFragment, mTag);
101+ } else {
102+ ft.attach(mFragment);
103+ }
104+ }
105+
106+ public void onTabUnselected(Tab tab, FragmentTransaction ft) {
107+ if (mFragment != null) {
108+ ft.detach(mFragment);
109+ }
110+ }
111+
112+ public void onTabReselected(Tab tab, FragmentTransaction ft) {
113+ Toast.makeText(mActivity, "Reselected!", Toast.LENGTH_SHORT).show();
114+ }
115+ }
116+}
--- a/samples/ApiDemos/src/com/example/android/apis/app/LoaderCursor.java
+++ b/samples/ApiDemos/src/com/example/android/apis/app/LoaderCursor.java
@@ -159,7 +159,11 @@ public class LoaderCursor extends Activity {
159159 mAdapter.swapCursor(data);
160160
161161 // The list should now be shown.
162- setListShown(true);
162+ if (isResumed()) {
163+ setListShown(true);
164+ } else {
165+ setListShownNoAnimation(true);
166+ }
163167 }
164168
165169 public void onLoaderReset(Loader<Cursor> loader) {
--- a/samples/ApiDemos/src/com/example/android/apis/app/LoaderCustom.java
+++ b/samples/ApiDemos/src/com/example/android/apis/app/LoaderCustom.java
@@ -1,5 +1,5 @@
11 /*
2- * Copyright (C) 2010 The Android Open Source Project
2+ * Copyright (C) 2011 The Android Open Source Project
33 *
44 * Licensed under the Apache License, Version 2.0 (the "License");
55 * you may not use this file except in compliance with the License.
@@ -466,7 +466,11 @@ public class LoaderCustom extends Activity {
466466 mAdapter.setData(data);
467467
468468 // The list should now be shown.
469- setListShown(true);
469+ if (isResumed()) {
470+ setListShown(true);
471+ } else {
472+ setListShownNoAnimation(true);
473+ }
470474 }
471475
472476 @Override public void onLoaderReset(Loader<List<AppEntry>> loader) {
--- a/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.java
+++ b/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.java
@@ -409,6 +409,9 @@ public class LoaderThrottle extends Activity {
409409 new int[] { android.R.id.text1 }, 0);
410410 setListAdapter(mAdapter);
411411
412+ // Start out with a progress indicator.
413+ setListShown(false);
414+
412415 // Prepare the loader. Either re-connect with an existing one,
413416 // or start a new one.
414417 getLoaderManager().initLoader(0, null, this);
@@ -492,6 +495,13 @@ public class LoaderThrottle extends Activity {
492495
493496 public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
494497 mAdapter.swapCursor(data);
498+
499+ // The list should now be shown.
500+ if (isResumed()) {
501+ setListShown(true);
502+ } else {
503+ setListShownNoAnimation(true);
504+ }
495505 }
496506
497507 public void onLoaderReset(Loader<Cursor> loader) {
--- a/samples/ApiDemos/src/com/example/android/apis/app/_index.html
+++ b/samples/ApiDemos/src/com/example/android/apis/app/_index.html
@@ -134,6 +134,10 @@
134134 <dd>Demonstrates creating a stack of Fragment instances similar to the
135135 traditional stack of activities.</dd>
136136
137+ <dt><a href="FragmentTabs.html">Fragment Tabs</a></dt>
138+ <dd>Demonstrates implementing ActionBar tabs by switching between
139+ Fragments.</dd>
140+
137141 </dl>
138142
139143
@@ -145,9 +149,10 @@ menu. This demo is for informative purposes only; see Usage for an example of us
145149 Action Bar in a more idiomatic manner.</dd>
146150 <dt><a href="ActionBarTabs.html">Action Bar Tabs</a></dt>
147151 <dd>Demonstrates the use of Action Bar tabs and how they interact with other action bar
148-features.</dd>
152+features. Also see the <a href="FragmentTabs.html">Fragment Tabs</a> for a more
153+complete example of how to switch between fragments.</dd>
149154 <dt><a href="ActionBarUsage.html">Action Bar Usage</a></dt>
150- <dd>Demonstrates imple usage of the Action Bar, including a SearchView as an action item. The
155+ <dd>Demonstrates simple usage of the Action Bar, including a SearchView as an action item. The
151156 default Honeycomb theme includes the Action Bar by default and a menu resource is used to populate
152157 the menu data itself. If you'd like to see how these things work under the hood, see
153158 Mechanics.</dd>
@@ -162,6 +167,10 @@ Mechanics.</dd>
162167 <dd>Demonstrates use of LoaderManager to perform a query for a Cursor that
163168 populates a ListFragment.</dd>
164169
170+ <dt><a href="LoaderCustom.html">Loader Custom</a></dt>
171+ <dd>Demonstrates implementation and use of a custom Loader class. The
172+ custom class here "loads" the currently installed applications.</dd>
173+
165174 <dt><a href="LoaderThrottle.html">Loader Throttle</a></dt>
166175 <dd>Complete end-to-end demonstration of a simple content provider that
167176 populates data in a list through a cursor loader. The UI allows the list
--- a/samples/Support13Demos/AndroidManifest.xml
+++ b/samples/Support13Demos/AndroidManifest.xml
@@ -26,7 +26,10 @@
2626
2727 <uses-sdk android:minSdkVersion="13" />
2828
29- <!-- This app has not been optimized for large screens. -->
29+ <!-- The smallest screen this app works on is a phone. The app will
30+ scale its UI to larger screens but doesn't make good use of them
31+ so allow the compatibility mode button to be shown (mostly because
32+ this is just convenient for testing). -->
3033 <supports-screens android:requiresSmallestWidthDp="320"
3134 android:compatibleWidthLimitDp="480" />
3235
@@ -60,5 +63,13 @@
6063 </intent-filter>
6164 </activity>
6265
66+ <activity android:name=".app.ActionBarTabsPager"
67+ android:label="@string/action_bar_tabs_pager">
68+ <intent-filter>
69+ <action android:name="android.intent.action.MAIN" />
70+ <category android:name="com.example.android.supportv13.SUPPORT13_SAMPLE_CODE" />
71+ </intent-filter>
72+ </activity>
73+
6374 </application>
6475 </manifest>
--- a/samples/Support13Demos/res/values/strings.xml
+++ b/samples/Support13Demos/res/values/strings.xml
@@ -29,4 +29,5 @@
2929
3030 <string name="fragment_state_pager_support">Fragment/State Pager</string>
3131
32+ <string name="action_bar_tabs_pager">Fragment/Action Bar Tabs Pager</string>
3233 </resources>
--- /dev/null
+++ b/samples/Support13Demos/src/com/example/android/supportv13/app/ActionBarTabsPager.java
@@ -0,0 +1,155 @@
1+/*
2+ * Copyright (C) 2011 The Android Open Source Project
3+ *
4+ * Licensed under the Apache License, Version 2.0 (the "License");
5+ * you may not use this file except in compliance with the License.
6+ * You may obtain a copy of the License at
7+ *
8+ * http://www.apache.org/licenses/LICENSE-2.0
9+ *
10+ * Unless required by applicable law or agreed to in writing, software
11+ * distributed under the License is distributed on an "AS IS" BASIS,
12+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ * See the License for the specific language governing permissions and
14+ * limitations under the License.
15+ */
16+package com.example.android.supportv13.app;
17+
18+import java.util.ArrayList;
19+
20+import com.example.android.supportv13.R;
21+
22+import android.app.ActionBar;
23+import android.app.ActionBar.Tab;
24+import android.app.Activity;
25+import android.app.Fragment;
26+import android.app.FragmentTransaction;
27+import android.content.Context;
28+import android.os.Bundle;
29+import android.support.v13.app.FragmentPagerAdapter;
30+import android.support.v4.view.ViewPager;
31+
32+/**
33+ * This demonstrates the use of action bar tabs and how they interact
34+ * with other action bar features.
35+ */
36+public class ActionBarTabsPager extends Activity {
37+ ViewPager mViewPager;
38+ TabsAdapter mTabsAdapter;
39+
40+ @Override
41+ protected void onCreate(Bundle savedInstanceState) {
42+ super.onCreate(savedInstanceState);
43+
44+ mViewPager = new ViewPager(this);
45+ mViewPager.setId(R.id.pager);
46+ setContentView(mViewPager);
47+
48+ final ActionBar bar = getActionBar();
49+ bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
50+ bar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE);
51+
52+ mTabsAdapter = new TabsAdapter(this, mViewPager);
53+ mTabsAdapter.addTab(bar.newTab().setText("Simple"),
54+ CountingFragment.class, null);
55+ mTabsAdapter.addTab(bar.newTab().setText("List"),
56+ FragmentPagerSupport.ArrayListFragment.class, null);
57+ mTabsAdapter.addTab(bar.newTab().setText("Cursor"),
58+ CursorFragment.class, null);
59+
60+ if (savedInstanceState != null) {
61+ bar.setSelectedNavigationItem(savedInstanceState.getInt("tab", 0));
62+ }
63+ }
64+
65+ @Override
66+ protected void onSaveInstanceState(Bundle outState) {
67+ super.onSaveInstanceState(outState);
68+ outState.putInt("tab", getActionBar().getSelectedNavigationIndex());
69+ }
70+
71+ /**
72+ * This is a helper class that implements the management of tabs and all
73+ * details of connecting a ViewPager with associated TabHost. It relies on a
74+ * trick. Normally a tab host has a simple API for supplying a View or
75+ * Intent that each tab will show. This is not sufficient for switching
76+ * between pages. So instead we make the content part of the tab host
77+ * 0dp high (it is not shown) and the TabsAdapter supplies its own dummy
78+ * view to show as the tab content. It listens to changes in tabs, and takes
79+ * care of switch to the correct paged in the ViewPager whenever the selected
80+ * tab changes.
81+ */
82+ public static class TabsAdapter extends FragmentPagerAdapter
83+ implements ActionBar.TabListener, ViewPager.OnPageChangeListener {
84+ private final Context mContext;
85+ private final ActionBar mActionBar;
86+ private final ViewPager mViewPager;
87+ private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
88+
89+ static final class TabInfo {
90+ private final Class<?> clss;
91+ private final Bundle args;
92+
93+ TabInfo(Class<?> _class, Bundle _args) {
94+ clss = _class;
95+ args = _args;
96+ }
97+ }
98+
99+ public TabsAdapter(Activity activity, ViewPager pager) {
100+ super(activity.getFragmentManager());
101+ mContext = activity;
102+ mActionBar = activity.getActionBar();
103+ mViewPager = pager;
104+ mViewPager.setAdapter(this);
105+ mViewPager.setOnPageChangeListener(this);
106+ }
107+
108+ public void addTab(ActionBar.Tab tab, Class<?> clss, Bundle args) {
109+ TabInfo info = new TabInfo(clss, args);
110+ tab.setTag(info);
111+ tab.setTabListener(this);
112+ mTabs.add(info);
113+ mActionBar.addTab(tab);
114+ notifyDataSetChanged();
115+ }
116+
117+ @Override
118+ public int getCount() {
119+ return mTabs.size();
120+ }
121+
122+ @Override
123+ public Fragment getItem(int position) {
124+ TabInfo info = mTabs.get(position);
125+ return Fragment.instantiate(mContext, info.clss.getName(), info.args);
126+ }
127+
128+ @Override
129+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
130+ }
131+
132+ @Override
133+ public void onPageSelected(int position) {
134+ mActionBar.setSelectedNavigationItem(position);
135+ }
136+
137+ @Override
138+ public void onTabSelected(Tab tab, FragmentTransaction ft) {
139+ Object tag = tab.getTag();
140+ for (int i=0; i<mTabs.size(); i++) {
141+ if (mTabs.get(i) == tag) {
142+ mViewPager.setCurrentItem(i);
143+ }
144+ }
145+ }
146+
147+ @Override
148+ public void onTabUnselected(Tab tab, FragmentTransaction ft) {
149+ }
150+
151+ @Override
152+ public void onTabReselected(Tab tab, FragmentTransaction ft) {
153+ }
154+ }
155+}
--- /dev/null
+++ b/samples/Support13Demos/src/com/example/android/supportv13/app/CountingFragment.java
@@ -0,0 +1,67 @@
1+/*
2+ * Copyright (C) 2011 The Android Open Source Project
3+ *
4+ * Licensed under the Apache License, Version 2.0 (the "License");
5+ * you may not use this file except in compliance with the License.
6+ * You may obtain a copy of the License at
7+ *
8+ * http://www.apache.org/licenses/LICENSE-2.0
9+ *
10+ * Unless required by applicable law or agreed to in writing, software
11+ * distributed under the License is distributed on an "AS IS" BASIS,
12+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ * See the License for the specific language governing permissions and
14+ * limitations under the License.
15+ */
16+package com.example.android.supportv13.app;
17+
18+import com.example.android.supportv13.R;
19+
20+import android.app.Fragment;
21+import android.os.Bundle;
22+import android.view.LayoutInflater;
23+import android.view.View;
24+import android.view.ViewGroup;
25+import android.widget.TextView;
26+
27+public class CountingFragment extends Fragment {
28+ int mNum;
29+
30+ /**
31+ * Create a new instance of CountingFragment, providing "num"
32+ * as an argument.
33+ */
34+ static CountingFragment newInstance(int num) {
35+ CountingFragment f = new CountingFragment();
36+
37+ // Supply num input as an argument.
38+ Bundle args = new Bundle();
39+ args.putInt("num", num);
40+ f.setArguments(args);
41+
42+ return f;
43+ }
44+
45+ /**
46+ * When creating, retrieve this instance's number from its arguments.
47+ */
48+ @Override
49+ public void onCreate(Bundle savedInstanceState) {
50+ super.onCreate(savedInstanceState);
51+ mNum = getArguments() != null ? getArguments().getInt("num") : 1;
52+ }
53+
54+ /**
55+ * The Fragment's UI is just a simple text view showing its
56+ * instance number.
57+ */
58+ @Override
59+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
60+ Bundle savedInstanceState) {
61+ View v = inflater.inflate(R.layout.hello_world, container, false);
62+ View tv = v.findViewById(R.id.text);
63+ ((TextView)tv).setText("Fragment #" + mNum);
64+ tv.setBackgroundDrawable(getResources().getDrawable(android.R.drawable.gallery_thumb));
65+ return v;
66+ }
67+}
--- /dev/null
+++ b/samples/Support13Demos/src/com/example/android/supportv13/app/CursorFragment.java
@@ -0,0 +1,154 @@
1+/*
2+ * Copyright (C) 2011 The Android Open Source Project
3+ *
4+ * Licensed under the Apache License, Version 2.0 (the "License");
5+ * you may not use this file except in compliance with the License.
6+ * You may obtain a copy of the License at
7+ *
8+ * http://www.apache.org/licenses/LICENSE-2.0
9+ *
10+ * Unless required by applicable law or agreed to in writing, software
11+ * distributed under the License is distributed on an "AS IS" BASIS,
12+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ * See the License for the specific language governing permissions and
14+ * limitations under the License.
15+ */
16+
17+package com.example.android.supportv13.app;
18+
19+import android.app.ListFragment;
20+import android.app.LoaderManager;
21+import android.content.CursorLoader;
22+import android.content.Loader;
23+import android.database.Cursor;
24+import android.net.Uri;
25+import android.os.Bundle;
26+import android.provider.ContactsContract.Contacts;
27+import android.text.TextUtils;
28+import android.util.Log;
29+import android.view.Menu;
30+import android.view.MenuInflater;
31+import android.view.MenuItem;
32+import android.view.View;
33+import android.widget.ListView;
34+import android.widget.SearchView;
35+import android.widget.SimpleCursorAdapter;
36+import android.widget.SearchView.OnQueryTextListener;
37+
38+
39+public class CursorFragment extends ListFragment
40+ implements OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> {
41+
42+ // This is the Adapter being used to display the list's data.
43+ SimpleCursorAdapter mAdapter;
44+
45+ // If non-null, this is the current filter the user has provided.
46+ String mCurFilter;
47+
48+ @Override public void onActivityCreated(Bundle savedInstanceState) {
49+ super.onActivityCreated(savedInstanceState);
50+
51+ // Give some text to display if there is no data. In a real
52+ // application this would come from a resource.
53+ setEmptyText("No phone numbers");
54+
55+ // We have a menu item to show in action bar.
56+ setHasOptionsMenu(true);
57+
58+ // Create an empty adapter we will use to display the loaded data.
59+ mAdapter = new SimpleCursorAdapter(getActivity(),
60+ android.R.layout.simple_list_item_2, null,
61+ new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS },
62+ new int[] { android.R.id.text1, android.R.id.text2 }, 0);
63+ setListAdapter(mAdapter);
64+
65+ // Start out with a progress indicator.
66+ setListShown(false);
67+
68+ // Prepare the loader. Either re-connect with an existing one,
69+ // or start a new one.
70+ getLoaderManager().initLoader(0, null, this);
71+ }
72+
73+ @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
74+ // Place an action bar item for searching.
75+ MenuItem item = menu.add("Search");
76+ item.setIcon(android.R.drawable.ic_menu_search);
77+ item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
78+ SearchView sv = new SearchView(getActivity());
79+ sv.setOnQueryTextListener(this);
80+ item.setActionView(sv);
81+ }
82+
83+ public boolean onQueryTextChange(String newText) {
84+ // Called when the action bar search text has changed. Update
85+ // the search filter, and restart the loader to do a new query
86+ // with this filter.
87+ mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
88+ getLoaderManager().restartLoader(0, null, this);
89+ return true;
90+ }
91+
92+ @Override public boolean onQueryTextSubmit(String query) {
93+ // Don't care about this.
94+ return true;
95+ }
96+
97+ @Override public void onListItemClick(ListView l, View v, int position, long id) {
98+ // Insert desired behavior here.
99+ Log.i("FragmentComplexList", "Item clicked: " + id);
100+ }
101+
102+ // These are the Contacts rows that we will retrieve.
103+ static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {
104+ Contacts._ID,
105+ Contacts.DISPLAY_NAME,
106+ Contacts.CONTACT_STATUS,
107+ Contacts.CONTACT_PRESENCE,
108+ Contacts.PHOTO_ID,
109+ Contacts.LOOKUP_KEY,
110+ };
111+
112+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
113+ // This is called when a new Loader needs to be created. This
114+ // sample only has one Loader, so we don't care about the ID.
115+ // First, pick the base URI to use depending on whether we are
116+ // currently filtering.
117+ Uri baseUri;
118+ if (mCurFilter != null) {
119+ baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
120+ Uri.encode(mCurFilter));
121+ } else {
122+ baseUri = Contacts.CONTENT_URI;
123+ }
124+
125+ // Now create and return a CursorLoader that will take care of
126+ // creating a Cursor for the data being displayed.
127+ String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
128+ + Contacts.HAS_PHONE_NUMBER + "=1) AND ("
129+ + Contacts.DISPLAY_NAME + " != '' ))";
130+ return new CursorLoader(getActivity(), baseUri,
131+ CONTACTS_SUMMARY_PROJECTION, select, null,
132+ Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
133+ }
134+
135+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
136+ // Swap the new cursor in. (The framework will take care of closing the
137+ // old cursor once we return.)
138+ mAdapter.swapCursor(data);
139+
140+ // The list should now be shown.
141+ if (isResumed()) {
142+ setListShown(true);
143+ } else {
144+ setListShownNoAnimation(true);
145+ }
146+ }
147+
148+ public void onLoaderReset(Loader<Cursor> loader) {
149+ // This is called when the last Cursor provided to onLoadFinished()
150+ // above is about to be closed. We need to make sure we are no
151+ // longer using it.
152+ mAdapter.swapCursor(null);
153+ }
154+}
--- a/samples/Support13Demos/src/com/example/android/supportv13/app/_index.html
+++ b/samples/Support13Demos/src/com/example/android/supportv13/app/_index.html
@@ -8,10 +8,16 @@ package features of the static support library fir API 13 or later.
88
99 <h3 id="Fragment">Fragment</h3>
1010 <dl>
11+ <dt><a href="ActionBarTabsPager.html">Action Bar Tabs Pager</a></dt>
12+ <dd>Demonstrates the use of fragments to implement switching between
13+ ActionBar tabs, using a ViewPager to manager the fragments so that
14+ the user can also fling left and right to switch tabs.</dd>
15+
1116 <dt><a href="FragmentPagerSupport.html">Fragment Pager Support</a></dt>
1217 <dd>Demonstrates the use of the v4 support class ViewPager with a
1318 FragmentPagerAdapter to build a user interface where the user can fling
1419 left or right to switch between fragments.</dd>
20+
1521 <dt><a href="FragmentStatePagerSupport.html">Fragment State Pager Support</a></dt>
1622 <dd>Demonstrates the use of the v4 support class ViewPager with a
1723 FragmentStatePagerAdapter to build a user interface where the user can fling
--- a/samples/Support4Demos/AndroidManifest.xml
+++ b/samples/Support4Demos/AndroidManifest.xml
@@ -26,7 +26,10 @@
2626
2727 <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="13" />
2828
29- <!-- This app has not been optimized for large screens. -->
29+ <!-- The smallest screen this app works on is a phone. The app will
30+ scale its UI to larger screens but doesn't make good use of them
31+ so allow the compatibility mode button to be shown (mostly because
32+ this is just convenient for testing). -->
3033 <supports-screens android:requiresSmallestWidthDp="320"
3134 android:compatibleWidthLimitDp="480" />
3235
@@ -147,6 +150,22 @@
147150 </intent-filter>
148151 </activity>
149152
153+ <activity android:name=".app.FragmentTabs"
154+ android:label="@string/fragment_tabs">
155+ <intent-filter>
156+ <action android:name="android.intent.action.MAIN" />
157+ <category android:name="com.example.android.supportv4.SUPPORT4_SAMPLE_CODE" />
158+ </intent-filter>
159+ </activity>
160+
161+ <activity android:name=".app.FragmentTabsPager"
162+ android:label="@string/fragment_tabs_pager">
163+ <intent-filter>
164+ <action android:name="android.intent.action.MAIN" />
165+ <category android:name="com.example.android.supportv4.SUPPORT4_SAMPLE_CODE" />
166+ </intent-filter>
167+ </activity>
168+
150169 <activity android:name=".app.FragmentPagerSupport"
151170 android:label="@string/fragment_pager_support">
152171 <intent-filter>
@@ -171,6 +190,14 @@
171190 </intent-filter>
172191 </activity>
173192
193+ <activity android:name=".app.LoaderCustomSupport"
194+ android:label="@string/loader_custom_support">
195+ <intent-filter>
196+ <action android:name="android.intent.action.MAIN" />
197+ <category android:name="com.example.android.supportv4.SUPPORT4_SAMPLE_CODE" />
198+ </intent-filter>
199+ </activity>
200+
174201 <activity android:name=".app.LoaderThrottleSupport"
175202 android:label="@string/loader_throttle_support">
176203 <intent-filter>
--- /dev/null
+++ b/samples/Support4Demos/res/layout/fragment_tabs.xml
@@ -0,0 +1,52 @@
1+<?xml version="1.0" encoding="utf-8"?>
2+<!--
3+/* //device/apps/common/assets/res/layout/tab_content.xml
4+**
5+** Copyright 2011, The Android Open Source Project
6+**
7+** Licensed under the Apache License, Version 2.0 (the "License");
8+** you may not use this file except in compliance with the License.
9+** You may obtain a copy of the License at
10+**
11+** http://www.apache.org/licenses/LICENSE-2.0
12+**
13+** Unless required by applicable law or agreed to in writing, software
14+** distributed under the License is distributed on an "AS IS" BASIS,
15+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+** See the License for the specific language governing permissions and
17+** limitations under the License.
18+*/
19+-->
20+
21+<TabHost
22+ xmlns:android="http://schemas.android.com/apk/res/android"
23+ android:id="@android:id/tabhost"
24+ android:layout_width="match_parent"
25+ android:layout_height="match_parent">
26+
27+ <LinearLayout
28+ android:orientation="vertical"
29+ android:layout_width="match_parent"
30+ android:layout_height="match_parent">
31+
32+ <TabWidget
33+ android:id="@android:id/tabs"
34+ android:orientation="horizontal"
35+ android:layout_width="match_parent"
36+ android:layout_height="wrap_content"
37+ android:layout_weight="0"/>
38+
39+ <FrameLayout
40+ android:id="@android:id/tabcontent"
41+ android:layout_width="0dp"
42+ android:layout_height="0dp"
43+ android:layout_weight="0"/>
44+
45+ <FrameLayout
46+ android:id="@+android:id/realtabcontent"
47+ android:layout_width="match_parent"
48+ android:layout_height="0dp"
49+ android:layout_weight="1"/>
50+
51+ </LinearLayout>
52+</TabHost>
--- /dev/null
+++ b/samples/Support4Demos/res/layout/fragment_tabs_pager.xml
@@ -0,0 +1,52 @@
1+<?xml version="1.0" encoding="utf-8"?>
2+<!--
3+/* //device/apps/common/assets/res/layout/tab_content.xml
4+**
5+** Copyright 2011, The Android Open Source Project
6+**
7+** Licensed under the Apache License, Version 2.0 (the "License");
8+** you may not use this file except in compliance with the License.
9+** You may obtain a copy of the License at
10+**
11+** http://www.apache.org/licenses/LICENSE-2.0
12+**
13+** Unless required by applicable law or agreed to in writing, software
14+** distributed under the License is distributed on an "AS IS" BASIS,
15+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+** See the License for the specific language governing permissions and
17+** limitations under the License.
18+*/
19+-->
20+
21+<TabHost
22+ xmlns:android="http://schemas.android.com/apk/res/android"
23+ android:id="@android:id/tabhost"
24+ android:layout_width="match_parent"
25+ android:layout_height="match_parent">
26+
27+ <LinearLayout
28+ android:orientation="vertical"
29+ android:layout_width="match_parent"
30+ android:layout_height="match_parent">
31+
32+ <TabWidget
33+ android:id="@android:id/tabs"
34+ android:orientation="horizontal"
35+ android:layout_width="match_parent"
36+ android:layout_height="wrap_content"
37+ android:layout_weight="0"/>
38+
39+ <FrameLayout
40+ android:id="@android:id/tabcontent"
41+ android:layout_width="0dp"
42+ android:layout_height="0dp"
43+ android:layout_weight="0"/>
44+
45+ <android.support.v4.view.ViewPager
46+ android:id="@+id/pager"
47+ android:layout_width="match_parent"
48+ android:layout_height="0dp"
49+ android:layout_weight="1"/>
50+
51+ </LinearLayout>
52+</TabHost>
--- /dev/null
+++ b/samples/Support4Demos/res/layout/list_item_icon_text.xml
@@ -0,0 +1,32 @@
1+<?xml version="1.0" encoding="utf-8"?>
2+<!-- Copyright (C) 2007 The Android Open Source Project
3+
4+ Licensed under the Apache License, Version 2.0 (the "License");
5+ you may not use this file except in compliance with the License.
6+ You may obtain a copy of the License at
7+
8+ http://www.apache.org/licenses/LICENSE-2.0
9+
10+ Unless required by applicable law or agreed to in writing, software
11+ distributed under the License is distributed on an "AS IS" BASIS,
12+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ See the License for the specific language governing permissions and
14+ limitations under the License.
15+-->
16+
17+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
18+ android:orientation="horizontal"
19+ android:layout_width="match_parent"
20+ android:layout_height="match_parent">
21+
22+ <ImageView android:id="@+id/icon"
23+ android:layout_width="48dip"
24+ android:layout_height="48dip" />
25+
26+ <TextView android:id="@+id/text"
27+ android:layout_gravity="center_vertical"
28+ android:layout_width="0dip"
29+ android:layout_weight="1.0"
30+ android:layout_height="wrap_content" />
31+
32+</LinearLayout>
--- a/samples/Support4Demos/res/values/strings.xml
+++ b/samples/Support4Demos/res/values/strings.xml
@@ -77,6 +77,10 @@
7777 <string name="fragment_stack_support">Fragment/Stack</string>
7878 <string name="new_fragment">New fragment</string>
7979
80+ <string name="fragment_tabs">Fragment/Tabs</string>
81+
82+ <string name="fragment_tabs_pager">Fragment/Tabs and Pager</string>
83+
8084 <string name="fragment_pager_support">Fragment/Pager</string>
8185 <string name="first">First</string>
8286 <string name="last">Last</string>
@@ -85,6 +89,8 @@
8589
8690 <string name="loader_cursor_support">Loader/Cursor</string>
8791
92+ <string name="loader_custom_support">Loader/Custom</string>
93+
8894 <string name="loader_throttle_support">Loader/Throttle</string>
8995
9096 </resources>
--- /dev/null
+++ b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentTabs.java
@@ -0,0 +1,169 @@
1+/*
2+ * Copyright (C) 2011 The Android Open Source Project
3+ *
4+ * Licensed under the Apache License, Version 2.0 (the "License");
5+ * you may not use this file except in compliance with the License.
6+ * You may obtain a copy of the License at
7+ *
8+ * http://www.apache.org/licenses/LICENSE-2.0
9+ *
10+ * Unless required by applicable law or agreed to in writing, software
11+ * distributed under the License is distributed on an "AS IS" BASIS,
12+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ * See the License for the specific language governing permissions and
14+ * limitations under the License.
15+ */
16+package com.example.android.supportv4.app;
17+
18+import java.util.HashMap;
19+
20+import com.example.android.supportv4.R;
21+
22+import android.content.Context;
23+import android.os.Bundle;
24+import android.support.v4.app.Fragment;
25+import android.support.v4.app.FragmentActivity;
26+import android.support.v4.app.FragmentTransaction;
27+import android.view.View;
28+import android.widget.TabHost;
29+
30+/**
31+ * This demonstrates how you can implement switching between the tabs of a
32+ * TabHost through fragments. It uses a trick (see the code below) to allow
33+ * the tabs to switch between fragments instead of simple views.
34+ */
35+public class FragmentTabs extends FragmentActivity {
36+ TabHost mTabHost;
37+ TabManager mTabManager;
38+
39+ @Override
40+ protected void onCreate(Bundle savedInstanceState) {
41+ super.onCreate(savedInstanceState);
42+
43+ setContentView(R.layout.fragment_tabs);
44+ mTabHost = (TabHost)findViewById(android.R.id.tabhost);
45+ mTabHost.setup();
46+
47+ mTabManager = new TabManager(this, mTabHost, R.id.realtabcontent);
48+
49+ mTabManager.addTab(mTabHost.newTabSpec("simple").setIndicator("Simple"),
50+ FragmentStackSupport.CountingFragment.class, null);
51+ mTabManager.addTab(mTabHost.newTabSpec("contacts").setIndicator("Contacts"),
52+ LoaderCursorSupport.CursorLoaderListFragment.class, null);
53+ mTabManager.addTab(mTabHost.newTabSpec("custom").setIndicator("Custom"),
54+ LoaderCustomSupport.AppListFragment.class, null);
55+ mTabManager.addTab(mTabHost.newTabSpec("throttle").setIndicator("Throttle"),
56+ LoaderThrottleSupport.ThrottledLoaderListFragment.class, null);
57+
58+ if (savedInstanceState != null) {
59+ mTabHost.setCurrentTabByTag(savedInstanceState.getString("tab"));
60+ }
61+ }
62+
63+ @Override
64+ protected void onSaveInstanceState(Bundle outState) {
65+ super.onSaveInstanceState(outState);
66+ outState.putString("tab", mTabHost.getCurrentTabTag());
67+ }
68+
69+ /**
70+ * This is a helper class that implements a generic mechanism for
71+ * associating fragments with the tabs in a tab host. It relies on a
72+ * trick. Normally a tab host has a simple API for supplying a View or
73+ * Intent that each tab will show. This is not sufficient for switching
74+ * between fragments. So instead we make the content part of the tab host
75+ * 0dp high (it is not shown) and the TabManager supplies its own dummy
76+ * view to show as the tab content. It listens to changes in tabs, and takes
77+ * care of switch to the correct fragment shown in a separate content area
78+ * whenever the selected tab changes.
79+ */
80+ public static class TabManager implements TabHost.OnTabChangeListener {
81+ private final FragmentActivity mActivity;
82+ private final TabHost mTabHost;
83+ private final int mContainerId;
84+ private final HashMap<String, TabInfo> mTabs = new HashMap<String, TabInfo>();
85+ TabInfo mLastTab;
86+
87+ static final class TabInfo {
88+ private final String tag;
89+ private final Class<?> clss;
90+ private final Bundle args;
91+ private Fragment fragment;
92+
93+ TabInfo(String _tag, Class<?> _class, Bundle _args) {
94+ tag = _tag;
95+ clss = _class;
96+ args = _args;
97+ }
98+ }
99+
100+ static class DummyTabFactory implements TabHost.TabContentFactory {
101+ private final Context mContext;
102+
103+ public DummyTabFactory(Context context) {
104+ mContext = context;
105+ }
106+
107+ @Override
108+ public View createTabContent(String tag) {
109+ View v = new View(mContext);
110+ v.setMinimumWidth(0);
111+ v.setMinimumHeight(0);
112+ return v;
113+ }
114+ }
115+
116+ public TabManager(FragmentActivity activity, TabHost tabHost, int containerId) {
117+ mActivity = activity;
118+ mTabHost = tabHost;
119+ mContainerId = containerId;
120+ mTabHost.setOnTabChangedListener(this);
121+ }
122+
123+ public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args) {
124+ tabSpec.setContent(new DummyTabFactory(mActivity));
125+ String tag = tabSpec.getTag();
126+
127+ TabInfo info = new TabInfo(tag, clss, args);
128+
129+ // Check to see if we already have a fragment for this tab, probably
130+ // from a previously saved state. If so, deactivate it, because our
131+ // initial state is that a tab isn't shown.
132+ info.fragment = mActivity.getSupportFragmentManager().findFragmentByTag(tag);
133+ if (info.fragment != null && !info.fragment.isDetached()) {
134+ FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction();
135+ ft.detach(info.fragment);
136+ ft.commit();
137+ }
138+
139+ mTabs.put(tag, info);
140+ mTabHost.addTab(tabSpec);
141+ }
142+
143+ @Override
144+ public void onTabChanged(String tabId) {
145+ TabInfo newTab = mTabs.get(tabId);
146+ if (mLastTab != newTab) {
147+ FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction();
148+ if (mLastTab != null) {
149+ if (mLastTab.fragment != null) {
150+ ft.detach(mLastTab.fragment);
151+ }
152+ }
153+ if (newTab != null) {
154+ if (newTab.fragment == null) {
155+ newTab.fragment = Fragment.instantiate(mActivity,
156+ newTab.clss.getName(), newTab.args);
157+ ft.add(mContainerId, newTab.fragment, newTab.tag);
158+ } else {
159+ ft.attach(newTab.fragment);
160+ }
161+ }
162+
163+ mLastTab = newTab;
164+ ft.commit();
165+ mActivity.getSupportFragmentManager().executePendingTransactions();
166+ }
167+ }
168+ }
169+}
--- /dev/null
+++ b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentTabsPager.java
@@ -0,0 +1,165 @@
1+/*
2+ * Copyright (C) 2011 The Android Open Source Project
3+ *
4+ * Licensed under the Apache License, Version 2.0 (the "License");
5+ * you may not use this file except in compliance with the License.
6+ * You may obtain a copy of the License at
7+ *
8+ * http://www.apache.org/licenses/LICENSE-2.0
9+ *
10+ * Unless required by applicable law or agreed to in writing, software
11+ * distributed under the License is distributed on an "AS IS" BASIS,
12+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ * See the License for the specific language governing permissions and
14+ * limitations under the License.
15+ */
16+package com.example.android.supportv4.app;
17+
18+import java.util.ArrayList;
19+
20+import com.example.android.supportv4.R;
21+
22+import android.content.Context;
23+import android.os.Bundle;
24+import android.support.v4.app.Fragment;
25+import android.support.v4.app.FragmentActivity;
26+import android.support.v4.app.FragmentPagerAdapter;
27+import android.support.v4.view.ViewPager;
28+import android.view.View;
29+import android.widget.TabHost;
30+
31+/**
32+ * Demonstrates combining a TabHost with a ViewPager to implement a tab UI
33+ * that switches between tabs and also allows the user to perform horizontal
34+ * flicks to move between the tabs.
35+ */
36+public class FragmentTabsPager extends FragmentActivity {
37+ TabHost mTabHost;
38+ ViewPager mViewPager;
39+ TabsAdapter mTabsAdapter;
40+
41+ @Override
42+ protected void onCreate(Bundle savedInstanceState) {
43+ super.onCreate(savedInstanceState);
44+
45+ setContentView(R.layout.fragment_tabs_pager);
46+ mTabHost = (TabHost)findViewById(android.R.id.tabhost);
47+ mTabHost.setup();
48+
49+ mViewPager = (ViewPager)findViewById(R.id.pager);
50+
51+ mTabsAdapter = new TabsAdapter(this, mTabHost, mViewPager);
52+
53+ mTabsAdapter.addTab(mTabHost.newTabSpec("simple").setIndicator("Simple"),
54+ FragmentStackSupport.CountingFragment.class, null);
55+ mTabsAdapter.addTab(mTabHost.newTabSpec("contacts").setIndicator("Contacts"),
56+ LoaderCursorSupport.CursorLoaderListFragment.class, null);
57+ mTabsAdapter.addTab(mTabHost.newTabSpec("custom").setIndicator("Custom"),
58+ LoaderCustomSupport.AppListFragment.class, null);
59+ mTabsAdapter.addTab(mTabHost.newTabSpec("throttle").setIndicator("Throttle"),
60+ LoaderThrottleSupport.ThrottledLoaderListFragment.class, null);
61+
62+ if (savedInstanceState != null) {
63+ mTabHost.setCurrentTabByTag(savedInstanceState.getString("tab"));
64+ }
65+ }
66+
67+ @Override
68+ protected void onSaveInstanceState(Bundle outState) {
69+ super.onSaveInstanceState(outState);
70+ outState.putString("tab", mTabHost.getCurrentTabTag());
71+ }
72+
73+ /**
74+ * This is a helper class that implements the management of tabs and all
75+ * details of connecting a ViewPager with associated TabHost. It relies on a
76+ * trick. Normally a tab host has a simple API for supplying a View or
77+ * Intent that each tab will show. This is not sufficient for switching
78+ * between pages. So instead we make the content part of the tab host
79+ * 0dp high (it is not shown) and the TabsAdapter supplies its own dummy
80+ * view to show as the tab content. It listens to changes in tabs, and takes
81+ * care of switch to the correct paged in the ViewPager whenever the selected
82+ * tab changes.
83+ */
84+ public static class TabsAdapter extends FragmentPagerAdapter
85+ implements TabHost.OnTabChangeListener, ViewPager.OnPageChangeListener {
86+ private final Context mContext;
87+ private final TabHost mTabHost;
88+ private final ViewPager mViewPager;
89+ private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
90+
91+ static final class TabInfo {
92+ private final String tag;
93+ private final Class<?> clss;
94+ private final Bundle args;
95+
96+ TabInfo(String _tag, Class<?> _class, Bundle _args) {
97+ tag = _tag;
98+ clss = _class;
99+ args = _args;
100+ }
101+ }
102+
103+ static class DummyTabFactory implements TabHost.TabContentFactory {
104+ private final Context mContext;
105+
106+ public DummyTabFactory(Context context) {
107+ mContext = context;
108+ }
109+
110+ @Override
111+ public View createTabContent(String tag) {
112+ View v = new View(mContext);
113+ v.setMinimumWidth(0);
114+ v.setMinimumHeight(0);
115+ return v;
116+ }
117+ }
118+
119+ public TabsAdapter(FragmentActivity activity, TabHost tabHost, ViewPager pager) {
120+ super(activity.getSupportFragmentManager());
121+ mContext = activity;
122+ mTabHost = tabHost;
123+ mViewPager = pager;
124+ mTabHost.setOnTabChangedListener(this);
125+ mViewPager.setAdapter(this);
126+ mViewPager.setOnPageChangeListener(this);
127+ }
128+
129+ public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args) {
130+ tabSpec.setContent(new DummyTabFactory(mContext));
131+ String tag = tabSpec.getTag();
132+
133+ TabInfo info = new TabInfo(tag, clss, args);
134+ mTabs.add(info);
135+ mTabHost.addTab(tabSpec);
136+ notifyDataSetChanged();
137+ }
138+
139+ @Override
140+ public int getCount() {
141+ return mTabs.size();
142+ }
143+
144+ @Override
145+ public Fragment getItem(int position) {
146+ TabInfo info = mTabs.get(position);
147+ return Fragment.instantiate(mContext, info.clss.getName(), info.args);
148+ }
149+
150+ @Override
151+ public void onTabChanged(String tabId) {
152+ int position = mTabHost.getCurrentTab();
153+ mViewPager.setCurrentItem(position);
154+ }
155+
156+ @Override
157+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
158+ }
159+
160+ @Override
161+ public void onPageSelected(int position) {
162+ mTabHost.setCurrentTab(position);
163+ }
164+ }
165+}
--- a/samples/Support4Demos/src/com/example/android/supportv4/app/LoaderCursorSupport.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/app/LoaderCursorSupport.java
@@ -81,6 +81,9 @@ public class LoaderCursorSupport extends FragmentActivity {
8181 new int[] { android.R.id.text1, android.R.id.text2 }, 0);
8282 setListAdapter(mAdapter);
8383
84+ // Start out with a progress indicator.
85+ setListShown(false);
86+
8487 // Prepare the loader. Either re-connect with an existing one,
8588 // or start a new one.
8689 getLoaderManager().initLoader(0, null, this);
@@ -147,6 +150,13 @@ public class LoaderCursorSupport extends FragmentActivity {
147150 // Swap the new cursor in. (The framework will take care of closing the
148151 // old cursor once we return.)
149152 mAdapter.swapCursor(data);
153+
154+ // The list should now be shown.
155+ if (isResumed()) {
156+ setListShown(true);
157+ } else {
158+ setListShownNoAnimation(true);
159+ }
150160 }
151161
152162 public void onLoaderReset(Loader<Cursor> loader) {
--- /dev/null
+++ b/samples/Support4Demos/src/com/example/android/supportv4/app/LoaderCustomSupport.java
@@ -0,0 +1,482 @@
1+/*
2+ * Copyright (C) 2010 The Android Open Source Project
3+ *
4+ * Licensed under the Apache License, Version 2.0 (the "License");
5+ * you may not use this file except in compliance with the License.
6+ * You may obtain a copy of the License at
7+ *
8+ * http://www.apache.org/licenses/LICENSE-2.0
9+ *
10+ * Unless required by applicable law or agreed to in writing, software
11+ * distributed under the License is distributed on an "AS IS" BASIS,
12+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ * See the License for the specific language governing permissions and
14+ * limitations under the License.
15+ */
16+
17+package com.example.android.supportv4.app;
18+
19+import com.example.android.supportv4.R;
20+
21+import java.io.File;
22+import java.text.Collator;
23+import java.util.ArrayList;
24+import java.util.Collections;
25+import java.util.Comparator;
26+import java.util.List;
27+
28+import android.content.BroadcastReceiver;
29+import android.content.Context;
30+import android.content.Intent;
31+import android.content.IntentFilter;
32+import android.content.pm.ActivityInfo;
33+import android.content.pm.ApplicationInfo;
34+import android.content.pm.PackageManager;
35+import android.content.res.Configuration;
36+import android.content.res.Resources;
37+import android.graphics.drawable.Drawable;
38+import android.os.Bundle;
39+import android.support.v4.app.FragmentActivity;
40+import android.support.v4.app.FragmentManager;
41+import android.support.v4.app.ListFragment;
42+import android.support.v4.app.LoaderManager;
43+import android.support.v4.content.AsyncTaskLoader;
44+import android.support.v4.content.Loader;
45+import android.text.TextUtils;
46+import android.util.Log;
47+import android.view.LayoutInflater;
48+import android.view.Menu;
49+import android.view.MenuInflater;
50+import android.view.MenuItem;
51+import android.view.View;
52+import android.view.ViewGroup;
53+import android.widget.ArrayAdapter;
54+import android.widget.ImageView;
55+import android.widget.ListView;
56+import android.widget.SearchView;
57+import android.widget.TextView;
58+import android.widget.SearchView.OnQueryTextListener;
59+
60+/**
61+ * Demonstration of the implementation of a custom Loader.
62+ */
63+public class LoaderCustomSupport extends FragmentActivity {
64+
65+ @Override
66+ protected void onCreate(Bundle savedInstanceState) {
67+ super.onCreate(savedInstanceState);
68+
69+ FragmentManager fm = getSupportFragmentManager();
70+
71+ // Create the list fragment and add it as our sole content.
72+ if (fm.findFragmentById(android.R.id.content) == null) {
73+ AppListFragment list = new AppListFragment();
74+ fm.beginTransaction().add(android.R.id.content, list).commit();
75+ }
76+ }
77+
78+//BEGIN_INCLUDE(loader)
79+ /**
80+ * This class holds the per-item data in our Loader.
81+ */
82+ public static class AppEntry {
83+ public AppEntry(AppListLoader loader, ApplicationInfo info) {
84+ mLoader = loader;
85+ mInfo = info;
86+ mApkFile = new File(info.sourceDir);
87+ }
88+
89+ public ApplicationInfo getApplicationInfo() {
90+ return mInfo;
91+ }
92+
93+ public String getLabel() {
94+ return mLabel;
95+ }
96+
97+ public Drawable getIcon() {
98+ if (mIcon == null) {
99+ if (mApkFile.exists()) {
100+ mIcon = mInfo.loadIcon(mLoader.mPm);
101+ return mIcon;
102+ } else {
103+ mMounted = false;
104+ }
105+ } else if (!mMounted) {
106+ // If the app wasn't mounted but is now mounted, reload
107+ // its icon.
108+ if (mApkFile.exists()) {
109+ mMounted = true;
110+ mIcon = mInfo.loadIcon(mLoader.mPm);
111+ return mIcon;
112+ }
113+ } else {
114+ return mIcon;
115+ }
116+
117+ return mLoader.getContext().getResources().getDrawable(
118+ android.R.drawable.sym_def_app_icon);
119+ }
120+
121+ @Override public String toString() {
122+ return mLabel;
123+ }
124+
125+ void loadLabel(Context context) {
126+ if (mLabel == null || !mMounted) {
127+ if (!mApkFile.exists()) {
128+ mMounted = false;
129+ mLabel = mInfo.packageName;
130+ } else {
131+ mMounted = true;
132+ CharSequence label = mInfo.loadLabel(context.getPackageManager());
133+ mLabel = label != null ? label.toString() : mInfo.packageName;
134+ }
135+ }
136+ }
137+
138+ private final AppListLoader mLoader;
139+ private final ApplicationInfo mInfo;
140+ private final File mApkFile;
141+ private String mLabel;
142+ private Drawable mIcon;
143+ private boolean mMounted;
144+ }
145+
146+ /**
147+ * Perform alphabetical comparison of application entry objects.
148+ */
149+ public static final Comparator<AppEntry> ALPHA_COMPARATOR = new Comparator<AppEntry>() {
150+ private final Collator sCollator = Collator.getInstance();
151+ @Override
152+ public int compare(AppEntry object1, AppEntry object2) {
153+ return sCollator.compare(object1.getLabel(), object2.getLabel());
154+ }
155+ };
156+
157+ /**
158+ * Helper for determining if the configuration has changed in an interesting
159+ * way so we need to rebuild the app list.
160+ */
161+ public static class InterestingConfigChanges {
162+ final Configuration mLastConfiguration = new Configuration();
163+ int mLastDensity;
164+
165+ boolean applyNewConfig(Resources res) {
166+ int configChanges = mLastConfiguration.updateFrom(res.getConfiguration());
167+ boolean densityChanged = mLastDensity != res.getDisplayMetrics().densityDpi;
168+ if (densityChanged || (configChanges&(ActivityInfo.CONFIG_LOCALE
169+ |ActivityInfo.CONFIG_UI_MODE|ActivityInfo.CONFIG_SCREEN_LAYOUT)) != 0) {
170+ mLastDensity = res.getDisplayMetrics().densityDpi;
171+ return true;
172+ }
173+ return false;
174+ }
175+ }
176+
177+ /**
178+ * Helper class to look for interesting changes to the installed apps
179+ * so that the loader can be updated.
180+ */
181+ public static class PackageIntentReceiver extends BroadcastReceiver {
182+ final AppListLoader mLoader;
183+
184+ public PackageIntentReceiver(AppListLoader loader) {
185+ mLoader = loader;
186+ IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
187+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
188+ filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
189+ filter.addDataScheme("package");
190+ mLoader.getContext().registerReceiver(this, filter);
191+ // Register for events related to sdcard installation.
192+ IntentFilter sdFilter = new IntentFilter();
193+ sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
194+ sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
195+ mLoader.getContext().registerReceiver(this, sdFilter);
196+ }
197+
198+ @Override public void onReceive(Context context, Intent intent) {
199+ // Tell the loader about the change.
200+ mLoader.onContentChanged();
201+ }
202+ }
203+
204+ /**
205+ * A custom Loader that loads all of the installed applications.
206+ */
207+ public static class AppListLoader extends AsyncTaskLoader<List<AppEntry>> {
208+ final InterestingConfigChanges mLastConfig = new InterestingConfigChanges();
209+ final PackageManager mPm;
210+
211+ List<AppEntry> mApps;
212+ PackageIntentReceiver mPackageObserver;
213+
214+ public AppListLoader(Context context) {
215+ super(context);
216+
217+ // Retrieve the package manager for later use; note we don't
218+ // use 'context' directly but instead the save global application
219+ // context returned by getContext().
220+ mPm = getContext().getPackageManager();
221+ }
222+
223+ /**
224+ * This is where the bulk of our work is done. This function is
225+ * called in a background thread and should generate a new set of
226+ * data to be published by the loader.
227+ */
228+ @Override public List<AppEntry> loadInBackground() {
229+ // Retrieve all known applications.
230+ List<ApplicationInfo> apps = mPm.getInstalledApplications(
231+ PackageManager.GET_UNINSTALLED_PACKAGES |
232+ PackageManager.GET_DISABLED_COMPONENTS);
233+ if (apps == null) {
234+ apps = new ArrayList<ApplicationInfo>();
235+ }
236+
237+ final Context context = getContext();
238+
239+ // Create corresponding array of entries and load their labels.
240+ List<AppEntry> entries = new ArrayList<AppEntry>(apps.size());
241+ for (int i=0; i<apps.size(); i++) {
242+ AppEntry entry = new AppEntry(this, apps.get(i));
243+ entry.loadLabel(context);
244+ entries.add(entry);
245+ }
246+
247+ // Sort the list.
248+ Collections.sort(entries, ALPHA_COMPARATOR);
249+
250+ // Done!
251+ return entries;
252+ }
253+
254+ /**
255+ * Called when there is new data to deliver to the client. The
256+ * super class will take care of delivering it; the implementation
257+ * here just adds a little more logic.
258+ */
259+ @Override public void deliverResult(List<AppEntry> apps) {
260+ if (isReset()) {
261+ // An async query came in while the loader is stopped. We
262+ // don't need the result.
263+ if (apps != null) {
264+ onReleaseResources(apps);
265+ }
266+ }
267+ List<AppEntry> oldApps = apps;
268+ mApps = apps;
269+
270+ if (isStarted()) {
271+ // If the Loader is currently started, we can immediately
272+ // deliver its results.
273+ super.deliverResult(apps);
274+ }
275+
276+ // At this point we can release the resources associated with
277+ // 'oldApps' if needed; now that the new result is delivered we
278+ // know that it is no longer in use.
279+ if (oldApps != null) {
280+ onReleaseResources(oldApps);
281+ }
282+ }
283+
284+ /**
285+ * Handles a request to start the Loader.
286+ */
287+ @Override protected void onStartLoading() {
288+ if (mApps != null) {
289+ // If we currently have a result available, deliver it
290+ // immediately.
291+ deliverResult(mApps);
292+ }
293+
294+ // Start watching for changes in the app data.
295+ if (mPackageObserver == null) {
296+ mPackageObserver = new PackageIntentReceiver(this);
297+ }
298+
299+ // Has something interesting in the configuration changed since we
300+ // last built the app list?
301+ boolean configChange = mLastConfig.applyNewConfig(getContext().getResources());
302+
303+ if (takeContentChanged() || mApps == null || configChange) {
304+ // If the data has changed since the last time it was loaded
305+ // or is not currently available, start a load.
306+ forceLoad();
307+ }
308+ }
309+
310+ /**
311+ * Handles a request to stop the Loader.
312+ */
313+ @Override protected void onStopLoading() {
314+ // Attempt to cancel the current load task if possible.
315+ cancelLoad();
316+ }
317+
318+ /**
319+ * Handles a request to cancel a load.
320+ */
321+ @Override public void onCanceled(List<AppEntry> apps) {
322+ super.onCanceled(apps);
323+
324+ // At this point we can release the resources associated with 'apps'
325+ // if needed.
326+ onReleaseResources(apps);
327+ }
328+
329+ /**
330+ * Handles a request to completely reset the Loader.
331+ */
332+ @Override protected void onReset() {
333+ super.onReset();
334+
335+ // Ensure the loader is stopped
336+ onStopLoading();
337+
338+ // At this point we can release the resources associated with 'apps'
339+ // if needed.
340+ if (mApps != null) {
341+ onReleaseResources(mApps);
342+ mApps = null;
343+ }
344+
345+ // Stop monitoring for changes.
346+ if (mPackageObserver != null) {
347+ getContext().unregisterReceiver(mPackageObserver);
348+ mPackageObserver = null;
349+ }
350+ }
351+
352+ /**
353+ * Helper function to take care of releasing resources associated
354+ * with an actively loaded data set.
355+ */
356+ protected void onReleaseResources(List<AppEntry> apps) {
357+ // For a simple List<> there is nothing to do. For something
358+ // like a Cursor, we would close it here.
359+ }
360+ }
361+//END_INCLUDE(loader)
362+
363+//BEGIN_INCLUDE(fragment)
364+ public static class AppListAdapter extends ArrayAdapter<AppEntry> {
365+ private final LayoutInflater mInflater;
366+
367+ public AppListAdapter(Context context) {
368+ super(context, android.R.layout.simple_list_item_2);
369+ mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
370+ }
371+
372+ public void setData(List<AppEntry> data) {
373+ clear();
374+ if (data != null) {
375+ addAll(data);
376+ }
377+ }
378+
379+ /**
380+ * Populate new items in the list.
381+ */
382+ @Override public View getView(int position, View convertView, ViewGroup parent) {
383+ View view;
384+
385+ if (convertView == null) {
386+ view = mInflater.inflate(R.layout.list_item_icon_text, parent, false);
387+ } else {
388+ view = convertView;
389+ }
390+
391+ AppEntry item = getItem(position);
392+ ((ImageView)view.findViewById(R.id.icon)).setImageDrawable(item.getIcon());
393+ ((TextView)view.findViewById(R.id.text)).setText(item.getLabel());
394+
395+ return view;
396+ }
397+ }
398+
399+ public static class AppListFragment extends ListFragment
400+ implements OnQueryTextListener, LoaderManager.LoaderCallbacks<List<AppEntry>> {
401+
402+ // This is the Adapter being used to display the list's data.
403+ AppListAdapter mAdapter;
404+
405+ // If non-null, this is the current filter the user has provided.
406+ String mCurFilter;
407+
408+ @Override public void onActivityCreated(Bundle savedInstanceState) {
409+ super.onActivityCreated(savedInstanceState);
410+
411+ // Give some text to display if there is no data. In a real
412+ // application this would come from a resource.
413+ setEmptyText("No applications");
414+
415+ // We have a menu item to show in action bar.
416+ setHasOptionsMenu(true);
417+
418+ // Create an empty adapter we will use to display the loaded data.
419+ mAdapter = new AppListAdapter(getActivity());
420+ setListAdapter(mAdapter);
421+
422+ // Start out with a progress indicator.
423+ setListShown(false);
424+
425+ // Prepare the loader. Either re-connect with an existing one,
426+ // or start a new one.
427+ getLoaderManager().initLoader(0, null, this);
428+ }
429+
430+ @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
431+ // Place an action bar item for searching.
432+ MenuItem item = menu.add("Search");
433+ item.setIcon(android.R.drawable.ic_menu_search);
434+ item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
435+ SearchView sv = new SearchView(getActivity());
436+ sv.setOnQueryTextListener(this);
437+ item.setActionView(sv);
438+ }
439+
440+ @Override public boolean onQueryTextChange(String newText) {
441+ // Called when the action bar search text has changed. Since this
442+ // is a simple array adapter, we can just have it do the filtering.
443+ mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
444+ mAdapter.getFilter().filter(mCurFilter);
445+ return true;
446+ }
447+
448+ @Override public boolean onQueryTextSubmit(String query) {
449+ // Don't care about this.
450+ return true;
451+ }
452+
453+ @Override public void onListItemClick(ListView l, View v, int position, long id) {
454+ // Insert desired behavior here.
455+ Log.i("LoaderCustom", "Item clicked: " + id);
456+ }
457+
458+ @Override public Loader<List<AppEntry>> onCreateLoader(int id, Bundle args) {
459+ // This is called when a new Loader needs to be created. This
460+ // sample only has one Loader with no arguments, so it is simple.
461+ return new AppListLoader(getActivity());
462+ }
463+
464+ @Override public void onLoadFinished(Loader<List<AppEntry>> loader, List<AppEntry> data) {
465+ // Set the new data in the adapter.
466+ mAdapter.setData(data);
467+
468+ // The list should now be shown.
469+ if (isResumed()) {
470+ setListShown(true);
471+ } else {
472+ setListShownNoAnimation(true);
473+ }
474+ }
475+
476+ @Override public void onLoaderReset(Loader<List<AppEntry>> loader) {
477+ // Clear the data in the adapter.
478+ mAdapter.setData(null);
479+ }
480+ }
481+//END_INCLUDE(fragment)
482+}
--- a/samples/Support4Demos/src/com/example/android/supportv4/app/LoaderThrottleSupport.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/app/LoaderThrottleSupport.java
@@ -410,6 +410,9 @@ public class LoaderThrottleSupport extends FragmentActivity {
410410 new int[] { android.R.id.text1 }, 0);
411411 setListAdapter(mAdapter);
412412
413+ // Start out with a progress indicator.
414+ setListShown(false);
415+
413416 // Prepare the loader. Either re-connect with an existing one,
414417 // or start a new one.
415418 getLoaderManager().initLoader(0, null, this);
@@ -493,6 +496,13 @@ public class LoaderThrottleSupport extends FragmentActivity {
493496
494497 public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
495498 mAdapter.swapCursor(data);
499+
500+ // The list should now be shown.
501+ if (isResumed()) {
502+ setListShown(true);
503+ } else {
504+ setListShownNoAnimation(true);
505+ }
496506 }
497507
498508 public void onLoaderReset(Loader<Cursor> loader) {
--- a/samples/Support4Demos/src/com/example/android/supportv4/app/_index.html
+++ b/samples/Support4Demos/src/com/example/android/supportv4/app/_index.html
@@ -67,6 +67,15 @@ and loaders.</p>
6767 <dd>Demonstrates creating a stack of Fragment instances similar to the
6868 traditional stack of activities.</dd>
6969
70+ <dt><a href="FragmentTabs.html">Fragment Tabs</a></dt>
71+ <dd>Demonstrates the use of fragments to implement switching between
72+ tabs in a TabHost.</dd>
73+
74+ <dt><a href="FragmentTabsPager.html">Fragment Tabs Pager</a></dt>
75+ <dd>Demonstrates the use of fragments to implement switching between
76+ tabs in a TabHost, using a ViewPager to manager the fragments so that
77+ the user can also fling left and right to switch tabs.</dd>
78+
7079 </dl>
7180
7281 <h3 id="LoaderManager">LoaderManager</h3>
@@ -74,6 +83,10 @@ and loaders.</p>
7483 <dt><a href="LoaderCursorSupport.html">Loader Cursor</a></dt>
7584 <dd>Demonstrates use of LoaderManager to perform a query for a Cursor that
7685 populates a ListFragment.</dd>
86+
87+ <dt><a href="LoaderCustomSupport.html">Loader Custom</a></dt>
88+ <dd>Demonstrates implementation and use of a custom Loader class. The
89+ custom class here "loads" the currently installed applications.</dd>
7790
7891 <dt><a href="LoaderThrottleSupport.html">Loader Throttle</a></dt>
7992 <dd>Complete end-to-end demonstration of a simple content provider that