2016/06/01

Android Navigation Drawer 實現 Sliding Menu 側邊欄

在 Android 中要實現如同 Google Play 側邊欄 (或稱 Sidebar | SlideMenu | Navigation Drawer),

其實現步驟, 如下 :

 注意事項
1) 使用 android.support.v4.widget.DrawerLayout 作為滑出滑入的 Layout 層, 有以下幾個注意事項 :

   1_1) android.support.v4.widget.DrawerLayout 不一定是 Root View.

   1_2) android.support.v4.widget.DrawerLayout 可以有多個 SubView.
   
   1_3) android.support.v4.widget.DrawerLayout 第一個 SubView主程式版面.

   1_4) android.support.v4.widget.DrawerLayout 第二個、第三個、... SubViewSlideMenu 版面.
   
   1_5) SlideMenu 必須要有 android:layout_gravity="left / right" 表示在左邊滑出 or 右邊滑出,
                  沒有這個屬性, 則 SlideMenu 會以全螢幕顯示, 且無法滑出.
   
   1_6) SlideMenu 在主程式中設定其寬度, 可以用 slideMenuList.getLayoutParams().width,
                  但不要用 slideMenuList.setLayoutParams, 這個導致 SlideMenu 全螢幕顯示, 且無法滑出.

2) 判斷 DrawerLayout 是否已開啟顯示, 可以利用 slideMenuLayout.isDrawerOpen( slideMenuList ).

3) 設定 DrawerLayout 開啟顯示, 可以利用 slideMenuLayout.openDrawer( slideMenuList ).

4) 設定 DrawerLayout 關閉顯示, 可以利用 slideMenuLayout.closeDrawer( slideMenuList ).

5) 鎖住 DrawerLayout 用 "手指" 滑進滑出, 可以利用 slideMenuLayout.setDrawerLockMode( 0 / 1 ).
   0: 可以用 "手指" 滑進滑出.
   1: 不能用 "手指" 滑進滑出, 但仍可用程式控制 DrawerLayout 的 openDrawer 與 closeDrawer.

6) 設定 DrawLayout 的相關 Listener, 可以利用 slideMenuLayout.setDrawerListener( xxx );

範例 :
 範例 Layout XML
<?xml version="1.0"  encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/slideMenuLayout"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <!-- 主程式畫面 -->
    <LinearLayout
        android:id="@+id/mainContent"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:background="#FFFFFFFF"
        >
        <TextView
            android:id="@+id/textView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="#FF000000"
            android:text="內容設定在程式中"
            />
    </LinearLayout>
    
    <!-- Sidebar 側邊欄 -->
    <ListView android:id="@+id/slideMenuList"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:layout_gravity="left"
        android:background="#FFFFFFFF"
        />
</android.support.v4.widget.DrawerLayout>

 範例程式碼
public class SlideMenu extends Activity
{
    /** 側邊欄 Layout */
    private DrawerLayout slideMenuLayout;
    
    /** 側邊欄 ListView */
    private ListView slideMenuList;
    
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView( R.layout.main_slide_menu );
        setTitle("Slide Menu 側邊欄");
        
        ((TextView)findViewById(R.id.textView1)).setText( "SlideMenu 顯示 :\n"
                                                        + "1)向左滑動,滑出側邊欄.\n"
                                                        + "2)點擊 Menu 鍵.\n"
                                                        + "\n"
                                                        + "SlideMenu 隱藏 :\n"
                                                        + "1)向右滑動,隱藏側邊欄.\n"
                                                        + "2)點擊 Menu 鍵.\n"
                                                        + "3)點擊 Return 鍵."
                                                        );
        
        slideMenuLayout = (DrawerLayout)findViewById(R.id.slideMenuLayout);
        
        // Sidebar
        slideMenuList = (ListView)findViewById(R.id.slideMenuList);
        
        // Sidebar 寬度
        DisplayMetrics dm = new DisplayMetrics();
        this.getWindowManager().getDefaultDisplay().getMetrics(dm);
        int gScreenWidth  = dm.widthPixels;
        
        slideMenuList.getLayoutParams().width = (int)(gScreenWidth*0.8f);
        
        // Sidebar 背景
        slideMenuList.setBackgroundResource(R.drawable.bg_gradient);
        
        // 設定側邊欄的資料來源
        setSidebarAdapter();
    }
    
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event)
    {
        // 這是 <Menu> KeyCode
        if( keyCode == 82 )
        {
            // 若有開啟 SlideMenu, 則關閉之
            if( slideMenuLayout.isDrawerOpen( slideMenuList ) )
            {
                slideMenuLayout.closeDrawer( slideMenuList );
            }
            // 若沒開啟 SlideMenu, 則開啟之
            else
            {
                slideMenuLayout.openDrawer( slideMenuList );
            }
            
            return true;
        }
        
        return super.onKeyDown(keyCode, event);
    }
    
    @Override
    public void onBackPressed()
    {
        // 若有開啟 SlideMenu, 則關閉之
        if( slideMenuLayout.isDrawerOpen( slideMenuList ) )
        {
            slideMenuLayout.closeDrawer( slideMenuList );
            return;
        }
        
        super.onBackPressed();
    }
    
    /** 功能:設定側邊欄的資料來源
     */
    private void setSidebarAdapter()
    {
        // Sidebar 資料來源
        List  listName = new ArrayList();
        List  listDesc = new ArrayList();
        List listIcon = new ArrayList();
        
        // Menu Item 1: iKnowlegeMan App
        listName.add(getString(R.string.my_knowlege_app_name));
        listIcon.add(R.drawable.icon_knowlege);
        listDesc.add( getString(R.string.my_knowlege_app_desc) );
        
        // Menu Item 2: More App
        listName.add(getString(R.string.menu_view_all_app));
        listIcon.add(android.R.drawable.sym_def_app_icon);
        listDesc.add("");
        
        // Menu Item 3 About Me
        listName.add(getString(R.string.menu_about));
        listIcon.add(android.R.drawable.ic_menu_info_details);
        listDesc.add("");
        
        slideMenuList.setAdapter( new SidebarListAdapter( this
                , listName
                , listDesc
                , listIcon
                ) );
    }
}

範例結果, 如下 :