Android自定義側滑抽屜菜單

預覽圖

界面布局

傳統(tǒng)的抽屜菜單一般采取DrawerLayout+Toolbar+NavigationView的布局組合。而為了使菜單欄界面設計更加自由,這兒使用自定義布局來代替NavigationView韩肝。在設計布局之前测垛,先在styles.xml中將“DarkActionBar”改為“NoActionBar”磅废,刪去標題欄秉宿,然后待會我們用Toolbar替代隆豹。

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">

接著開始寫我們的主界面先煎,采取Material Design的DrawerLayout布局贼涩。
activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white">

    <android.support.v7.widget.CardView
        android:id="@+id/card_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:cardCornerRadius="0dp"
        app:cardElevation="20dp">

        <android.support.constraint.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:fitsSystemWindows="true"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />

            <FrameLayout
                android:id="@+id/content_view"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:layout_constraintTop_toBottomOf="@+id/toolbar"/>

        </android.support.constraint.ConstraintLayout>

    </android.support.v7.widget.CardView>

    <fragment
        android:id="@+id/nav_view"
        android:name="com.example.yc.androidsrc.MenuFragment"
        android:layout_gravity="start"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</android.support.v4.widget.DrawerLayout>

DrawerLayout布局下的第一個子視圖表示主內容視圖,我將其嵌套在一個CardView里面薯蝎,主要是為了當拉開菜單時遥倦,主內容視圖的邊緣能呈現(xiàn)出卡片式的圓角和陰影。主內容視圖主要包括Toolbar和FrameLayout占锯,其中FrameLayout用以動態(tài)加載各個菜單選項所對應的fragment袒哥。

DrawerLayout布局下的第二個子視圖表示菜單欄視圖,我們使用靜態(tài)加載fragment的方式來呈現(xiàn)界面內容消略,其中android:layout_gravity="start"表示抽屜菜單是從左邊拉開堡称,而end則表示右邊。

菜單布局不詳說了艺演。不過菜單選項的ListView Item布局左邊有個設置為透明的View却紧,當該項被選中時才顯示為黑色,充當“視覺標簽”的作用胎撤。

菜單側滑時的監(jiān)聽事件

我們主要實現(xiàn)的效果是:當滑動菜單時晓殊,主視圖逐漸縮小,移至界面右邊并呈現(xiàn)出卡片式的圓角與陰影伤提,菜單欄視圖逐漸放大并完整呈現(xiàn)在界面左邊巫俺。

因此我們需要監(jiān)聽DrawerLayout的滑動事件,代碼如下:

        mDrawerLayout.addDrawerListener(new DrawerLayout.DrawerListener() {
            @Override
            public void onDrawerSlide(View drawerView, float slideOffset) {
                View mContent = mDrawerLayout.getChildAt(0);
                View mMenu = drawerView;
                float scale = 1 - slideOffset;
                float rightScale = 0.8f + scale * 0.2f;
                float leftScale = 0.5f + slideOffset * 0.5f;
                mMenu.setAlpha(leftScale);
                mMenu.setScaleX(leftScale);
                mMenu.setScaleY(leftScale);
                mContent.setPivotX(0);
                mContent.setPivotY(mContent.getHeight() * 1/2);
                mContent.setScaleX(rightScale);
                mContent.setScaleY(rightScale);
                mContent.setTranslationX(mMenu.getWidth() * slideOffset);
            }

            @Override
            public void onDrawerOpened(View drawerView) {
                cardView.setRadius(20);
            }

            @Override
            public void onDrawerClosed(View drawerView) {
                cardView.setRadius(0);
            }

            @Override
            public void onDrawerStateChanged(int newState) {

            }
        });

我們來分析一下:其中參數(shù)slideOffset代表此時菜單欄的滑動比例飘弧,數(shù)值為0~1识藤。而在這個過程中,我希望主內容視圖的尺寸從原本的1縮放至最后的0.8次伶,因此我們可以得到一個很簡單的線性函數(shù)痴昧,即主內容視圖的尺寸Scale=0.8+(1-slideOffset)x0.2。同樣的冠王,我設置在這個過程中赶撰,菜單欄的尺寸由0.5擴大到1,因此它的尺寸線性函數(shù)為0.5+slideOffset*0.5。而此時主內容視圖的右移尺寸剛好對應為菜單欄滑出來的寬度豪娜,因此設置為mContent.setTranslationX(mMenu.getWidth()xslideOffset)餐胀。另外關于主內容視圖要從以哪個坐標點為中心進行縮放,可通過setPivot進行設置瘤载,我覺得從左邊界的中間點進行縮放否灾,效果會比較好看些。當然鸣奔,以上這些數(shù)據你都可以自由設計墨技,不同的數(shù)據得到的界面動態(tài)效果各有千秋。

