1. 程式人生 > >Android抽屜式導航欄使用及相關類認識

Android抽屜式導航欄使用及相關類認識

抽屜式導航欄官方推出好久了,之前也使用過,但對這些類都是懵懵懂懂的。今天認真看了下文件才把這些類搞清楚。個人對MD的設計風格還是很喜歡的。

DrawerLayout

DrawerLayout是一個視窗內容的頂層容器(像LinearLayout、FrameLayout這些一樣),它的用處在於允許從視窗的一個或兩個垂直邊緣拉出互動式“抽屜”檢視(這個抽屜指的就是滑出的View,說抽屜還挺形象的啊)。也就是說要實現滑出View的話,你得使用這個ViewGroup作為頂層佈局。

抽屜定位和佈局使用android:layout_gravity屬性來控制,該屬性決定抽屜檢視從哪個方向滑出:左側或右側(基於支援佈局方向的平臺版本,例如有些地區閱讀方向是從右往左,要考慮這些情況可以統一使用start而不是left)。每個垂直邊緣只能有一個抽屜檢視。如果佈局在視窗的每個垂直邊緣配置多個抽屜檢視,則會在執行時丟擲異常。

  • 要使用DrawerLayout,需要將主內容檢視作為第一個子節點(因為在xml檔案中佈局將在z軸上進行覆蓋,後面的佈局覆蓋前面的,這裡必須保證滑出檢視在主檢視之上)。
  • 主內容檢視寬度和高度為match_parent而且不要使用layout_gravity。主內容檢視設定為匹配父檢視的寬度和高度, 因為在抽屜式導航欄處於隱藏狀態時, 它和平時UI介面沒什麼兩樣。
  • 在主內容檢視之後新增抽屜檢視(滑出的View),並設定layout_gravity,這決定檢視從那邊滑出,如果不設定將直接顯示而不是滑出。抽屜通常使用match_parent為高度,寬度為固定值(寬度不應超過 320dp,從而使用者始終可以看到部分主內容)。

根據“Android設計指南”,任何位於左側(基於閱讀順序)的抽屜應當是對應用程式中內容的導航,而位於右側的抽屜是對當前內容所執行的操作。

使用示例

<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
>
<!-- 主內容檢視,通常由抽屜中的item點選來決定使用相應的fragment進行填充 --> <FrameLayout android:id="@+id/content_frame" android:layout_width="match_parent" android:layout_height="match_parent" /> <!-- 抽屜檢視,可以為任意檢視 --> <ListView android:id="@+id/left_drawer" android:layout_width="240dp" android:layout_height="match_parent" android:layout_gravity="start" <!-- 指定滑出方向,關鍵點 --> android:choiceMode="singleChoice" android:divider="@android:color/transparent" android:dividerHeight="0dp" android:background="#111"/> </android.support.v4.widget.DrawerLayout>

