am e7df2e3f: am f6457d05: Merge "Retroactively add EffectiveNavigation and TabCompat sample code." into jb-dev

* commit 'e7df2e3f6eb1f847cc362f70b062fc76e7825d30':
  Retroactively add EffectiveNavigation and TabCompat sample code.
This commit is contained in:
Roman Nurik
2012-08-23 14:11:40 -07:00
committed by Android Git Automerger
49 changed files with 1517 additions and 0 deletions

View File

@@ -0,0 +1,38 @@
<!--
Copyright 2012 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.effectivenavigation"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="14" />
<application android:label="@string/app_name"
android:icon="@drawable/ic_launcher"
android:theme="@android:style/Theme.Holo.Light.DarkActionBar">
<activity android:name=".MainActivity" android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".CollectionDemoActivity" android:label="@string/demo_collection" />
</application>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@@ -0,0 +1,35 @@
<!--
Copyright 2012 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.
-->
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--
This title strip will display the currently visible page title, as well as the page
titles for adjacent pages.
-->
<android.support.v4.view.PagerTitleStrip android:id="@+id/pager_title_strip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:background="#33b5e5"
android:textColor="#fff"
android:paddingTop="4dp"
android:paddingBottom="4dp" />
</android.support.v4.view.ViewPager>

View File

@@ -0,0 +1,20 @@
<!--
Copyright 2012 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.
-->
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />

View File

@@ -0,0 +1,23 @@
<!--
Copyright 2012 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.
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:textSize="128sp"
android:padding="32dp" />

View File

@@ -0,0 +1,23 @@
<!--
Copyright 2012 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.
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:textSize="24sp"
android:padding="32dp" />

View File

@@ -0,0 +1,39 @@
<!--
Copyright 2012 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.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center">
<Button android:id="@+id/demo_collection_button"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:text="@string/demo_collection"/>
<Button android:id="@+id/demo_external_activity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/demo_external_activity" />
</LinearLayout>
</FrameLayout>

View File

@@ -0,0 +1,24 @@
<!--
Copyright 2012 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.
-->
<resources>
<string name="app_name">Effective Navigation</string>
<string name="dummy_section_text">Section %1d is just a dummy section.</string>
<string name="demo_external_activity">Demo External Activity</string>
<string name="demo_collection">Demo Collection</string>
</resources>

View File

