v4 DrawerLayout을 이용한 슬라이딩 메뉴 구현

스누피2 1 4,293 2015.12.30 20:18

DrawerLayout을 이용한 슬라이딩 메뉴 구현

 

드로우레이아웃은 v4 서포트 라이브러리에 포함되어 있다.

메뉴를 리스트뷰로 구성하고 평소 화면 왼쪽에 숨어있다가 액션바 타이틀 부분을 누르거나 화면을 스와이프하면 나타나는 슬라이딩 메뉴이다. 직관적인 메뉴 선택을 통해 화면이동이 자유롭다는 장점이 있다.

반면에 화면이동에 순서가 없으므로 액티비티로 처리하는 경우 백스택 관리가 난해하게 된다.

그러므로 액티비티 사용은 자제하고 대신 프래그먼트를 사용한다.

 


 

사용 개발툴은 이클립스를 사용하기로 한다.

android:minSdkVersion="16"

android:targetSdkVersion="22"

 

1. 새로운 프로젝트를 만든다.

특별한 설정을 필요로 하지 않는다. 기본 설정으로 만든다.

 

2. 화면 디자인 파일을 만든다.

반드시 FrameLayout을 사용해야 하는 것은 아니다.

FrameLayout에 컨텐츠 뷰를 표시한다.

ListView로 슬라이딩 용 메뉴 정보를 표시한다.

ListView는 평소에 화면 왼쪽에 숨어 있도록 설정한다.

             android:layout_gravity="start"

 

layout/activity_main.xml

<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:orientation="vertical"

    tools:context="com.example.x01_drawer.MainActivity" >

 

    <android.support.v4.widget.DrawerLayout

        android:id="@+id/drawerLayout"

        android:layout_width="match_parent"

        android:layout_height="match_parent" >

 

        <FrameLayout

            android:id="@+id/frameLayout"

            android:layout_width="match_parent"

            android:layout_height="match_parent" />

 

        <ListView

            android:id="@+id/listView"

            android:layout_gravity="start"

            style="@style/ListViewStyle" />

       

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

 

</LinearLayout>

 

3. 커스텀 style을 사용하기 위해 styles.xml 만든다.

메뉴가 선택되었다는 것을 알려주기 위해 singleChoice 모드를 설정한다.

             item name="android:choiceMode">singleChoice</item>

스타일에 설정을 해두면 다른 화면에서 재사용할 수 있는 장점이 있다.

 

layout/styles.xml

<resources>

 

    <style name="AppBaseTheme" parent="android:Theme.Light">

       

    </style>

 

    <style name="AppTheme" parent="AppBaseTheme">

       

    </style>

   

    <style name="ListViewStyle">

        <item name="android:layout_width">match_parent</item>

        <item name="android:layout_height">match_parent</item>

        <item name="android:background">#f4f5f5</item>

        <item name="android:cacheColorHint">@android:color/transparent</item>

        <item name="android:choiceMode">singleChoice</item>

        <item name="android:divider">@android:color/transparent</item>

        <item name="android:dividerHeight">0dp</item>

    </style>

 

</resources>

 

4. 메뉴에서 사용할 xml 파일을 만든다.

메뉴의 다양한 구성을 테스트 하기 위해 menu1은 액션바 상단에 배치한다.

             android:showAsAction="always"

 

menu/main.xml

<menu xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    tools:context="com.example.x01_drawer.MainActivity" >

 

    <item

        android:id="@+id/menu1"

        android:orderInCategory="100"

        android:showAsAction="always"

        android:icon="@android:drawable/ic_menu_gallery"

        android:title="menu1"/>

   

    <item

        android:id="@+id/menu2"

        android:orderInCategory="200"

        android:showAsAction="never"

        android:title="menu2"/>

   

    <item

        android:id="@+id/menu3"

        android:orderInCategory="300"

        android:showAsAction="never"

        android:title="menu3"/>

 

</menu>

 

5. 슬라이딩 메뉴에 대응하는 프래그먼트가 사용할 디자인 파일을 3개 만든다.

간단히 프래그먼트 구분을 위한 TextView를 사용한다.

 

layout/fragment1.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical"

    android:padding="10dp" >

 

    <TextView

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="fragment1" />

 

</LinearLayout>

 

layout/fragment2.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical"

    android:padding="10dp" >

 

    <TextView

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="fragment2" />

 

</LinearLayout>

 

layout/fragment3.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical"

    android:padding="10dp" >

 

    <TextView

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="fragment3" />

 

</LinearLayout>

 