主要的代碼如下:

MainActivity.java

public class MainActivity extends AppCompatActivity {

    private DrawerLayout mDrawerLayout;
    private FrameLayout contentFrameLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        mDrawerLayout.setScrimColor(Color.TRANSPARENT); // 菜單滑動時content不被陰影覆蓋

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        getSupportActionBar().setTitle(""); // 不顯示程序應用名
        toolbar.setNavigationIcon(R.drawable.ic_menu_black_24dp); // 在toolbar最左邊添加icon
        toolbar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mDrawerLayout.openDrawer(GravityCompat.START);
            }
        });

        final CardView cardView = (CardView) findViewById(R.id.card_view);

        contentFrameLayout = (FrameLayout) findViewById(R.id.content_view);
        replaceFragment(new TabFragment1());

        // 監(jiān)聽抽屜的滑動事件
        mDrawerLayout.addDrawerListener(new DrawerLayout.DrawerListener() {
            @Override
            public void onDrawerSlide(View drawerView, float slideOffset) {
                View mContent = mDrawerLayout.getChildAt(0);
                View mMenu = drawerView;
                float scale = 1 - slideOffset;
                float rightScale = 0.8f + scale * 0.2f;
                float leftScale = 0.5f + slideOffset * 0.5f;
                mMenu.setAlpha(leftScale);
                mMenu.setScaleX(leftScale);
                mMenu.setScaleY(leftScale);
                mContent.setPivotX(0);
                mContent.setPivotY(mContent.getHeight() * 1/2);
                mContent.setScaleX(rightScale);
                mContent.setScaleY(rightScale);
                mContent.setTranslationX(mMenu.getWidth() * slideOffset);
            }

            @Override
            public void onDrawerOpened(View drawerView) {
                cardView.setRadius(20); // 拉開菜單時挎狸,主內容視圖的邊緣能呈現(xiàn)出卡片式的圓角和陰影
            }

            @Override
            public void onDrawerClosed(View drawerView) {
                cardView.setRadius(0); // 菜單關閉扣汪,圓角消失
            }

            @Override
            public void onDrawerStateChanged(int newState) {

            }
        });

    }

    public void replaceFragment(Fragment fragment) { // 動態(tài)加載fragment
        FragmentManager fragmentManager = getSupportFragmentManager();
        FragmentTransaction transaction = fragmentManager.beginTransaction();
        transaction.replace(R.id.content_view, fragment);
        transaction.addToBackStack(null);
        transaction.commit();
    }

}

至于菜單欄視圖,主要是設置好每個選項的點擊事件锨匆。這里我通過getActivity()強行獲得MainActivity對象崭别,然后再直接通過上面寫好的replaceFragment()函數(shù)去動態(tài)加載對應的fragment,將所要的不同功能視圖呈現(xiàn)在主視圖中恐锣。

MenuFragment

public class MenuFragment extends Fragment {

    private ListView mListView;
    private List<MenuItem> menuItemList = new ArrayList<>();
    private MenuItemAdapter adapter;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState)
    {
        View navView = inflater.inflate(R.layout.activity_menu, container, false);
        mListView = (ListView) navView.findViewById(R.id.menu_list_view);
        mListView.setDivider(null); // 去掉分割線
        initListView();
        clickEvents();
        return navView;
    }

    public void initListView() {
        String[] data_zh = getResources().getStringArray(R.array.menu_zh);
        String[] data_en = getResources().getStringArray(R.array.menu_en);
        for (int i = 0; i < data_zh.length; i++) {
            MenuItem menuItem = new MenuItem(data_zh[i], data_en[i]);
            menuItemList.add(menuItem);
        }
        adapter = new MenuItemAdapter(getActivity(), R.layout.menu_list_item, menuItemList);
        mListView.setAdapter(adapter);
    }

    public void clickEvents() {
        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                adapter.changeSelected(position); // 先定義好樣式如何改變
                MainActivity activity = (MainActivity) getActivity();
                DrawerLayout mDrawerLayout = (DrawerLayout) activity.findViewById(R.id.drawer_layout);
                mDrawerLayout.closeDrawer(Gravity.START);
                switch (position) {
                    case 0:
                        activity.replaceFragment(new TabFragment1());
                        break;
                    case 1:
                        activity.replaceFragment(new TabFragment2());
                        break;
                    case 2:
                        activity.replaceFragment(new TabFragment3());
                        break;
                    case 3:
                        activity.replaceFragment(new TabFragment4());
                        break;
                    case 4:
                        activity.replaceFragment(new TabFragment5());
                        break;
                    default:
                        break;
                }
            }
        });

    }
}

