Android系統的應用組件之一:ActionBar,是一個相當重要的組件,爲了提升用戶與移動應用的良好交互性,我們十分有必要好好學習一下它的原理及用法(例如通過點擊ActionBar的相關Item啓動系統服務或者啓動類似於微博、空間的分享功能),除此之外,利用ActionBar結合Fragment創建出類似於TabHost的效果也非常有用。
以下對本演示程序結合運行效果並進行代碼解釋,細節可參看源碼附件:
1. 手工添加ActionBar中的MenuItem和普通的MenuItem:Action Bar Mechanics
ActionBarMechanicsActivity.java
public class ActionBarMechanicsActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); /* * Action Bar屬於窗口屬性,但是但是要使用它,應該在窗口開始渲染界面之前, * 通過特定的請求激活(如下),通常情況下在AndroidManifest.xml文件對應的 * Activity節點通過設置android:theme="@android:style/Theme.WithActionBar"亦可。 * 也可以通過自定義的Theme設置自定義Action Bar, * 只需要在自定義主題中添加:<item name="android:windowActionBar">true</item>即可(略)。 */ getWindow().requestFeature(Window.FEATURE_ACTION_BAR); // (筆者嘗試註釋掉該行仍可正常運行,特此說明) setContentView(R.layout.activity_action_bar_mechanics); } @Override public boolean onCreateOptionsMenu(Menu menu) { /* * Menu Item默認情況下不會顯示在Action Bar中。對於大多數設備, * Menu Item都會在實體按鈕(menu)被按下的時候顯示。 */ menu.add("Normal Item"); MenuItem actionItem = menu.add("Action Button"); /* * 被添加的Menu Item只會在Action Bar擁有足夠的空間的時候顯示, * 爲了防止Action Bar被過量的Menu Item擠爆變得擁擠不堪,那些 * 過多的Menu Item將被放置在額外的屏幕空間內,通過點擊MENU實體按鈕可見。 */ actionItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); /* * 很多時候,我們只需要使用一些富有含義的png圖片,就可以避免因爲 * 使用文字帶來的單調,而那些富有含義的png圖片,Android OS已經爲我們提供了。 */ actionItem.setIcon(android.R.drawable.ic_menu_share); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { /* * 不論是Normal的MenuItem還是From Action Bar的MenuItem, * 它們都同時被同時監聽,利用同一個回調函數即可處理響應事件。 */ Toast.makeText(this, item.getTitle(), Toast.LENGTH_SHORT).show(); return true; } }
2.ActionBar結合Fragment創建類似於TabHost的可切換界面
ActionBarTabsActivity.java
import android.annotation.SuppressLint; import android.app.ActionBar; import android.app.ActionBar.Tab; import android.app.Activity; import android.app.Fragment; import android.app.FragmentTransaction; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import android.widget.Toast; public class ActionBarTabsActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.action_bar_tabs); } public void onAdd(View v) { ActionBar bar = getActionBar(); int tabCount = bar.getTabCount(); String text = "Tab" + tabCount; // 添加Tab,並且爲每一個Tab添加監聽器 bar.addTab(bar.newTab().setText(text) .setTabListener(new TabListener(new TabContentFragment(text)))); } public void onRemove(View v) { ActionBar bar = getActionBar(); if(bar.getTabCount() > 0) { bar.removeTabAt(bar.getTabCount() - 1); } } public void onToggle(View v) { ActionBar bar = getActionBar(); // 實現每次點擊Toggle按鈕,則接環ActionBar顯示模式 if(bar.getNavigationMode() == ActionBar.NAVIGATION_MODE_TABS) { bar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); bar.setDisplayOptions(ActionBar.DISPLAY_SHOW_TITLE, ActionBar.DISPLAY_SHOW_TITLE); } else { bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); bar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE); } } public void onClear(View v) { ActionBar bar = getActionBar(); if(bar.getTabCount() > 0) { bar.removeAllTabs(); } } @SuppressLint("ValidFragment") private class TabContentFragment extends Fragment { private String mText; @SuppressLint("ValidFragment") public TabContentFragment(String mText) { this.mText = mText; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View fragView = inflater.inflate(R.layout.action_bar_tabs_content, container, false); TextView text = (TextView) fragView.findViewById(R.id.tv_content); text.setText(mText); return fragView; } private String getText() { return this.mText; } } /** * A TabListener receives event callbacks from the action bar as tabs * are deselected, selected, and reselected. A FragmentTransaction * is provided to each of these callbacks; if any operations are added * to it, it will be committed at the end of the full tab switch operation. * This lets tab switches be atomic without the app needing to track * the interactions between different tabs. * 每個ActionBar的tab各自註冊了一個TabListener實例,當tab被點擊時或不被點擊時,其對應的監聽器 * 執行事件對應的回調函數。此時會產生一個實例FragmentTransaction ft用於處理tab切換的事務。 * 這使得整個應用本身不需要在全局的角度去處理不同的tab之間的切換。 */ private class TabListener implements ActionBar.TabListener { private TabContentFragment mFragment; public TabListener(TabContentFragment mFragment) { this.mFragment = mFragment; } @Override public void onTabSelected(Tab tab, FragmentTransaction ft) { // 把當前this.mFragment添加到佈局視圖R.id.fragment_content,同時設置tab的標籤名爲this.mFragment.getText() ft.add(R.id.fragment_content, this.mFragment, this.mFragment.getText()); } @Override public void onTabUnselected(Tab tab, FragmentTransaction ft) { ft.remove(this.mFragment); } @Override public void onTabReselected(Tab tab, FragmentTransaction ft) { Toast.makeText(ActionBarTabsActivity.this, "Reselected..." , Toast.LENGTH_SHORT).show(); } } }
R.layout.action_bar_tabs.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <FrameLayout android:id="@+id/fragment_content" android:layout_width="match_parent" android:layout_height="0dip" android:layout_weight="1" /> <LinearLayout android:layout_width="match_parent" android:layout_height="0dip" android:layout_weight="1" android:orientation="vertical"> <Button android:id="@+id/btn_add_tab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Add Tab" android:onClick="onAdd"/> <Button android:id="@+id/btn_remove_tab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Remove Last Tab" android:onClick="onRemove"/> <Button android:id="@+id/btn_toggle_tabs" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Toggle Tabs" android:onClick="onToggle"/> <Button android:id="@+id/btn_clear_tabs" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Clear All Tabs" android:onClick="onClear"/> </LinearLayout> </LinearLayout>
3.使用ActionBar處理菜單事件
ActionBarUsageActivity.java
import android.app.Activity; import android.os.Bundle; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.widget.SearchView; import android.widget.Toast; import android.widget.SearchView.OnQueryTextListener; import android.widget.TextView; public class ActionBarUsageActivity extends Activity implements OnQueryTextListener { TextView mSearchText; // 顯示事件響應效果 int mSortMode = -1; // 保存排序模式,默認爲-1 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mSearchText = new TextView(this); setContentView(mSearchText); } @Override public boolean onCreateOptionsMenu(Menu menu) { // 取得MenuInflater,用於將xml資源轉換成Menu實例 MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.actions, menu); // 取得id爲R.id.action_search的MenuItem,由於xml文件中已經指定android:actionViewClass="android.widget.SearchView" // 所以通過getActionView()便可以顯式轉換爲SearchView SearchView searchView = (SearchView)menu.findItem(R.id.action_search).getActionView(); // 註冊搜索輸入欄的事件監聽器,來自於實現接口:android.widget.SearchView.OnQueryTextListener // 事件回調方法爲:onQueryTextSubmit()開始搜索事件;onQueryTextChange()文本改變事件 searchView.setOnQueryTextListener(this); return true; } @Override public boolean onQueryTextSubmit(String query) { Toast.makeText(this, query, Toast.LENGTH_SHORT).show(); return true; } @Override public boolean onQueryTextChange(String newText) { this.mSearchText.setText(newText); return true; } @Override public boolean onMenuItemSelected(int featureId, MenuItem item) { Toast.makeText(this, item.getTitle(), Toast.LENGTH_SHORT).show(); return true; } /** * 在xml文件中顯式聲明的點擊事件回調函數:android:onClick="onSort" * @param item */ public void onSort(MenuItem item) { this.mSortMode = item.getItemId(); // 發送變更請求,通知Menu更新其內容,響應方法爲onPrepareOptionsMenu(Menu menu) invalidateOptionsMenu(); } @Override public boolean onPrepareOptionsMenu(Menu menu) { if(this.mSortMode != -1) { String strMode = menu.findItem(this.mSortMode).getTitle().toString(); menu.findItem(R.id.action_sort).setTitle("Sort: " + strMode); } return super.onPrepareOptionsMenu(menu); } }
R.menu.actions.xml
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" > <item android:id="@+id/action_search" android:title="Search" android:icon="@android:drawable/ic_menu_search" android:showAsAction="always" android:actionViewClass="android.widget.SearchView"/> <item android:id="@+id/action_add" android:title="Add" android:icon="@android:drawable/ic_menu_add" /> <item android:id="@+id/action_edit" android:title="Edit" android:icon="@android:drawable/ic_menu_edit" android:showAsAction="always" /> <item android:id="@+id/action_share" android:title="Share" android:icon="@android:drawable/ic_menu_share" android:showAsAction="always"/> <item android:id="@+id/action_sort" android:title="Sort: Size" android:icon="@android:drawable/ic_menu_sort_by_size" android:showAsAction="ifRoom"> <menu> <item android:id="@+id/action_sort_size" android:title="Size" android:icon="@android:drawable/ic_menu_sort_by_size" android:onClick="onSort"/> <item android:id="@+id/action_sort_alpha" android:title="Alphabetically" android:icon="@android:drawable/ic_menu_sort_alphabetically" android:onClick="onSort"/> </menu> </item> </menu>
4.ActionBar創建ActionProvider
ActionBarProviderActivity.java
import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.provider.Settings; import android.view.ActionProvider; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.ImageButton; import android.widget.ShareActionProvider; import android.widget.Toast; public class ActionBarProviderActivity extends Activity { private static final String SHARED_FILE_NAME = "share.png"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); copyPrivateRawResourceToPubliclyAccessibleFile(); } @Override public boolean onCreateOptionsMenu(Menu menu) { // 加載xml文件中的各種MenuItem getMenuInflater().inflate(R.menu.action_bar_action_provider, menu); // 通過ID取得對應MenuItem MenuItem actionItem = menu.findItem(R.id.menu_item_share_action_provider_action_bar); // 取得在xml中聲明的Action Provider Class實例 ShareActionProvider actionProvider = (ShareActionProvider)actionItem.getActionProvider(); // 設置默認的共享文件名: public static final String DEFAULT_SHARE_HISTORY_FILE_NAME = "share_history.xml"; actionProvider.setShareHistoryFileName(ShareActionProvider.DEFAULT_SHARE_HISTORY_FILE_NAME); // 設置共享意圖 actionProvider.setShareIntent(createShareIntent()); MenuItem overflowItem = menu.findItem(R.id.menu_item_share_action_provider_overflow); ShareActionProvider overflowProvider = (ShareActionProvider)overflowItem.getActionProvider(); overflowProvider.setShareHistoryFileName(ShareActionProvider.DEFAULT_SHARE_HISTORY_FILE_NAME); overflowProvider.setShareIntent(createShareIntent()); return true; } private Intent createShareIntent() { Intent shareIntent = new Intent(Intent.ACTION_SEND); shareIntent.setType("iamge/*"); /* * Returns the absolute path on the filesystem * where a file created with openFileOutput is stored. * getFileStreamPath("share.png")可以從被保存在文件系統的文件輸出流中 * 讀取對應文件名的絕對路徑 */ Uri uri = Uri.fromFile(getFileStreamPath(SHARED_FILE_NAME)); // 爲Intent附加資源 shareIntent.putExtra(Intent.EXTRA_STREAM, uri); return shareIntent; } private void copyPrivateRawResourceToPubliclyAccessibleFile() { InputStream inputStream = null; FileOutputStream fileOutputStream = null; try { // Open a data stream for reading a raw resource inputStream = getResources().openRawResource(R.raw.robot); // Open a private file associated with this Context's application package for writing. // Creates the file if it doesn't already exist. fileOutputStream = openFileOutput( SHARED_FILE_NAME, Context.MODE_WORLD_READABLE | Context.MODE_APPEND); byte[] buffer = new byte[1024]; int len = 0; while((len = inputStream.read(buffer)) != -1) { fileOutputStream.write(buffer, 0, len); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { inputStream.close(); fileOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } @Override public boolean onMenuItemSelected(int featureId, MenuItem item) { /* * 如果被點擊的MenuItem存在於ActionBar,那麼此方法將不會被調用。 * 因爲對於被點擊的MenuItem的整個事件監聽以及處理,都被封裝到 * Provider activity中了,即由我們對ImageButton設定的監聽器處理。 * 那麼如果被點擊的MenuItem不存在於ActionBar中呢?請參看內部 * 靜態類型SettingSActionProvider的onPerformDefaultAction方法, * 注意:此時的返回值應該爲false,讀者可以嘗試將其修改爲true查看有趣的運行結果。 */ Toast.makeText(this, "From host activity: " + item.getTitle(), Toast.LENGTH_SHORT).show(); return false; } public static class SettingSActionProvider extends ActionProvider { private static final Intent mSettingsIntent = new Intent(Settings.ACTION_SETTINGS); private final Context mContext; public SettingSActionProvider(Context context) { super(context); // Call requires API level 14 this.mContext = context; } @Override public View onCreateActionView() { // 取得當前上下文的LayoutInflater實例 LayoutInflater inflater = LayoutInflater.from(mContext); // 將xml形式的MenuItem轉換爲View實例 View view = inflater.inflate(R.layout.action_bar_settings_action_provider, null); // 取得按鈕控件 ImageButton imgbSettings = (ImageButton) view.findViewById(R.id.imgb_setting); imgbSettings.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 當按鈕被點擊,執行Activity跳轉 mContext.startActivity(mSettingsIntent); } }); return view; } @Override public boolean onPerformDefaultAction() { /* * 爲了體驗此方法的作用效果,我們還需要爲當前host activity添加一個 * 不存在ActionBar中的MenuItem,通過點擊實體MENU顯現。 * 當被點擊的MenuItem不存在於ActionBar中的時候,並且host activity顯式 * 聲明不會負責處理響應事件,即onMenuItemSelected()返回值爲false,此時的 * 響應事件在此執行處理。 * 其實也可將mContext.startActivity(mSettingsIntent);放置在onMenuItemSelected()中的return之前, * 但是這麼幹就不能體現創建自定義SettingSActionProvider類型的優勢(組件複用)所在了。 */ mContext.startActivity(mSettingsIntent); return true; } } }
R.menu.action_bar_action_provider.xml
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" > <item android:id="@+id/menu_item_setting_action_provider_action_bar" android:title="Setting" android:showAsAction="ifRoom" android:actionProviderClass="com.panny.actionbardemo.ActionBarProviderActivity$SettingSActionProvider"/> <item android:id="@+id/menu_item_setting_action_provider_overflow" android:title="Setting" android:showAsAction="never" android:actionProviderClass="com.panny.actionbardemo.ActionBarProviderActivity$SettingSActionProvider"/> <item android:id="@+id/menu_item_share_action_provider_action_bar" android:title="Share" android:showAsAction="always" android:actionProviderClass="android.widget.ShareActionProvider" /> <item android:id="@+id/menu_item_share_action_provider_overflow" android:title="Share" android:showAsAction="never" android:actionProviderClass="android.widget.ShareActionProvider" /> </menu>
說明:ShareActionProvider功能需要在真機上運行纔能有效果
5.關於ActionBar內容的相關操作
ActionBarDisplayOptionsActivity.java
import android.app.ActionBar; import android.app.ActionBar.Tab; import android.app.ActionBar.TabListener; import android.app.Activity; import android.app.FragmentTransaction; import android.os.Bundle; import android.util.Log; import android.view.Gravity; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup.LayoutParams; public class ActionBarDisplayOptionsActivity extends Activity implements OnClickListener, TabListener { private static final String TAG = "ActionBarDisplayOptionsActivity"; private View mCustomView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.action_bar_display_opstions); findViewById(R.id.btn_cycle_custom_gravity).setOnClickListener(this); findViewById(R.id.btn_home_as_up).setOnClickListener(this); findViewById(R.id.btn_show_custom).setOnClickListener(this); findViewById(R.id.btn_show_home).setOnClickListener(this); findViewById(R.id.btn_show_title).setOnClickListener(this); findViewById(R.id.btn_use_logo).setOnClickListener(this); findViewById(R.id.btn_navigation).setOnClickListener(this); this.mCustomView = getLayoutInflater().inflate(R.layout.action_bar_display_opstions_custom, null); ActionBar bar = getActionBar(); bar.setCustomView(this.mCustomView, new ActionBar.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); bar.addTab(bar.newTab().setText("Tab 1").setTabListener(this)); bar.addTab(bar.newTab().setText("Tab 2").setTabListener(this)); bar.addTab(bar.newTab().setText("Tab 3").setTabListener(this)); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.display_options_menu_item, menu); return true; } @Override public void onClick(View v) { ActionBar bar = getActionBar(); int flags = 0; switch (v.getId()) { case R.id.btn_home_as_up: flags = ActionBar.DISPLAY_HOME_AS_UP; break; case R.id.btn_show_home: flags = ActionBar.DISPLAY_SHOW_HOME; break; case R.id.btn_use_logo: flags = ActionBar.DISPLAY_USE_LOGO; break; case R.id.btn_show_title: flags = ActionBar.DISPLAY_SHOW_TITLE; break; case R.id.btn_show_custom: flags = ActionBar.DISPLAY_SHOW_CUSTOM; break; case R.id.btn_navigation: bar.setNavigationMode(bar.getNavigationMode() == ActionBar.NAVIGATION_MODE_STANDARD? ActionBar.NAVIGATION_MODE_TABS:ActionBar.NAVIGATION_MODE_STANDARD); return; case R.id.btn_cycle_custom_gravity: ActionBar.LayoutParams lp = (android.app.ActionBar.LayoutParams) this.mCustomView.getLayoutParams(); int newGravity = 0; switch (lp.gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) { case Gravity.START: newGravity = Gravity.CENTER_HORIZONTAL; break; case Gravity.CENTER_HORIZONTAL: newGravity = Gravity.END; break; case Gravity.END: newGravity = Gravity.START; break; } // 這樣的寫法看起來有點晦澀,其實也可以簡單一點:lp.gravity = newGravity; lp.gravity = lp.gravity & ~Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK | newGravity; Log.i(TAG, "lp.gravity:" + lp.gravity); bar.setCustomView(this.mCustomView, lp); return; } Log.i(TAG, "bar.getDisplayOptions(): " + bar.getDisplayOptions()); Log.i(TAG, "flags: " + flags); int change = bar.getDisplayOptions() ^ flags; Log.i(TAG, "change: " + change); bar.setDisplayOptions(change, flags); } @Override public void onTabSelected(Tab tab, FragmentTransaction ft) { // TODO Auto-generated method stub } @Override public void onTabUnselected(Tab tab, FragmentTransaction ft) { // TODO Auto-generated method stub } @Override public void onTabReselected(Tab tab, FragmentTransaction ft) { // TODO Auto-generated method stub } }
R.layout.action_bar_display_opstions.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/btn_show_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Show Title" /> <Button android:id="@+id/btn_show_home" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Show Home" /> <Button android:id="@+id/btn_home_as_up" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Show Home As Up" /> <Button android:id="@+id/btn_use_logo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Show Loogo" /> <Button android:id="@+id/btn_show_custom" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Show Custom" /> <Button android:id="@+id/btn_cycle_custom_gravity" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Cycle Custom Gravity" /> <Button android:id="@+id/btn_navigation" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Navigation" /> </LinearLayout>
R.layout.action_bar_display_opstions_custom.xml
<?xml version="1.0" encoding="utf-8"?> <Button xmlns:android="http://schemas.android.com/apk/res/android" android:text="Custom" />
本演示程序的AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.panny.actionbardemo" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="17" /> <uses-permission android:name="android.permission.SEND_SMS" /> <uses-permission android:name="android.permission.RECEIVE_SMS" /> <instrumentation android:targetPackage="com.panny.actionbardemo" android:name="android.test.InstrumentationTestRunner"></instrumentation> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="com.panny.actionbardemo.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="com.panny.actionbardemo.ActionBarMechanicsActivity" android:label="@string/title_activity_action_bar_mechanics" > </activity> <activity android:name="com.panny.actionbardemo.ActionBarUsageActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.SAMPLE_CODE" /> </intent-filter> </activity> <activity android:name="com.panny.actionbardemo.ActionBarProviderActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.SAMPLE_CODE" /> </intent-filter> </activity> <activity android:name="com.panny.actionbardemo.ActionBarTabsActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.SAMPLE_CODE" /> </intent-filter> </activity> <activity android:name="com.panny.actionbardemo.ActionBarDisplayOptionsActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.SAMPLE_CODE" /> </intent-filter> </activity> <uses-library android:name="android.test.runner"/> </application> </manifest>