@@ -0,0 +1,148 @@
/*
* Copyright 2012 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.effectivenavigation;
import android.app.ActionBar;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.app.NavUtils;
import android.support.v4.app.TaskStackBuilder;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class CollectionDemoActivity extends FragmentActivity {
/**
* The {@link android.support.v4.view.PagerAdapter} that will provide fragments representing
* each object in a collection. We use a {@link android.support.v4.app.FragmentStatePagerAdapter}
* derivative, which will destroy and re-create fragments as needed, saving and restoring their
* state in the process. This is important to conserve memory and is a best practice when
* allowing navigation between objects in a potentially large collection.
*/
DemoCollectionPagerAdapter mDemoCollectionPagerAdapter;
/**
* The {@link android.support.v4.view.ViewPager} that will display the object collection.
*/
ViewPager mViewPager;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_collection_demo);
// Create an adapter that when requested, will return a fragment representing an object in
// the collection.
//
// ViewPager and its adapters use support library fragments, so we must use
// getSupportFragmentManager.
mDemoCollectionPagerAdapter = new DemoCollectionPagerAdapter(getSupportFragmentManager());
// Set up action bar.
final ActionBar actionBar = getActionBar();
// Specify that the Home button should show an "Up" caret, indicating that touching the
// button will take the user one step up in the application's hierarchy.
actionBar.setDisplayHomeAsUpEnabled(true);
// Set up the ViewPager, attaching the adapter.
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mDemoCollectionPagerAdapter);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
// This is called when the Home (Up) button is pressed in the action bar.
// Create a simple intent that starts the hierarchical parent activity and
// use NavUtils in the Support Package to ensure proper handling of Up.
Intent upIntent = new Intent(this, MainActivity.class);
if (NavUtils.shouldUpRecreateTask(this, upIntent)) {
// This activity is not part of the application's task, so create a new task
// with a synthesized back stack.
TaskStackBuilder.from(this)
// If there are ancestor activities, they should be added here.
.addNextIntent(upIntent)
.startActivities();
finish();
} else {
// This activity is part of the application's task, so simply
// navigate up to the hierarchical parent activity.
NavUtils.navigateUpTo(this, upIntent);
}
return true;
}
return super.onOptionsItemSelected(item);
}
/**
* A {@link android.support.v4.app.FragmentStatePagerAdapter} that returns a fragment
* representing an object in the collection.
*/
public static class DemoCollectionPagerAdapter extends FragmentStatePagerAdapter {
public DemoCollectionPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int i) {
Fragment fragment = new DemoObjectFragment();
Bundle args = new Bundle();
args.putInt(DemoObjectFragment.ARG_OBJECT, i + 1); // Our object is just an integer :-P
fragment.setArguments(args);
return fragment;
}
@Override
public int getCount() {
// For this contrived example, we have a 100-object collection.
return 100;
}
@Override
public CharSequence getPageTitle(int position) {
return "OBJECT " + (position + 1);
}
}
/**
* A dummy fragment representing a section of the app, but that simply displays dummy text.
*/
public static class DemoObjectFragment extends Fragment {
public static final String ARG_OBJECT = "object";
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_collection_object, container, false);
Bundle args = getArguments();
((TextView) rootView.findViewById(android.R.id.text1)).setText(
Integer.toString(args.getInt(ARG_OBJECT)));
return rootView;
}
}
}

View File

@@ -0,0 +1,204 @@
/*
* Copyright 2012 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.effectivenavigation;
import android.app.ActionBar;
import android.app.FragmentTransaction;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class MainActivity extends FragmentActivity implements ActionBar.TabListener {
/**
* The {@link android.support.v4.view.PagerAdapter} that will provide fragments for each of the
* three primary sections of the app. We use a {@link android.support.v4.app.FragmentPagerAdapter}
* derivative, which will keep every loaded fragment in memory. If this becomes too memory
* intensive, it may be best to switch to a {@link android.support.v4.app.FragmentStatePagerAdapter}.
*/
AppSectionsPagerAdapter mAppSectionsPagerAdapter;
/**
* The {@link ViewPager} that will display the three primary sections of the app, one at a
* time.
*/
ViewPager mViewPager;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Create the adapter that will return a fragment for each of the three primary sections
// of the app.
mAppSectionsPagerAdapter = new AppSectionsPagerAdapter(getSupportFragmentManager());
// Set up the action bar.
final ActionBar actionBar = getActionBar();
// Specify that the Home/Up button should not be enabled, since there is no hierarchical
// parent.
actionBar.setHomeButtonEnabled(false);
// Specify that we will be displaying tabs in the action bar.
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
// Set up the ViewPager, attaching the adapter and setting up a listener for when the
// user swipes between sections.
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mAppSectionsPagerAdapter);
mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
// When swiping between different app sections, select the corresponding tab.
// We can also use ActionBar.Tab#select() to do this if we have a reference to the
// Tab.
actionBar.setSelectedNavigationItem(position);
}
});
// For each of the sections in the app, add a tab to the action bar.
for (int i = 0; i < mAppSectionsPagerAdapter.getCount(); i++) {
// Create a tab with text corresponding to the page title defined by the adapter.
// Also specify this Activity object, which implements the TabListener interface, as the
// listener for when this tab is selected.
actionBar.addTab(
actionBar.newTab()
.setText(mAppSectionsPagerAdapter.getPageTitle(i))
.setTabListener(this));
}
}
@Override
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
}
@Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
// When the given tab is selected, switch to the corresponding page in the ViewPager.
mViewPager.setCurrentItem(tab.getPosition());
}
@Override
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
}
/**
* A {@link FragmentPagerAdapter} that returns a fragment corresponding to one of the primary
* sections of the app.
*/
public static class AppSectionsPagerAdapter extends FragmentPagerAdapter {
public AppSectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int i) {
switch (i) {
case 0:
// The first section of the app is the most interesting -- it offers
// a launchpad into the other demonstrations in this example application.
return new LaunchpadSectionFragment();
default:
// The other sections of the app are dummy placeholders.
Fragment fragment = new DummySectionFragment();
Bundle args = new Bundle();
args.putInt(DummySectionFragment.ARG_SECTION_NUMBER, i + 1);
fragment.setArguments(args);
return fragment;
}
}
@Override
public int getCount() {
return 3;
}
@Override
public CharSequence getPageTitle(int position) {
return "Section " + (position + 1);
}
}
/**
* A fragment that launches other parts of the demo application.
*/
public static class LaunchpadSectionFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_section_launchpad, container, false);
// Demonstration of a collection-browsing activity.
rootView.findViewById(R.id.demo_collection_button)
.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(getActivity(), CollectionDemoActivity.class);
startActivity(intent);
}
});
// Demonstration of navigating to external activities.
rootView.findViewById(R.id.demo_external_activity)
.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// Create an intent that asks the user to pick a photo, but using
// FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET, ensures that relaunching
// the application from the device home screen does not return
// to the external activity.
Intent externalActivityIntent = new Intent(Intent.ACTION_PICK);
externalActivityIntent.setType("image/*");
externalActivityIntent.addFlags(
Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
startActivity(externalActivityIntent);
}
});
return rootView;
}
}
/**
* A dummy fragment representing a section of the app, but that simply displays dummy text.
*/
public static class DummySectionFragment extends Fragment {
public static final String ARG_SECTION_NUMBER = "section_number";
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_section_dummy, container, false);
Bundle args = getArguments();
((TextView) rootView.findViewById(android.R.id.text1)).setText(
getString(R.string.dummy_section_text, args.getInt(ARG_SECTION_NUMBER)));
return rootView;
}
}
}