6. 프래그먼트를 3개 만든다.

프래그먼트는 뷰가 아니다. 프래그먼트는 뷰의 역할을 수행할 수 있는 레이아웃에 장착되어 사용하는 개념이다. 액티비티의 변화 없이 화면을 바꿀 수 있다는 장점이 있다.

 

Fragment1.java

public class Fragment1 extends Fragment{

 

             @Override

             public View onCreateView(LayoutInflater inflater, ViewGroup container,

                                        Bundle savedInstanceState) {

                           View view = inflater.inflate(R.layout.fragment1, container, false);

                           return view;

             }

}

 

Fragment2.java

public class Fragment2 extends Fragment{

 

             @Override

             public View onCreateView(LayoutInflater inflater, ViewGroup container,

                                        Bundle savedInstanceState) {

                           View view = inflater.inflate(R.layout.fragment2, container, false);

                           return view;

             }

}

 

Fragment3.java

public class Fragment3 extends Fragment{

 

             @Override

             public View onCreateView(LayoutInflater inflater, ViewGroup container,

                                        Bundle savedInstanceState) {

                           View view = inflater.inflate(R.layout.fragment3, container, false);

                           return view;

             }

}

 

7. 액티비티 파일을 만든다.

deprecated v4ActionBarDrawerToggle 대신 v7ActionBarDrawerToggle을 사용한다.

 

MainActivity.java

package com.example.x01_drawer;

 

import java.util.ArrayList;

import java.util.Collections;

 

import android.annotation.SuppressLint;

import android.app.Activity;

import android.app.Fragment;

import android.content.res.Configuration;

import android.os.Bundle;

import android.os.Handler;

import android.os.PersistableBundle;

import android.support.v4.view.GravityCompat;

import android.support.v4.widget.DrawerLayout;

import android.support.v7.app.ActionBarDrawerToggle;

import android.util.Log;

import android.view.Menu;

import android.view.MenuItem;

import android.view.View;

import android.widget.AdapterView;

import android.widget.AdapterView.OnItemClickListener;

import android.widget.ArrayAdapter;

import android.widget.ListView;

import android.widget.Toast;

 

import com.example.x01_drawer.fragment.Fragment1;

import com.example.x01_drawer.fragment.Fragment2;

import com.example.x01_drawer.fragment.Fragment3;

 

public class MainActivity extends Activity {

 

             private DrawerLayout drawerLayout;

             private ListView listView;

 

             private ActionBarDrawerToggle drawerToggle;

 

             private ArrayList < String > slidingMenu = new ArrayList < String > ();

 

             public MainActivity() {

                           Collections.addAll(slidingMenu, new String[]{

                                        "프래그먼트 1",

                                        "프래그먼트 2",

                                        "프래그먼트 3"

                           });

             }

 

             private CharSequence mActionBarTitle;

             private CharSequence mMenuTitle;

 

             private Handler handler = new Handler();

 

              @ Override

             protected void onCreate(Bundle savedInstanceState) {

                           super.onCreate(savedInstanceState);

                           setContentView(R.layout.activity_main);

 

                           drawerLayout = (DrawerLayout)findViewById(R.id.drawerLayout);

                           listView = (ListView)findViewById(R.id.listView);

 

                           setActionBar();

                           setSlidingMenu();

                           setDrawer();

 

                           if (savedInstanceState == null) {

                                        displayView(0);

                                        getActionBar().setTitle(slidingMenu.get(0));

                           }

             }

 

             private void displayView(int position) {

                           Fragment fragment = null;

                           switch (position) {

                           case 0:

                                        fragment = new Fragment1();

                                        break;

                           case 1:

                                        fragment = new Fragment2();

                                        break;

                           case 2:

                                        fragment = new Fragment3();

                                        break;

                           default:

                                        break;

                           }

 

                           if (fragment != null) {

                                        // 프래그먼트 전환의 부드러운 처리를 위해 스레드를 사용한다.

                                        handler.post(new CommitFragmentRunnable(fragment));

 

                                        listView.setItemChecked(position, true);

                                        listView.setSelection(position);

                                        drawerLayout.closeDrawer(listView);

 

                           } else {

                                        Log.e(getClass().getSimpleName(), "error in displayView(int position)");

                           }

             }

 

             private void setActionBar() {

                           mMenuTitle = mActionBarTitle = getTitle();

 

                           getActionBar().setDisplayHomeAsUpEnabled(true);

                           getActionBar().setHomeButtonEnabled(true);

             }

 