沉浸式狀態(tài)欄

可以看到效果圖上的頂部狀態(tài)欄為沉浸式茅主,背景色與Toolbar一致并且字體為黑色(一般為白色)。

首先土榴,應該明白各個版本的區(qū)別:要修改狀態(tài)欄暗膜,Android版本至少要在4.4以上,并且在4.4是不能讓狀態(tài)欄透明的鞭衩,只能達到一種半透明的陰影背景,而在5.x的版本中娃善,是可以修改背景顏色但無法修改字體顏色的论衍,只有在6.0以上是可以隨意修改的。但是在魅族和小米第三方ROM在4.4版本以上的手機都提供了修改的接口聚磺。

這里就不詳細展開了坯台,具體怎么做,我覺得這兩篇博客總結得比較好瘫寝,放上地址:

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末蜒蕾,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子焕阿,更是在濱河造成了極大的恐慌咪啡,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,013評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件暮屡,死亡現(xiàn)場離奇詭異撤摸,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,205評論 2 382
  • 文/潘曉璐 我一進店門准夷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來钥飞,“玉大人,你說我怎么就攤上這事衫嵌《林妫” “怎么了?”我有些...
    開封第一講書人閱讀 152,370評論 0 342
  • 文/不壞的土叔 我叫張陵楔绞,是天一觀的道長结闸。 經常有香客問我,道長墓律,這世上最難降的妖魔是什么膀估? 我笑而不...
    開封第一講書人閱讀 55,168評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮耻讽,結果婚禮上察纯,老公的妹妹穿的比我還像新娘。我一直安慰自己针肥,他們只是感情好饼记,可當我...
    茶點故事閱讀 64,153評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著慰枕,像睡著了一般具则。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上具帮,一...
    開封第一講書人閱讀 48,954評論 1 283
  • 那天博肋,我揣著相機與錄音,去河邊找鬼蜂厅。 笑死匪凡,一個胖子當著我的面吹牛,可吹牛的內容都是我干的掘猿。 我是一名探鬼主播病游,決...
    沈念sama閱讀 38,271評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼稠通!你這毒婦竟也來了衬衬?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 36,916評論 0 259
  • 序言:老撾萬榮一對情侶失蹤改橘,失蹤者是張志新(化名)和其女友劉穎滋尉,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體唧龄,經...
    沈念sama閱讀 43,382評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡兼砖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,877評論 2 323
  • 正文 我和宋清朗相戀三年奸远,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片讽挟。...
    茶點故事閱讀 37,989評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡懒叛,死狀恐怖,靈堂內的尸體忽然破棺而出耽梅,到底是詐尸還是另有隱情薛窥,我是刑警寧澤,帶...
    沈念sama閱讀 33,624評論 4 322
  • 正文 年R本政府宣布眼姐,位于F島的核電站诅迷,受9級特大地震影響,放射性物質發(fā)生泄漏众旗。R本人自食惡果不足惜罢杉,卻給世界環(huán)境...
    茶點故事閱讀 39,209評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望贡歧。 院中可真熱鬧滩租,春花似錦、人聲如沸利朵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,199評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽绍弟。三九已至技即,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間樟遣,已是汗流浹背而叼。 一陣腳步聲響...
    開封第一講書人閱讀 31,418評論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留豹悬,地道東北人澈歉。 一個月前我還...
    沈念sama閱讀 45,401評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像屿衅,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子莹弊,可洞房花燭夜當晚...
    茶點故事閱讀 42,700評論 2 345