效果圖:
要實現的效果如圖所示,簡單歸納:
- 安置一個底部菜單欄,共有4個功能選項。
- 4個功能選項分別聯繫4個不同內容的fragment。
- 支持通過滑動屏幕的方式,完成fragment的切換。
- 支持通過類似tabhost,點擊選項卡的方式,完成fragment的切換。
- 我們會有一個用於擺放主界面控件的佈局文件,對應的自然有一個MainActivity.java文件。
- 底部菜單中,共有4個功能選項,其結構都同樣爲一個ImageView(Function-Icon)以及一個TextView(Function-Title)。所以爲了避免重複的編碼工作,我們將其抽離出來,單獨封裝成“bottom_menu_item_view.xml”。
- 4個功能選項分別聯繫4個功能界面,所以我們還需要用於顯示這4個功能界面的Fragment,所以我們還會有4個fragment佈局文件及fragment類。
- 爲了達到“通過滑動屏幕切換功能界面”的目的,我們使用了ViewPager。對應的,那麼我們自然會需要一個“PagerAdapter”。在這裏需要注意的是,因爲我們選擇了使用“ViewPager+Fragment”的方式,這種情況android推薦使用“FragmentPagerAdapter”。
- 最後我們注意到,當我們正常滑動屏幕切換界面時,一切都很美好。但是,假設目前用戶正停留在“聊天”界面中,而此時他通過點擊“我”的功能選項卡選擇進入到最後一個功能界面,這個時候ViewPager會顯示從“聊天”到“我”多個view的滑動切換效果,這看上去始終有點彆扭。所以,我們定義了一個“ViewPagerScroll”的類,通過反射的方式控制ViewPager滑動效果的時間,當滑動橫跨多個界面時,我們將滑動時間設爲0,以此取消掉滑動效果。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="bottom"
android:orientation="vertical"
tools:context="com.tsr.bigmousechating.AppMainActivity" >
<android.support.v4.view.ViewPager
android:id="@+id/app_main_viewpager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<!-- 邊框 -->
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_gravity="bottom"
android:background="@color/lightgrey" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom" >
<LinearLayout
android:id="@+id/bottom_menu_chats"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical" >
</LinearLayout>
<LinearLayout
android:id="@+id/bottom_menu_contacts"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical" >
</LinearLayout>
<LinearLayout
android:id="@+id/bottom_menu_discover"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical" >
</LinearLayout>
<LinearLayout
android:id="@+id/bottom_menu_me"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical" >
</LinearLayout>
</LinearLayout>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical"
android:padding="2dp" >
<ImageView
android:id="@+id/tab_item_icon"
android:layout_width="30dp"
android:layout_height="30dp" />
<TextView
android:id="@+id/tab_item_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="2dp"
android:textSize="12sp" />
</LinearLayout>
接下來,定義好4個fragment的佈局文件以及類文件,這裏因爲定義十分基礎,不貼代碼了。
public class AppMainActivity extends FragmentActivity {
// 視圖轉換器對象
private LayoutInflater mInflater;
// 存放底部菜單中的四個選項卡的layout
private LinearLayout mBottomMenuChats,
mBottomMenuContacts,
mBottomMenuDiscover,
mBottomMenuMe;
private LinearLayout[] mBottomMenuItemViews;
// 底部菜單欄 選項卡標題
private int[] mMenuTitles = new int[] {
R.string.bottom_menu_chats_item,
R.string.bottom_menu_contacts_item,
R.string.bottom_menu_discover_item,
R.string.bottom_menu_me_item };
// 底部菜單欄 選項卡圖標(非選中狀態)
private int[] mMenuNormalIcons = new int[] {
R.drawable.bottom_menu_chats_normal,
R.drawable.bottom_menu_contacts_normal,
R.drawable.bottom_menu_discover_normal,
R.drawable.bottom_menu_me_normal };
// 底部菜單欄 選項卡圖標(選中狀態)
private int[] mMenuSelectedIcons = new int[] {
R.drawable.bottom_menu_chats_selected,
R.drawable.bottom_menu_contacts_selected,
R.drawable.bottom_menu_discover_selected,
R.drawable.bottom_menu_me_selected };
// 用於滑動切換視圖的viewPager
private ViewPager mViewPager;
// 用於控制viewPager滑動效果
private ViewPagerScroller mScroller;
// viewPager的適配器(在viewPager配合fragment使用時,應使用FragmentPagerAdapter)
private MainBottomMenuFragmentPagerAdapter mAdapter;
// 需要裝載進viewPager中的各個頁面內容
private ArrayList<Fragment> mPagerContents = new ArrayList<Fragment>();
//
private MainBottomMenuFragmentPagerAdapter.PageChangerListener mPageChangeListener = new MainBottomMenuFragmentPagerAdapter.PageChangerListener(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_app_main);
// 初始化頁面控件顯示
initView();
}
private void initView() {
// 獲取視圖轉換器對象
mInflater = LayoutInflater.from(this);
// 底部菜單的各個選項卡的初始化工作
mBottomMenuChats = (LinearLayout) this.findViewById(R.id.bottom_menu_chats);
mBottomMenuChats.setOnClickListener(new MyPageChangeListener(PAGE_CHATS_INDEX));
mBottomMenuContacts = (LinearLayout) this.findViewById(R.id.bottom_menu_contacts);
mBottomMenuContacts.setOnClickListener(new MyPageChangeListener(PAGE_CONTACTS_INDEX));
mBottomMenuDiscover = (LinearLayout) this.findViewById(R.id.bottom_menu_discover);
mBottomMenuDiscover.setOnClickListener(new MyPageChangeListener(PAGE_DISCOVER_INDEX));
mBottomMenuMe = (LinearLayout) this.findViewById(R.id.bottom_menu_me);
mBottomMenuMe.setOnClickListener(new MyPageChangeListener(PAGE_ME_INDEX));
// 將選項卡對象存放進一個數組,方便根據數組下標對選項卡的內容進行裝載
mBottomMenuItemViews = new LinearLayout[] { mBottomMenuChats, mBottomMenuContacts, mBottomMenuDiscover,
mBottomMenuMe };
// 裝載選項卡顯示內容
for (int index = 0; index < mBottomMenuItemViews.length; index++) {
initBottomMenuItemView(index);
}
// 接着進行ViewPager的實例化工作
initViewPager();
}
private void initViewPager() {
mViewPager = (ViewPager) this.findViewById(R.id.app_main_viewpager);
mScroller = new ViewPagerScroller(this);
mScroller.initViewPagerScroll(mViewPager);
// 裝載頁面內容
mPagerContents.add(new AppChatsFragment());
mPagerContents.add(new AppContactsFragment());
mPagerContents.add(new AppDiscoverFragment());
mPagerContents.add(new AppMeFragment());
// 爲viewPager綁定適配器
mAdapter = new MainBottomMenuFragmentPagerAdapter(getSupportFragmentManager(), mPagerContents);
mViewPager.setAdapter(mAdapter);
// 設置當前選中的界面
mViewPager.setCurrentItem(PAGE_DEFAULT_INDEX,false);
// 綁定頁面改變的監聽事件
mViewPager.addOnPageChangeListener(mPageChangeListener);
}
/**
* 給Tab按鈕設置圖標和文字
*/
private void initBottomMenuItemView(int index) {
View view = mInflater.inflate(R.layout.bottom_menu_item_view, null);
ImageView imageView = (ImageView) view.findViewById(R.id.tab_item_icon);
int imageSourceID = index == PAGE_DEFAULT_INDEX ? mMenuSelectedIcons[index] : mMenuNormalIcons[index];
imageView.setImageResource(imageSourceID);
TextView textView = (TextView) view.findViewById(R.id.tab_item_title);
textView.setText(mMenuTitles[index]);
mBottomMenuItemViews[index].addView(view);
}
/**
* 回調函數,用於在監聽器監聽到page改變時,更新選項卡的圖標顯示
* @param beforeIndex 之前選中的菜單選項
* @param selectedIndex 此次選中的菜單選項
*/
public void onMenuItemSelected(int beforeIndex, int selectedIndex) {
ImageView beforeIcon = (ImageView) mBottomMenuItemViews[beforeIndex].findViewById(R.id.tab_item_icon);
beforeIcon.setImageResource(mMenuNormalIcons[beforeIndex]);
ImageView selectedIcon = (ImageView) mBottomMenuItemViews[selectedIndex].findViewById(R.id.tab_item_icon);
selectedIcon.setImageResource(mMenuSelectedIcons[selectedIndex]);
}
/**
* 選項卡點擊事件的監聽器
* @author TSR
*/
private class MyPageChangeListener implements View.OnClickListener {
private int pageIndex;
public MyPageChangeListener(int pageIndex) {
this.pageIndex = pageIndex;
}
@Override
public void onClick(View v) {
int duration = Math.abs(mPageChangeListener.getmCurrentPageIndex() - pageIndex) > 1 ? 0 : 2500;
mScroller.setScrollDuration(duration);
mViewPager.setCurrentItem(pageIndex);
}
}
public ViewPagerScroller getmScroller() {
return mScroller;
}
public static final int PAGE_CHATS_INDEX = 0;
public static final int PAGE_CONTACTS_INDEX = 1;
public static final int PAGE_DISCOVER_INDEX = 2;
public static final int PAGE_ME_INDEX = 3;
public static final int PAGE_DEFAULT_INDEX = PAGE_CHATS_INDEX;
}
我們在AppMainActivity中用到了ViewPager,所以我們接着根據自己的需求定義適配器類:
public class MainBottomMenuFragmentPagerAdapter extends FragmentPagerAdapter {
private ArrayList<Fragment> fragmentList;
public MainBottomMenuFragmentPagerAdapter(FragmentManager fm, ArrayList<Fragment> fragmentList) {
super(fm);
this.fragmentList = fragmentList;
}
@Override
public Fragment getItem(int index) {
return fragmentList.get(index);
}
@Override
public int getCount() {
return fragmentList.size();
}
/**
* 頁卡切換監聽
*/
public static class PageChangerListener implements OnPageChangeListener {
private int mCurrentPageIndex;
private AppMainActivity mContext;
public PageChangerListener(AppMainActivity context) {
this.mContext = context;
mCurrentPageIndex = AppMainActivity.PAGE_DEFAULT_INDEX;
}
@Override
public void onPageScrollStateChanged(int arg0) {
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
// TODO Auto-generated method stub
}
@Override
public void onPageSelected(int pageIndex) {
mContext.getmScroller().setScrollDuration(2000);
mContext.onMenuItemSelected(mCurrentPageIndex, pageIndex);
mCurrentPageIndex = pageIndex;
}
public int getmCurrentPageIndex() {
return mCurrentPageIndex;
}
}
}
到這裏,我們的工作其實已經算完成了。但因爲我們想要當ViewPager的滑動經過多個頁面時,取消掉滑動效果。所以,我們還需要定義一個Scroller類,來控制ViewPager的滑動動畫時間。
/**
* ViewPager 滾動速度設置
*
*/
public class ViewPagerScroller extends Scroller {
private int mScrollDuration = 2000; // 滑動速度
/**
* 設置速度速度
*
* @param duration
*/
public void setScrollDuration(int duration) {
this.mScrollDuration = duration;
}
public ViewPagerScroller(Context context) {
super(context);
}
public ViewPagerScroller(Context context, Interpolator interpolator) {
super(context, interpolator);
}
public ViewPagerScroller(Context context, Interpolator interpolator, boolean flywheel) {
super(context, interpolator, flywheel);
}
@Override
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
super.startScroll(startX, startY, dx, dy, mScrollDuration);
}
@Override
public void startScroll(int startX, int startY, int dx, int dy) {
super.startScroll(startX, startY, dx, dy, mScrollDuration);
}
public void initViewPagerScroll(ViewPager viewPager) {
try {
Field mScroller = ViewPager.class.getDeclaredField("mScroller");
mScroller.setAccessible(true);
mScroller.set(viewPager, this);
} catch (Exception e) {
e.printStackTrace();
}
}
}
到此,我們的工作就完畢了。