             private void setSlidingMenu() {

                           ArrayAdapter<String> adapter = new ArrayAdapter < String > (

                                       this, android.R.layout.simple_list_item_activated_1);

                           adapter.addAll(slidingMenu);

 

                           listView.setAdapter(adapter);

 

                           listView.setOnItemClickListener(new OnItemClickListener() {

                                         @ Override

                                        public void onItemClick(AdapterView <  ?  > parent, View view,

                                                    int position, long id) {

                                                     mMenuTitle = slidingMenu.get(position);

                                                     getActionBar().setTitle(mMenuTitle);

                                                     displayView(position);

                                        }

                           });

             }

 

             private void setDrawer() {

                           drawerToggle = new ActionBarDrawerToggle(this, drawerLayout,

                                       R.string.app_name, R.string.app_name) {

                                         @ Override

                                        public void onDrawerOpened(View drawerView) {

                                                     super.onDrawerOpened(drawerView);

                                                     getActionBar().setTitle(mActionBarTitle);

                                                     invalidateOptionsMenu();

                                        }

 

                                         @ Override

                                        public void onDrawerClosed(View drawerView) {

                                                     super.onDrawerClosed(drawerView);

                                                     getActionBar().setTitle(mMenuTitle);

                                                     invalidateOptionsMenu();

                                        }

                           };

 

                           drawerToggle.setDrawerIndicatorEnabled(true);

 

                           drawerLayout.setDrawerListener(drawerToggle);

                           drawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);

             }

 

              @ Override

             public void onBackPressed() {

                           if (drawerLayout.isDrawerOpen(listView)) {

                                        drawerLayout.closeDrawer(listView);

                           } else {

                                        super.onBackPressed();

                           }

             }

 

              @ Override

             public boolean onCreateOptionsMenu(Menu menu) {

                           getMenuInflater().inflate(R.menu.main, menu);

                           return true;

             }

 

              @ Override

             public boolean onOptionsItemSelected(MenuItem item) {

                           // 토글이 이벤트를 소비했다면 이벤트를 전파시키지 않고 종결한다.

                           if (drawerToggle.onOptionsItemSelected(item)) {

                                        return true;

                           }

 

                           int id = item.getItemId();

 

                           switch (id) {

                           case R.id.menu1:

                                        alert(item.getTitle().toString());

                                        return true;

                           case R.id.menu2:

                                        alert(item.getTitle().toString());

                                        return true;

                           case R.id.menu3:

                                        alert(item.getTitle().toString());

                                        return true;

                           default:

                                        return super.onOptionsItemSelected(item);

                           }

 

             }

 

             // invalidateOptionsMenu() 메소드를 호출하면 다음 메소드가 기동한다.

              @ Override

             public boolean onPrepareOptionsMenu(Menu menu) {

                           boolean isSlidingMenuOpened = drawerLayout.isDrawerOpen(listView);

                           menu.findItem(R.id.menu2).setVisible(!isSlidingMenuOpened);

                           menu.findItem(R.id.menu3).setVisible(!isSlidingMenuOpened);

                           return super.onPrepareOptionsMenu(menu);

             }

 

             protected void alert(String text) {

                           Toast.makeText(this, text, Toast.LENGTH_SHORT).show();

             }

            

             // 액션바 토글 상태를 동기화하기 위해서 다음 두 개의 메서드를 오버라이드 한다.

              @ SuppressLint("NewApi")

             public void onPostCreate(Bundle savedInstanceState, PersistableBundle persistentState) {

                           super.onPostCreate(savedInstanceState, persistentState);

                           drawerToggle.syncState();

             }

 

              @ Override

             public void onConfigurationChanged(Configuration newConfig) {

                           super.onConfigurationChanged(newConfig);

                           drawerToggle.onConfigurationChanged(newConfig);

             }

 

             private class CommitFragmentRunnable implements Runnable {

 

                           private Fragment fragment;

 

                           public CommitFragmentRunnable(Fragment fragment) {

                                        this.fragment = fragment;

                           }

 

                            @ Override

                           public void run() {

                                        getFragmentManager().beginTransaction()

                                        .replace(R.id.frameLayout, fragment).commit();

                           }

             }

}

 

8. 테스트

다양한 시나리오로 테스트해보고 업그레이드할 점을 고민해 보자.

- 액션바 타이틀 부분을 클릭한다.

- 휴대폰 하단에 메뉴 버튼을 클릭한다.

- 휴대폰 백버튼을 누른다.

Comments

FSP(이종철) 2015.12.30 20:32
감사합니다^^