View File

@@ -0,0 +1,32 @@
<!--
Copyright 2012 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.tabcompat"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="5" android:targetSdkVersion="14" />
<application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
<activity android:name=".MainActivity" android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 676 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 824 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 513 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 853 B

View File

@@ -0,0 +1,22 @@
<!--
Copyright 2012 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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_tab_photos_selected"
android:state_selected="true"
android:state_pressed="false" />
<item android:drawable="@drawable/ic_tab_photos_unselected" />
</selector>

View File

@@ -0,0 +1,22 @@
<!--
Copyright 2012 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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_tab_videos_selected"
android:state_selected="true"
android:state_pressed="false" />
<item android:drawable="@drawable/ic_tab_videos_unselected" />
</selector>

View File

@@ -0,0 +1,21 @@
<!--
Copyright 2012 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.
-->
<!-- API level 11+ only needs the container for tab content. -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/tabcontent"
android:layout_width="match_parent"
android:layout_height="match_parent" />

View File

@@ -0,0 +1,40 @@
<!--
Copyright 2012 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.
-->
<!-- This layout is for API level 5-10 only. -->
<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/tabhost"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TabWidget
android:id="@android:id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
</TabHost>

View File

@@ -0,0 +1,22 @@
<!--
Copyright 2012 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.
-->
<resources>
<string name="app_name">Tab Demo</string>
<string name="tab_photos">Photos</string>
<string name="tab_videos">Videos</string>
</resources>

View File

