第一部分:ActionBar重要知识点汇总
1.什么ActionBar
主要用于向用户当前Activity的标题、导航按钮或者其它动作,使用它,统一了样式和更好适应不同的屏幕;
ActionBar 在Android3.0 API 11开始使用,通过Support Library在2.1(API 7)版本及其以上可以使用;
如果仅支持3.0以上设备,导入
import android.app.ActionBar
支持2.1以上的
,
import android.support.v7.app.ActionBar
public abstract class ActionBar extends Object : android.app.ActionBar抽象类,不是视图view
2.给Activity添加ActionBar
两点操作:
Activity继承ActionBarActivity
同时,Activity主题设置为
<activity
android:theme
=
"@style/Theme.AppCompat.Light"
...
>(该主题的子类也可以)
3.动态移除ActionBar
//API 11以上, getActionBar即可
ActionBar actionBar = getSupportActionBar();
actionBar.hide();
actionBar.show(); //显示
注意:动态移除或显示,会引发重新布局,如果不需要该操作,可以考虑overlay mode
设置:windowActionBarOverlay=true
4.ActionBar选项菜单
ActionBar上面有各种图标和文字按钮,还有个 overflow Button(不够空间或者不重要的,收缩)
Activity通过onCreateOptionsMenu()方法填充ActionBar的Items;
logo代替icon:默认显示icon,在<application>和<activity>的logo属性设置
在menu res定义所有的Action Items;
res/menu/main_activity_actions.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:id="@+id/action_search"
android:icon="@drawable/ic_action_search"
android:title="@string/action_search"/>
<item android:id="@+id/action_compose"
android:icon="@drawable/ic_action_compose"
android:title="@string/action_compose" />
</menu>
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu items for use in the action bar
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main_activity_actions, menu);
return super.onCreateOptionsMenu(menu);
}
yourapp:名字自己起的,因为是支持库,系统没有内置showAsAction属性,所以需要自定义空间
showAsAction: ifRoom如果有空间,就直接显示在ActionBar上,没有空间,就收缩起来
<menu
xmlns:android
=
"http://schemas.android.com/apk/res/android"
xmlns:yourapp="http://schemas.android.com/apk/res-auto"
>
<item android:id="@+id/action_search"
android:icon="@drawable/ic_action_search"
android:title="@string/action_search"
yourapp:showAsAction="ifRoom" />
...
</menu>
如果有空间同时显示文字:
<item yourapp:showAsAction="ifRoom|withText" ... />
如果想让某个Item一直出现ActionButton,设置为 always, 尽量不用该选项,避免狭窄屏幕问题
Action Item事件:
Activity首先处理ItemSelected事件,Fragment在后面;
当Activity不做处理时,应该调用super,以让其他Fragment有机会处理,而不是仅仅返回false
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle presses on the action bar items
switch (item.getItemId()) {
case R.id.action_search:
openSearch();
return true;
case R.id.action_compose:
composeMessage();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
5.使用分割的ActionBar
当手机屏幕狭窄时,在屏幕底部提供单独的bar,显示所有的Action Items
<application> <activity> 中设置
uiOptions="splitActionBarWhenNarrow"
支持老版本,使用<meta-data>
<manifest ...>
<activity uiOptions="splitActionBarWhenNarrow" ... >
<meta-data android:name="android.support.UI_OPTIONS"
android:value="splitActionBarWhenNarrow" />
</activity>
</manifest>
通过设置,允许收缩Action Bar(顶部的)
setDisplayShowHomeEnabled(false) and setDisplayShowTitleEnabled(false).
6.向前返回 导航
Up导航不同于Back,Back由系统按照screen历史记录,up根据应用的层次结构设置;
(1)让app icon成为Up Button,设置
ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
(2)定义Up Parent Activity:两种方式
<activity
android:name="com.example.myfirstapp.DisplayMessageActivity"
android:label="@string/title_activity_display_message"
android:parentActivityName="com.example.myfirstapp.MainActivity" >
<!-- Parent activity meta-data to support API level 7+ -->
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.example.myfirstapp.MainActivity" />
</activity>
或者重写,适合每个页面返回不一样的Activity
getSupportParentActivityIntent()
onCreateSupportNavigateUpTaskStack()
7.添加Action View: Action Button的替代:一定注意使用yourapp,否则低版本的应用无法显示;
通过actionLayout或者actionViewClass定义
添加一个搜索框
<?
xml version
=
"1.0"
encoding
=
"utf-8"
?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:yourapp="http://schemas.android.com/apk/res-auto" >
<item android:id="@+id/action_search"
android:title="@string/action_search"
android:icon="@drawable/ic_action_search"
yourapp:showAsAction="ifRoom|collapseActionView"
yourapp:actionViewClass="android.support.v7.widget.SearchView" />
</menu>
注意:collapseActionView 必须添加,说明该view被收缩
获取ActionView:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main_activity_actions, menu);
MenuItem searchItem = menu.findItem(R.id.action_search);
SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
// Configure the search info and add any event listeners
...
return super.onCreateOptionsMenu(menu);
}
处理收缩的Action View:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.options, menu);
MenuItem menuItem = menu.findItem(R.id.actionItem);
...
// When using the support library, the setOnActionExpandListener() method is
// static and accepts the MenuItem object as an argument
MenuItemCompat.setOnActionExpandListener(menuItem, new OnActionExpandListener() {
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
// Do something when collapsed
return true; // Return true to collapse action view
}
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
// Do something when expanded
return true; // Return true to expand action view
}
});
}
8.添加 Action Provider
和Action View类似,但是可以完全控制Action的行为,可以定义每个按钮的子菜单
使用内置的ActionProvider或者继承ActionProvider
每个ActionProvider,可以定义自己的行为,无须在Activity中定义onOptionsItemsSelected,如果定义,不要返回true,返回false,以便于其它ActionProvider处理;
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:yourapp="http://schemas.android.com/apk/res-auto" >
<item android:id="@+id/action_share"
android:title="@string/share"
yourapp:showAsAction="ifRoom"
yourapp:actionProviderClass="android.support.v7.widget.ShareActionProvider"
/>
...
</menu>
private ShareActionProvider mShareActionProvider;
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main_activity_actions, menu);
// Set up ShareActionProvider's default share intent
MenuItem shareItem = menu.findItem(R.id.action_share);
mShareActionProvider = (ShareActionProvider)
MenuItemCompat.getActionProvider(shareItem);
mShareActionProvider.setShareIntent(getDefaultIntent());
return super.onCreateOptionsMenu(menu);
}
/** Defines a default (dummy) share intent to initialize the action provider.
* However, as soon as the actual content to be used in the intent
* is known or changes, you must update the share intent by again calling
* mShareActionProvider.setShareIntent()
*/
private Intent getDefaultIntent() {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("image/*");
return intent;
}
9.创建定制的ActionProvider
方便重用和管理ActionItem的行为,不需要在Fragment或者Activity中处理;
继承ActionProvider,实现回调方法即可:
ActionProvider(Context context): 构造器,会传递一个context,可以保存到一个字段中,方便使用
onCreateActionView(MenuItem): 对于每一个的MenuItem,创建ActionView
public View onCreateActionView(MenuItem forItem) {
// Inflate the action view to be shown on the action bar.
LayoutInflater layoutInflater = LayoutInflater.from(mContext);
View view = layoutInflater.inflate(R.layout.action_provider, null);
ImageButton button = (ImageButton) view.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Do something...
}
});
return view;
}
onPerformDefaultAction():当action overflow的菜单项被选择,调用执行默认的Action
如果Action View有子菜单,onPrepareSubMenu()调用,不会调用该方法;
如果在Activity或者Fragment中已经处理 onOptionsItemSelected() (返回true的话),也不会调用;
10.添加导航的Tab
布局中必须有一个Id的ViewGroup,绑定Fragment和Tab
如果每个Tab内容填充整个屏幕,可以使用内置的布局,通过android.R.id.content 引用
基本过程:
实现ActionBar.TabListener接口,响应用户点击切换tab事件;【滑动切换,用ViewPager】
动态添加每个Tab: 初始化ActionBar.Tab, setTabListener, setText,setIcon,addTab
getSelectedNavigationIndex():获取tab索引位置
注意:系统调用commit,自己不用调用,调用将失败;不能够将tab压入stack中
public static class TabListener<T extends Fragment> implements ActionBar.TabListener {
private Fragment mFragment;
private final Activity mActivity;
private final String mTag;
private final Class<T> mClass;
/** Constructor used each time a new tab is created.
* @param activity The host Activity, used to instantiate the fragment
* @param tag The identifier tag for the fragment
* @param clz The fragment's Class, used to instantiate the fragment
*/
public TabListener(Activity activity, String tag, Class<T> clz) {
mActivity = activity;
mTag = tag;
mClass = clz;
}
/* The following are each of theActionBar.TabListener
callbacks */
public void onTabSelected(Tab tab, FragmentTransaction ft) {
// Check if the fragment is already initialized
if (mFragment == null) {
// If not, instantiate and add it to the activity
mFragment = Fragment.instantiate(mActivity, mClass.getName());
ft.add(android.R.id.content, mFragment, mTag);
} else {
// If it exists, simply attach it in order to show it
ft.attach(mFragment);
}
}
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
if (mFragment != null) {
// Detach the fragment, because another one is being attached
ft.detach(mFragment);
}
}
public void onTabReselected(Tab tab, FragmentTransaction ft) {
// User selected the already selected tab. Usually do nothing.
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Notice that setContentView() is not used, because we use the root
// android.R.id.content as the container for each fragment
// setup action bar for tabs
ActionBar actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
actionBar.setDisplayShowTitleEnabled(false);
Tab tab = actionBar.newTab()
.setText(R.string.artist)
.setTabListener(new TabListener<ArtistFragment>(
this, "artist", ArtistFragment.class));
actionBar.addTab(tab);
tab = actionBar.newTab()
.setText(R.string.album)
.setTabListener(new TabListener<AlbumFragment>(
this, "album", AlbumFragment.class));
actionBar.addTab(tab);
}
11.添加下拉列表式导航
当导航的内容不是经常切换的时候,使用下拉列表式(spinner),如果内容经常切换,使用tab
创建下拉列表的基本过程:
- SpinnerAdapter:提供数据项和布局视图
- 实现ActionBar.onNavigationListener接口,监听列表项选择事件
- 在Activity的onCreate方法,设置ActionBar的mode,setNavigationMode(NAVIGATION_MODE_LIST).
- actionBar.setListNavigationCallbacks(mSpinnerAdapter, mNavigationCallback);
SpinnerAdapter mSpinnerAdapter = ArrayAdapter.createFromResource(this,
R.array.action_list, android.R.layout.simple_spinner_dropdown_item);mOnNavigationListener = new OnNavigationListener() {
// Get the same strings provided for the drop-down's ArrayAdapter
String[] strings = getResources().getStringArray(R.array.action_list);
@Override
public boolean onNavigationItemSelected(int position, long itemId) {
// Create new fragment from our own Fragment class
ListContentFragment newFragment = new ListContentFragment();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
// Replace whatever is in the fragment container with this fragment
// and give the fragment a tag name equal to the string at the position
// selected
ft.replace(R.id.fragment_container, newFragment, strings[position]);
// Apply changes
ft.commit();
return true;
}
};public class ListContentFragment extends Fragment {
private String mText;
@Override
public void onAttach(Activity activity) {
// This is the first callback received; here we can set the text for
// the fragment as defined by the tag specified during the fragment
// transaction
super.onAttach(activity);
mText = getTag();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// This is called to define the layout for the fragment;
// we just create a TextView and set its text to be the fragment tag
TextView text = new TextView(getActivity());
text.setText(mText);
return text;
}
}
第二部分:ViewPager组件的使用
1.public class ViewPager extends android.view.ViewGroup
android.support.v4.view.ViewPager
布局管理器,实现通过滑动切换页面,提供接口PagerAdapter的实现产生Page
ViewPager经常与Fragment联合使用,并提供标准的Adapter:FragmentPagerAdapter和FragmentStatePagerAdapter
2.public abstract class PagerAdapter extends Object [android.support.v4.view.PagerAdapter]
抽象类,提供基本接口,产生ViewPager需要的page,子类必须实现抽象方法或者重写:
Object instantiateItem(ViewGroup container, int position):创建给定位置的视图Page
void destroyItem(ViewGroup container, int position, Object object):移除给定位置的Page
abstract boolean isViewFromObject(View view, Object object):判断Page和返回的对象是否相等,return view == object
abstract int getCount():返回可用的Pager数目
ViewPager关联Page到一个Object,而不是视图View,调用的过程:
startUpdate(ViewGroup):表明即将开始更新页面
instantiateItem(ViewGroup, int) and/or destroyItem(ViewGroup, int, Object):多次调用
finishUpdate(ViewGroup):结束更新,返回对象;
每当Adapter发生改变,应该 notifyDataSetChanged() ,通知更新;
两个直接抽象子类:FragmentPagerAdapter和FragmentStatePagerAdapter
FragmentPagerAdapter:实现了PagerAdapter,并且返回的每个Page是Fragment,适合静态的一定量的页面,对于大量的页面集合,使用FragmentStatePagerAdapter
只需要实现的getItem(int)和getCount()方法接口,同时ViewPager必须有一个有效的Id
FragmentStatePagerAdapter:返回Fragment,同时处理保存和恢复Fragment的状态,适合保存大量的Page,工作方式类似于ListView,当Fragment不可见,将可能完全销毁,仅仅保存状态,相对于前面的Adapter,使用更少的内存;
实例代码:
- public class FragmentPagerSupport extends FragmentActivity {
static final int NUM_ITEMS = 10;
MyAdapter mAdapter;
ViewPager mPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_pager);
mAdapter = new MyAdapter(getSupportFragmentManager());
mPager = (ViewPager)findViewById(R.id.pager);
mPager.setAdapter(mAdapter);
// Watch for button clicks.
Button button = (Button)findViewById(R.id.goto_first);
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
mPager.setCurrentItem(0);
}
});
button = (Button)findViewById(R.id.goto_last);
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
mPager.setCurrentItem(NUM_ITEMS-1);
}
});
}
public static class MyAdapter extends FragmentPagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
@Override
public int getCount() {
return NUM_ITEMS;
}
@Override
public Fragment getItem(int position) {
return ArrayListFragment.newInstance(position);
}
}
public static class ArrayListFragment extends ListFragment {
int mNum;
/**
* Create a new instance of CountingFragment, providing "num"
* as an argument.
*/
static ArrayListFragment newInstance(int num) {
ArrayListFragment f = new ArrayListFragment();
// Supply num input as an argument.
Bundle args = new Bundle();
args.putInt("num", num);
f.setArguments(args);
return f;
}
/**
* When creating, retrieve this instance's number from its arguments.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mNum = getArguments() != null ? getArguments().getInt("num") : 1;
}
/**
* The Fragment's UI is just a simple text view showing its
* instance number.
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_pager_list, container, false);
View tv = v.findViewById(R.id.text);
((TextView)tv).setText("Fragment #" + mNum);
return v;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setListAdapter(new ArrayAdapter<String>(getActivity(),
android.R.layout.simple_list_item_1, Cheeses.sCheeseStrings));
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
Log.i("FragmentList", "Item clicked: " + id);
}
}
}The
R.layout.fragment_pager
resource of the top-level fragment is:<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:padding="4dip"
android:gravity="center_horizontal"
android:layout_width="match_parent" android:layout_height="match_parent">
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="1">
</android.support.v4.view.ViewPager>
<LinearLayout android:orientation="horizontal"
android:gravity="center" android:measureWithLargestChild="true"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:layout_weight="0">
<Button android:id="@+id/goto_first"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="@string/first">
</Button>
<Button android:id="@+id/goto_last"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="@string/last">
</Button>
</LinearLayout>
</LinearLayout>The
R.layout.fragment_pager_list
resource containing each individual fragment's layout is:<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:drawable/gallery_thumb">
<TextView android:id="@+id/text"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:gravity="center_vertical|center_horizontal"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="@string/hello_world"/>
<!-- The frame layout is here since we will be showing either
the empty view or the list view. -->
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1" >
<!-- Here is the list. Since we are using a ListActivity, we
have to call it "@android:id/list" so ListActivity will
find it -->
<ListView android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:drawSelectorOnTop="false"/>
<!-- Here is the view to show if the list is emtpy -->
<TextView android:id="@android:id/empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="No items."/>
</FrameLayout>
</LinearLayout>
3.使用方法,经常与Tab ActionBar结合,支持点击和滑动换页,具体见 csdn博客android微信实例开发
使用要点:
【关键是Tab点击事件和ViewPager滑动选择事件联合起来】
XML或代码定义ViewPager,但是必须设置有效的id;
设置ActionBar的tab导航模式;
实现ActionBar.TabListener:选择事件改变当前的ViewPager的页面
实现FragmentPagerAdapter,和Fragment对象;
实现ViewPager.OnPagerChangeListener:滑动事件改变当前的ViewPager页面和Tab选择情况
public static interface ActionBar.TabListener
abstract void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft)
abstract void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft)
abstract void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft)
public static interface ViewPager.OnPageChangeListener:
abstract void onPageScrollStateChanged(int state) :
当scroll滚动的状态发生改变时,可以发现用户什么时候开始拖动、正在拖动设置中、以及完成拖动设置好页面;
三种状态:SCROLL_STATE_IDLE(0)、SCROLL_STATE_DRAGGING(1) 、SCROLL_STATE_SETTLING(2)
public abstract void onPageScrolled (int position, float positionOffset, int positionOffsetPixels):
当前的页面被拖动时调用,position当前拖动的页面index,offset不为0,则position+1可见,取值范围[0,1)
public abstract void onPageSelected (int position):
当一个新的已经被选择后,调用(动画不必完成)
刚刚启动ViewPager时,会调用一次onPageScrolled方法,当滑动切换时,上述三个方法的调用顺序是:
开始点击滑动时,调用onPageScrollStateChanged一次(此时状态为SCROLL_STATE_DRAGGING),然后一直调用onPageScrolled计算滑动的位置(多次调用),当你放手,结束滑动,调用onPageScrollStateChanged一次(此时状态为SCROLL_STATE_SETTLING),然后调用onPageSelected一次(确定选中的Page),接着还要调用onPageScrolled多次,滑动到指定的Page,然后再次调用onPageScrollStateChanged一次(此时状态为SCROLL_STATE_IDLE),具体见图,当滑动“聊天”到“发现”页面时,截图;