Android仿小米商城底部導航欄之二(BottomNavigationBar、ViewPager和Fragment的聯動使用)
簡介
在前文《Android仿小米商城底部導航欄(基於BottomNavigationBar)》我們使用BottomNavigationBar控制元件模仿實現了小米商城底部導航欄效果。接下來更進一步的,我們將通過BottomNavigationBar控制元件和ViewPager空間的聯動使用來實現主介面的滑動導航。
導航是移動應用最重要的方面之一,對使用者體驗是良好還是糟糕起著至關重要的作用。好的導航可以讓一款應用更加易用並且讓使用者快速上手。相反,糟糕的應用導航很容易讓人討厭,並遭到使用者的拋棄。為了打造流暢的使用者導航體驗,我們不得不依賴智慧手機最常見的一個功能:觸控。
觸控改變應用程式的檢視是現在最流行一種導航設計。本文中,我們將經過必要的幾步來實現應用內的橫向滑動導航與底部導航的聯動。
基本原理
我們的MainActivity包含了ViewPager元件,它封裝了幾個不同的子介面,每一介面有一個不同的Fragment。我們要做的第一件事就是宣告一個FragmentPagerAdapter,用它在不用的介面Fragment間切換。
ViewPager 如其名所述,是負責翻頁的一個 View。準確說是一個 ViewGroup,包含多個 View 頁,在手指橫向滑動螢幕時,其負責對 View 進行切換。為了生成這些 View 頁,需要提供一個 PagerAdapter 來進行和資料繫結以及生成最終的 View 頁。
ViewPager 通過 setAdapter() 來建立與 PagerAdapter 的聯絡。這個聯絡是雙向的,一方面,ViewPager 會擁有 PagerAdapter 物件,從而可以在需要時呼叫 PagerAdapter 的方法;另一方面,ViewPager 會在 setAdapter() 中呼叫 PagerAdapter 的 registerDataSetObserver() 方法,註冊一個自己生成的 PagerObserver 物件,從而在 PagerAdapter 有所需要時(如 notifyDataSetChanged()或 notifyDataSetInvalidated() 時),可以呼叫 Observer 的 onChanged() 或 onInvalidated() 方法,從而實現 PagerAdapter 向 ViewPager 方向傳送資訊。
PageAdapter 是 ViewPager 的支持者,ViewPager 將呼叫它來取得所需顯示的頁,而 PageAdapter 也會在資料變化時,通知 ViewPager。這個類也是FragmentPagerAdapter 以及 FragmentStatePagerAdapter 的基類。如果繼承自該類,至少需要實現 instantiateItem(), destroyItem(), getCount() 以及 isViewFromObject()。
FragmentPagerAdapter 繼承自 PagerAdapter。相比通用的 PagerAdapter,該類更專注於每一頁均為 Fragment 的情況。如文件所述,該類內的每一個生成的 Fragment 都將儲存在記憶體之中,因此適用於那些相對靜態的頁,數量也比較少的那種;如果需要處理有很多頁,並且資料動態性較大、佔用記憶體較多的情況,應該使用FragmentStatePagerAdapter。FragmentPagerAdapter 過載實現了幾個必須的函式,因此來自 PagerAdapter 的函式,我們只需要實現 getCount(),即可。且,由於 FragmentPagerAdapter.instantiateItem() 的實現中,呼叫了一個新增的虛擬函式 getItem(),因此,我們還至少需要實現一個 getItem()。因此,總體上來說,相對於繼承自 PagerAdapter,更方便一些。
設定ViewPager的監聽事件
當ViewPager的某個頁面被選中時,需要相應的選中BottomNavigationBar控制元件的對應標籤。
viewPager.addOnPageChangeListener(this);
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
bottomNavigationBar.selectTab(position);
}
@Override
public void onPageScrollStateChanged(int state) {
}
設定BottomNavigationBar的監聽事件
當BottomNavigationBar控制元件的某個標籤被選中的時候,需要相應的選中ViewPager的對應頁面。
bottomNavigationBar.setTabSelectedListener(this);
@Override
public void onTabSelected(int position) {
viewPager.setCurrentItem(position);
}
@Override
public void onTabUnselected(int position) {
}
@Override
public void onTabReselected(int position) {
}
實現效果
主要程式碼
主介面佈局檔案activity_main,xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:id="@+id/activity_main"
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:orientation="vertical"
tools:context="com.rainsong.mishop.MainActivity">
<android.support.v4.view.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<com.ashokvarma.bottomnavigation.BottomNavigationBar
android:id="@+id/bottom_navigation_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"/>
</LinearLayout>
MainActivity.java
package com.rainsong.mishop;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import com.ashokvarma.bottomnavigation.BottomNavigationBar;
import com.ashokvarma.bottomnavigation.BottomNavigationItem;
import com.rainsong.mishop.adapter.SectionsPagerAdapter;
import com.rainsong.mishop.fragment.CatagoryFragment;
import com.rainsong.mishop.fragment.DiscoverFragment;
import com.rainsong.mishop.fragment.HomeFragment;
import com.rainsong.mishop.fragment.UserCentralFragment;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity implements BottomNavigationBar
.OnTabSelectedListener, ViewPager.OnPageChangeListener {
private ViewPager viewPager;
private BottomNavigationBar bottomNavigationBar;
private List<Fragment> fragments;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
initBottomNavigationBar();
initViewPager();
}
private void initBottomNavigationBar() {
bottomNavigationBar = (BottomNavigationBar) findViewById(R.id.bottom_navigation_bar);
bottomNavigationBar.setTabSelectedListener(this);
bottomNavigationBar.clearAll();
bottomNavigationBar.setMode(BottomNavigationBar.MODE_FIXED);
bottomNavigationBar.setBackgroundStyle(BottomNavigationBar.BACKGROUND_STYLE_STATIC);
bottomNavigationBar
.addItem(new BottomNavigationItem(R.drawable.icon_main_home_selected, R.string.home)
.setInactiveIconResource(R.drawable.icon_main_home_normal)
.setActiveColorResource(R.color.orange))
.addItem(new BottomNavigationItem(R.drawable.icon_main_category_selected, R
.string.category)
.setInactiveIconResource(R.drawable.icon_main_category_normal)
.setActiveColorResource(R.color.orange))
.addItem(new BottomNavigationItem(R.drawable.icon_main_discover_selected, R
.string.discover)
.setInactiveIconResource(R.drawable.icon_main_discover_normal)
.setActiveColorResource(R.color.orange))
.addItem(new BottomNavigationItem(R.drawable.icon_main_mine_selected, R.string.mine)
.setInactiveIconResource(R.drawable.icon_main_mine_normal)
.setActiveColorResource(R.color.orange))
.initialise();
}
private void initViewPager() {
viewPager = (ViewPager) findViewById(R.id.view_pager);
fragments = new ArrayList<Fragment>();
fragments.add(new HomeFragment());
fragments.add(new CatagoryFragment());
fragments.add(new DiscoverFragment());
fragments.add(new UserCentralFragment());
viewPager.setAdapter(new SectionsPagerAdapter(getSupportFragmentManager(), fragments));
viewPager.addOnPageChangeListener(this);
viewPager.setCurrentItem(0);
}
@Override
public void onTabSelected(int position) {
viewPager.setCurrentItem(position);
}
@Override
public void onTabUnselected(int position) {
}
@Override
public void onTabReselected(int position) {
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
bottomNavigationBar.selectTab(position);
}
@Override
public void onPageScrollStateChanged(int state) {
}
}
SectionsPagerAdapter.java
package com.rainsong.mishop.adapter;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import java.util.List;
/**
* Created by maxliaops on 17-1-6.
*/
public class SectionsPagerAdapter extends FragmentPagerAdapter {
List<Fragment> fragments;
public SectionsPagerAdapter(FragmentManager fm, List<Fragment> fragments) {
super(fm);
this.fragments = fragments;
}
@Override
public Fragment getItem(int position) {
return fragments.get(position);
}
@Override
public int getCount() {
return fragments.size();
}
}