@@ -0,0 +1,129 @@
/*
* Copyright 2012 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.tabcompat;
import com.example.android.tabcompat.lib.CompatTab;
import com.example.android.tabcompat.lib.CompatTabListener;
import com.example.android.tabcompat.lib.TabCompatActivity;
import com.example.android.tabcompat.lib.TabHelper;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class MainActivity extends TabCompatActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
TabHelper tabHelper = getTabHelper();
CompatTab photosTab = tabHelper.newTab("photos")
.setText(R.string.tab_photos)
.setIcon(R.drawable.ic_tab_photos)
.setTabListener(new InstantiatingTabListener(this, PhotosFragment.class));
tabHelper.addTab(photosTab);
CompatTab videosTab = tabHelper.newTab("videos")
.setText(R.string.tab_videos)
.setIcon(R.drawable.ic_tab_videos)
.setTabListener(new InstantiatingTabListener(this, VideosFragment.class));
tabHelper.addTab(videosTab);
}
/**
* Implementation of {@link CompatTabListener} to handle tab change events. This implementation
* instantiates the specified fragment class with no arguments when its tab is selected.
*/
public static class InstantiatingTabListener implements CompatTabListener {
private final TabCompatActivity mActivity;
private final Class mClass;
/**
* Constructor used each time a new tab is created.
*
* @param activity The host Activity, used to instantiate the fragment
* @param cls The class representing the fragment to instantiate
*/
public InstantiatingTabListener(TabCompatActivity activity, Class<? extends Fragment> cls) {
mActivity = activity;
mClass = cls;
}
/* The following are each of the ActionBar.TabListener callbacks */
@Override
public void onTabSelected(CompatTab tab, FragmentTransaction ft) {
// Check if the fragment is already initialized
Fragment fragment = tab.getFragment();
if (fragment == null) {
// If not, instantiate and add it to the activity
fragment = Fragment.instantiate(mActivity, mClass.getName());
tab.setFragment(fragment);
ft.add(android.R.id.tabcontent, fragment, tab.getTag());
} else {
// If it exists, simply attach it in order to show it
ft.attach(fragment);
}
}
@Override
public void onTabUnselected(CompatTab tab, FragmentTransaction ft) {
Fragment fragment = tab.getFragment();
if (fragment != null) {
// Detach the fragment, because another one is being attached
ft.detach(fragment);
}
}
@Override
public void onTabReselected(CompatTab tab, FragmentTransaction ft) {
// User selected the already selected tab. Do nothing.
}
}
public static class PhotosFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
TextView textView = new TextView(getActivity());
textView.setGravity(Gravity.CENTER);
textView.setText(R.string.tab_photos);
return textView;
}
}
public static class VideosFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
TextView textView = new TextView(getActivity());
textView.setGravity(Gravity.CENTER);
textView.setText(R.string.tab_videos);
return textView;
}
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2012 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.tabcompat.lib;
import android.graphics.drawable.Drawable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
/**
* Represents a single tab.
* The {@link TabHelper} initializes one of the subclasses of this based
* on the current platform version, upon call to {@link TabHelper#newTab(String)}()
*/
public abstract class CompatTab {
final FragmentActivity mActivity;
final String mTag;
protected CompatTab(FragmentActivity activity, String tag) {
mActivity = activity;
mTag = tag;
}
public abstract CompatTab setText(int resId);
public abstract CompatTab setIcon(int resId);
public abstract CompatTab setTabListener(CompatTabListener callback);
public abstract CompatTab setFragment(Fragment fragment);
public abstract CharSequence getText();
public abstract Drawable getIcon();
public abstract CompatTabListener getCallback();
public abstract Fragment getFragment();
public abstract Object getTab();
public String getTag() {
return mTag;
}
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright 2012 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.tabcompat.lib;
import android.graphics.drawable.Drawable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
/**
* A base implementation of the {@link CompatTab} interface.
*/
public class CompatTabEclair extends CompatTab {
private CompatTabListener mCallback;
private CharSequence mText;
private Drawable mIcon;
private Fragment mFragment;
protected CompatTabEclair(FragmentActivity activity, String tag) {
super(activity, tag);
}
@Override
public CompatTab setText(int resId) {
mText = mActivity.getResources().getText(resId);
return this;
}
@Override
public CompatTab setIcon(int resId) {
mIcon = mActivity.getResources().getDrawable(resId);
return this;
}
@Override
public CompatTab setTabListener(CompatTabListener callback) {
mCallback = callback;
return this;
}
@Override
public CompatTab setFragment(Fragment fragment) {
mFragment = fragment;
return this;
}
@Override
public Fragment getFragment() {
return mFragment;
}
@Override
public CharSequence getText() {
return mText;
}
@Override
public Drawable getIcon() {
return mIcon;
}
@Override
public Object getTab() {
return null;
}
@Override
public CompatTabListener getCallback() {
return mCallback;
}
}

View File

@@ -0,0 +1,116 @@
/*
* Copyright 2012 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.tabcompat.lib;
import android.app.ActionBar;
import android.app.ActionBar.Tab;
import android.graphics.drawable.Drawable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTransaction;
/**
* An implementation of the {@link CompatTab} interface that relies on API 11 APIs.
*/
public class CompatTabHoneycomb extends CompatTab implements ActionBar.TabListener {
/**
* The native tab object that this {@link CompatTab} acts as a proxy for.
*/
ActionBar.Tab mTab;
CompatTabListener mCallback;
Fragment mFragment;
protected CompatTabHoneycomb(FragmentActivity activity, String tag) {
super(activity, tag);
mTab = activity.getActionBar().newTab();
}
@Override
public CompatTab setText(int resId) {
mTab.setText(resId);
return this;
}
@Override
public CompatTab setIcon(int resId) {
mTab.setIcon(resId);
return this;
}
@Override
public CompatTab setTabListener(CompatTabListener callback) {
mCallback = callback;
mTab.setTabListener(this);
return this;
}
@Override
public CharSequence getText() {
return mTab.getText();
}
@Override
public Drawable getIcon() {
return mTab.getIcon();
}
@Override
public Object getTab() {
return mTab;
}
@Override
public CompatTabListener getCallback() {
return mCallback;
}
@Override
public void onTabReselected(Tab tab, android.app.FragmentTransaction f) {
FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction();
ft.disallowAddToBackStack();
mCallback.onTabReselected(this, ft);
ft.commit();
}
@Override
public void onTabSelected(Tab tab, android.app.FragmentTransaction f) {
FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction();
ft.disallowAddToBackStack();
mCallback.onTabSelected(this, ft);
ft.commit();
}
@Override
public void onTabUnselected(Tab arg0, android.app.FragmentTransaction f) {
FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction();
ft.disallowAddToBackStack();
mCallback.onTabUnselected(this, ft);
ft.commit();
}
@Override
public CompatTab setFragment(Fragment fragment) {
mFragment = fragment;
return this;
}
@Override
public Fragment getFragment() {
return mFragment;
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2012 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.tabcompat.lib;
import android.support.v4.app.FragmentTransaction;
/**
* @see android.app.ActionBar.TabListener
*/
public interface CompatTabListener {
/**
* @see android.app.ActionBar.TabListener#onTabSelected(
*android.app.ActionBar.Tab, android.app.FragmentTransaction)
*/
public void onTabSelected(CompatTab tab, FragmentTransaction ft);
/**
* @see android.app.ActionBar.TabListener#onTabUnselected(
*android.app.ActionBar.Tab, android.app.FragmentTransaction)
*/
public void onTabUnselected(CompatTab tab, FragmentTransaction ft);
/**
* @see android.app.ActionBar.TabListener#onTabReselected(
*android.app.ActionBar.Tab, android.app.FragmentTransaction)
*/
public void onTabReselected(CompatTab tab, FragmentTransaction ft);
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2012 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.tabcompat.lib;
import com.example.android.tabcompat.lib.TabHelper;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
/**
* A base activity that defers tab functionality to a {@link TabHelper}.
*
* When building an activity with tabs, extend this class in order to provide compatibility with API
* level 5 and above. Using this class along with the {@link TabHelper} and {@link com.example.android.tabcompat.lib.CompatTab}
* classes, you can build a tab UI that's built using the {@link android.app.ActionBar} on
* Honeycomb+ and the {@link android.widget.TabWidget} on all older versions.
*
* The {@link TabHelper} APIs obfuscate all the compatibility work for you.
*/
public abstract class TabCompatActivity extends FragmentActivity {
TabHelper mTabHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mTabHelper = TabHelper.createInstance(this);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mTabHelper.onSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
mTabHelper.onRestoreInstanceState(savedInstanceState);
}
/**
* Returns the {@link TabHelper} for this activity.
*/
protected TabHelper getTabHelper() {
mTabHelper.setUp();
return mTabHelper;
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright 2012 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.tabcompat.lib;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
/**
* Convenience helper to build a set of tabs for a {@link TabCompatActivity}. To use this class,
* extend {@link TabCompatActivity} and:
*
* Call {@link TabCompatActivity#getTabHelper()}, returning a {@link TabHelper}.
*
* Create a {@link CompatTabListener}.
*
* Call {@link TabHelper#newTab(String)} to create each tab.
*
* Call CompatTab.setText().setIcon().setTabListener() to set up your tabs.
*
* Call {@link TabHelper#addTab(CompatTab)} for each tab, and you're done.
*/
public abstract class TabHelper {
protected FragmentActivity mActivity;
protected TabHelper(FragmentActivity activity) {
mActivity = activity;
}
/**
* Factory method for creating TabHelper objects for a given activity. Depending on which device
* the app is running, either a basic helper or Honeycomb-specific helper will be returned.
* Don't call this yourself; the TabCompatActivity instantiates one. Instead call
* TabCompatActivity.getTabHelper().
*/
public static TabHelper createInstance(FragmentActivity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
return new TabHelperHoneycomb(activity);
} else {
return new TabHelperEclair(activity);
}
}
/**
* Create a new tab.
*
* @param tag A unique tag to associate with the tab and associated fragment
* @return CompatTab for the appropriate android version
*/
public CompatTab newTab(String tag) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
return new CompatTabHoneycomb(mActivity, tag);
} else {
return new CompatTabEclair(mActivity, tag);
}
}
public abstract void addTab(CompatTab tab);
protected abstract void onSaveInstanceState(Bundle outState);
protected abstract void onRestoreInstanceState(Bundle savedInstanceState);
protected abstract void setUp();
}

View File

@@ -0,0 +1,156 @@
/*
* Copyright 2012 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.tabcompat.lib;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTransaction;
import android.view.View;
import android.widget.TabHost;
import android.widget.TabHost.TabSpec;
import java.util.HashMap;
/**
* This is a helper class to build tabs on pre-Honeycomb. Call {@link
* TabCompatActivity#getTabHelper()} to get the generic instance for
* compatibility with other versions.
*
* It implements a generic mechanism for associating fragments with the tabs in a tab host. It
* relies on a trick: Normally a tab host has a simple API for supplying a View or Intent that each
* tab will show. This is not sufficient for switching between fragments. So instead we make the
* content part of the tab host 0dp high (it is not shown) and this supplies its own dummy view to
* show as the tab content. It listens to changes in tabs, then passes the event back to the tab's
* callback interface so the activity can take care of switching to the correct fragment.
*/
public class TabHelperEclair extends TabHelper implements TabHost.OnTabChangeListener {
private final HashMap<String, CompatTab> mTabs = new HashMap<String, CompatTab>();
private TabHost mTabHost;
CompatTabListener mCallback;
CompatTab mLastTab;
protected TabHelperEclair(FragmentActivity activity) {
super(activity);
mActivity = activity;
}
@Override
protected void setUp() {
if (mTabHost == null) {
mTabHost = (TabHost) mActivity.findViewById(android.R.id.tabhost);
mTabHost.setup();
mTabHost.setOnTabChangedListener(this);
}
}
@Override
public void addTab(CompatTab tab) {
String tag = tab.getTag();
TabSpec spec;
if (tab.getIcon() != null) {
spec = mTabHost.newTabSpec(tag).setIndicator(tab.getText(), tab.getIcon());
} else {
spec = mTabHost.newTabSpec(tag).setIndicator(tab.getText());
}
spec.setContent(new DummyTabFactory(mActivity));
// Check to see if we already have a fragment for this tab, probably
// from a previously saved state. If so, deactivate it, because our
// initial state is that a tab isn't shown.
Fragment fragment = mActivity.getSupportFragmentManager().findFragmentByTag(tag);
tab.setFragment(fragment);
if (fragment != null && !fragment.isDetached()) {
FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction();
ft.detach(fragment);
ft.commit();
}
mTabs.put(tag, tab);
mTabHost.addTab(spec);
}
/**
* Converts the basic "tab changed" event for TabWidget into the three possible events for
* CompatTabListener: selected, unselected, reselected.
*/
@Override
public void onTabChanged(String tabId) {
CompatTab newTab = mTabs.get(tabId);
FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction();
if (mLastTab != newTab) {
if (mLastTab != null) {
if (mLastTab.getFragment() != null) {
// Pass the unselected event back to the tab's CompatTabListener
mLastTab.getCallback().onTabUnselected(mLastTab, ft);
}
}
if (newTab != null) {
// Pass the selected event back to the tab's CompatTabListener
newTab.getCallback().onTabSelected(newTab, ft);
}
mLastTab = newTab;
} else {
// Pass the re-selected event back to the tab's CompatTabListener
newTab.getCallback().onTabReselected(newTab, ft);
}
ft.commit();
mActivity.getSupportFragmentManager().executePendingTransactions();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
// Save and restore the selected tab for rotations/restarts.
outState.putString("tab", mTabHost.getCurrentTabTag());
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
if (savedInstanceState != null) {
mTabHost.setCurrentTabByTag(savedInstanceState.getString("tab"));
}
}
/**
* Backwards-compatibility mumbo jumbo
*/
static class DummyTabFactory implements TabHost.TabContentFactory {
private final Context mContext;
public DummyTabFactory(Context context) {
mContext = context;
}
@Override
public View createTabContent(String tag) {
View v = new View(mContext);
v.setMinimumWidth(0);
v.setMinimumHeight(0);
return v;
}
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright 2012 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.tabcompat.lib;
import android.app.ActionBar;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTransaction;
/**
* Helper class to build tabs on Honeycomb. Call {@link TabCompatActivity#getTabHelper()}
* to get the generic instance for compatibility with older versions.
*/
public class TabHelperHoneycomb extends TabHelper {
ActionBar mActionBar;
protected TabHelperHoneycomb(FragmentActivity activity) {
super(activity);
}
@Override
protected void setUp() {
if (mActionBar == null) {
mActionBar = mActivity.getActionBar();
mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
}
}
@Override
public void addTab(CompatTab tab) {
String tag = tab.getTag();
// Check to see if we already have a fragment for this tab, probably
// from a previously saved state. If so, deactivate it, because our
// initial state is that a tab isn't shown.
Fragment fragment = mActivity.getSupportFragmentManager().findFragmentByTag(tag);
tab.setFragment(fragment);
if (fragment != null && !fragment.isDetached()) {
FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction();
ft.detach(fragment);
ft.commit();
}
if (tab.getCallback() == null) {
throw new IllegalStateException("CompatTab must have a CompatTabListener");
}
// We know tab is a CompatTabHoneycomb instance, so its
// native tab object is an ActionBar.Tab.
mActionBar.addTab((ActionBar.Tab) tab.getTab());
}
@Override
protected void onSaveInstanceState(Bundle outState) {
int position = mActionBar.getSelectedTab().getPosition();
outState.putInt("tab_position", position);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
int position = savedInstanceState.getInt("tab_position");
mActionBar.setSelectedNavigationItem(position);
}
}