新增監聽器

  1. 抽屜中item的點選事件

    這決定於你在抽屜中所使用的View,用常規方式為它們新增點選事件即可。下面是使用ListView作為抽屜View的例子:

    //這是官方的例子。這裡對ListView.OnItemClickListener進行了一層封裝,不封裝直接使用也是可以的
    private class DrawerItemClickListener implements ListView.OnItemClickListener {
       @Override
       public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
           selectItem(position);
       }
    }
    
    /* 這裡以切換fragment為例 */
    private void selectItem(int position) {
       // Create a new fragment and specify the planet to show based on position
       Fragment fragment = new PlanetFragment();
       Bundle args = new Bundle();
       args.putInt(PlanetFragment.ARG_PLANET_NUMBER, position);
       fragment.setArguments(args);
    
       // Insert the fragment by replacing any existing fragment
       FragmentManager fragmentManager = getFragmentManager();
       fragmentManager.beginTransaction()
                      .replace(R.id.content_frame, fragment)
                      .commit();
    
       //將item置為選中狀態,更新標題,隱藏抽屜
       mDrawerList.setItemChecked(position, true);
       setTitle(mPlanetTitles[position]);
       mDrawerLayout.closeDrawer(mDrawerList);
    }
    
    @Override
    public void setTitle(CharSequence title) {
       mTitle = title;
       getActionBar().setTitle(mTitle);
    }
  2. 監聽抽屜狀態

    通過下面的步驟新增監聽器:

    1. 首先繫結DrawerLayout取到它的引用。
    2. 呼叫addDrawerListener方法並傳入一個DrawerLayout.DrawerListener的例項。(注意:原有的setDrawerListener()` 方法已被移除)
    3. 複寫DrawerLayout.DrawerListener一系列方法即可。
    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
    drawer.addDrawerListener(new DrawerLayout.DrawerListener() {
    //滑動時觸發,會多次呼叫
       @Override
       public void onDrawerSlide(View drawerView, float slideOffset) {
        Log.d(TAG, "onDrawerSlide: ");
       }
    //抽屜滑出顯示時觸發
       @Override
       public void onDrawerOpened(View drawerView) {
        Log.d(TAG, "onDrawerOpened: ");
       }
    //抽屜滑入隱藏時觸發
       @Override
       public void onDrawerClosed(View drawerView) {
        Log.d(TAG, "onDrawerClosed: ");
       }
    //狀態改變時觸發,也就是當顯示變為隱藏,或反過來時觸發
       @Override
       public void onDrawerStateChanged(int newState) {
        Log.d(TAG, "onDrawerStateChanged: ");
       }
    });

ActionBarDrawerToggle

這是一個ActionBar(Toolbar)與DrawerLayout之間的觸發器,將DrawerLayout的滑動與Actionbar進行聯動。它在抽屜狀態變化時改變Actionbar上的返回圖示(android.R.id.home),而且帶有動畫效果。

這個類是DrawerLayout.DrawerListener的子類,它有兩個構造方法,分別對應Actionbar與Toolbar。

  • ActionBarDrawerToggle(Activity activity, DrawerLayout drawerLayout, int openDrawerContentDescRes, int closeDrawerContentDescRes)
  • ActionBarDrawerToggle(Activity activity, DrawerLayout drawerLayout, Toolbar toolbar, int openDrawerContentDescRes, int closeDrawerContentDescRes)

最後兩個引數是為輔助功能(accessibility)所提供的描述性動作字串。

考慮到它是DrawerLayout.DrawerListener的子類,當然也是作為監聽器來使用的,在裡面同樣可以複寫監聽DrawerLayout狀態改變的方法。

 @Override
 protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.activity_main);
     Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
     setSupportActionBar(toolbar);

    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
    ActionBarDrawerToggle mDrawerToggle = new ActionBarDrawerToggle(
      this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close){
        //再這複寫監聽方法,如果需要的話
        @Override
        public void onDrawerSlide(View drawerView, float slideOffset) {
            super.onDrawerSlide(drawerView, slideOffset);
            Log.d(TAG, "onDrawerSlide: ");
        }
    };
    drawer.addDrawerListener(mDrawerToggle);
    mDrawerToggle.syncState();
}

@Override
protected void onPostCreate(Bundle savedInstanceState) {
    super.onPostCreate(savedInstanceState);
    //在這裡呼叫同步方法
    mDrawerToggle.syncState();
}

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    //在視窗配置發生改變的時候同樣要同步toggle的指示器(就是那個圖示)狀態
    mDrawerToggle.onConfigurationChanged(newConfig);
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // 如果mDrawerToggle的onOptionsItemSelected(item)方法返回true,那麼直接返回true
    if (mDrawerToggle.onOptionsItemSelected(item)) {
        return true;
    }
    // Handle your other action bar items...
    return super.onOptionsItemSelected(item);
}

上面說過在DrawerLayout中可以新增任意的View作為抽屜的檢視,但官方還是提供了一個標準的抽屜View,作為抽屜式導航欄(典型的MD風格),它就是NavigationView。

NavigationView繼承於FrameLayout。通常我們看到的是這樣的:

這裡寫圖片描述

xml如下:

<android.support.design.widget.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:fitsSystemWindows="true"
        app:headerLayout="@layout/nav_header_main"   <!--頭部佈局檔案-->
        app:menu="@menu/activity_main_drawer"/>     <!--選單xml檔案-->

當然也可以通過程式碼的方式動態新增。

新增監聽器

通過呼叫方法:

setNavigationItemSelectedListener(NavigationView.OnNavigationItemSelectedListener listener)

示例:

navigationView.setNavigationItemSelectedListener(new NavigationView
                .OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                //通過id值與menu檔案中item的id匹配
                int id = item.getItemId();
                if (id == R.id.nav_camera) {
                    // Handle the camera action
                }
                //關閉抽屜
                DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
                drawer.closeDrawer(GravityCompat.START);
                return true;
            }
        });

最後附一個狀態列半透明的方法,此時NavigationView可以延伸到狀態列,這樣效果很好。

//透明狀態列(5.0以上系統支援)
if(Build.VERSION.SDK_INT >= 21){
    View decorView = getWindow().getDecorView();
    decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
    getWindow().setStatusBarColor(Color.TRANSPARENT);
}