개발하는 두더지

[Android] DrawerLayout, NavigationView 사용하기 본문

Java,Android

[Android] DrawerLayout, NavigationView 사용하기

덜지 2018. 4. 21. 20:20

Drawer Navigation 이란?

Hambuger, Sandwich 라고 불리는 보통 왼쪽 상단에 위치하는 메뉴버튼을 클릭하거나 손가락으로 화면 왼쪽을 스와이프하여 나오는 뷰를 말합니다.

모바일 디바이스는 화면이 작아서 모든 UI를 담을 수 없습니다. 

Toolbar, Fragment와 같이 앱의 Depth (Activity 이동 최소화) 를 줄일 수 있는 뷰를 제공하게 되었습니다. 

아래에서 Drawer Navigation을 적용하는 방법을 알아보겠습니다.



실행 결과






소스 코드


build.gradle


아래 2개를 추가해줘야 사용 가능합니다.

dependencies {
...
// DrawerLayout
compile 'com.android.support:support-v4:26.1.0'
// NavigationView
compile 'com.android.support:design:26.1.0'
}



style.xml


API 21 부터 Toolbar가 추가되었습니다. Toolbar는 View이기 때문에 기존에 ActionBar에서 하지 못했던 것들을 할 수 있게 해줍니다.

ActionBar는 View가 아니며 내부 아이템과 위치를 제어하기 어렵습니다.

Toolbar는 View이며 제어하기 쉽습니다.


ActionBar가 deprecated 되었기 때문에 Toolbar를 사용하기위해 NoActionBar를 적용해줍니다.

<resources>

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>

</resources>



drawer.xml


네비게이션에 사용될 menu 입니다.

res 하위에 반드시 menu 폴더를 만들고 그 하위에 drawer.xml 파일을 만들어야 합니다.

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

<item
android:id="@+id/item1"
android:icon="@mipmap/ic_launcher_round"
android:title="item1"
/>

<item
android:id="@+id/item2"
android:icon="@mipmap/ic_launcher_round"
android:title="item2"
/>

<item
android:id="@+id/item3"
android:icon="@mipmap/ic_launcher_round"
android:title="item3"
/>

</menu>



toolbar.xml


Custom Toolbar를 새로 만들어 줍니다.

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/toolbar"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:minHeight="?attr/actionBarSize"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
android:background="?attr/colorPrimary"
/>



activty_main.xml


DrawerLayout 는 반드시 메인 뷰와 네비게이션 뷰를 감싸고 있어야 합니다.

메인 뷰는  xml 순서가 z-ordering 순서로 되어있기 때문에 DrawerLayout 의 처음에 위치해야 합니다.

네비게이션 뷰는 문서에보면 보통 ListView나 RecyclerView를 사용한다고 나오지만 NavigationView를 이용하여 커스텀UI를 사용해도 됩니다.


DrawerLayout에서

fitsSystemWindows    :  true를 주면 네비게이션 뷰가 상태창과 소프트키 사이를 차지한다. 즉, toolbar를 덮는다.

openDrawer                :  start를 주면 네비게이션 뷰가 왼쪽에서 생긴다


NavigationView에서

headerLayout  :   헤더 레이아웃 

menu               :   메뉴 레이아웃


<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">

<android.support.v4.widget.DrawerLayout
android:id="@+id/dl_main_drawer_root"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:fitsSystemWindows="true"
tools:openDrawer="start"
>

<!-- main content -->
<include
android:layout_width="match_parent"
android:layout_height="match_parent"
layout="@layout/activity_main_content" />

<!-- navigation drawer-->
<android.support.design.widget.NavigationView
android:id="@+id/nv_main_navigation_root"
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/drawer" />

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

</android.support.constraint.ConstraintLayout>



activity_main_content.xml


Custom Toolbar를 include 해주고 나머지는 메인 콘텐츠 내용을 구성합니다.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">

<!--Custom Toolbar-->
<include layout="@layout/toolbar" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:text="SAMPLE"
/>

</android.support.constraint.ConstraintLayout>



nav_header_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="@android:color/holo_green_dark"
android:gravity="bottom"
android:orientation="vertical"
android:paddingBottom="8dp"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:paddingTop="8dp"
android:theme="@style/ThemeOverlay.AppCompat.Dark">

<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="8dp"
app:srcCompat="@mipmap/ic_launcher_round" />

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="8dp"
android:text="duzi"
android:textAppearance="@style/TextAppearance.AppCompat.Body1" />

<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="duzi@naver.com" />

</LinearLayout>



string.xml

<resources>
<string name="app_name">NavigationSample</string>
<string name="drawer_open">drawer_open</string>
<string name="drawer_close">drawer_close</string>
</resources>



MainActivity.java


public class MainActivity extends AppCompatActivity 
implements NavigationView.OnNavigationItemSelectedListener {

DrawerLayout drawerLayout;
NavigationView navigationView;
ActionBarDrawerToggle drawerToggle;
Toolbar toolbar;

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

initLayout();
}

@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch(item.getItemId()) {
case R.id.item1:
Toast.makeText(this, "item1 clicked..", Toast.LENGTH_SHORT).show();
break;
case R.id.item2:
Toast.makeText(this, "item2 clicked..", Toast.LENGTH_SHORT).show();
break;
case R.id.item3:
Toast.makeText(this, "item3 clicked..", Toast.LENGTH_SHORT).show();
break;
}

drawerLayout.closeDrawer(GravityCompat.START);
return false;
}

@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
// Sync the toggle state after onRestoreInstanceState has occurred.
drawerToggle.syncState();
}

@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
drawerToggle.onConfigurationChanged(newConfig);
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Pass the event to ActionBarDrawerToggle, if it returns
// true, then it has handled the app icon touch event
if (drawerToggle.onOptionsItemSelected(item)) {
return true;
}
// Handle your other action bar items...

return super.onOptionsItemSelected(item);
}

private void initLayout() {
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setTitle("");
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeAsUpIndicator(R.mipmap.ic_menu_white_24dp);

drawerLayout = (DrawerLayout) findViewById(R.id.dl_main_drawer_root);
navigationView = (NavigationView) findViewById(R.id.nv_main_navigation_root);
drawerToggle = new ActionBarDrawerToggle(
this,
drawerLayout,
toolbar,
R.string.drawer_open,
R.string.drawer_close
);
drawerLayout.addDrawerListener(drawerToggle);
navigationView.setNavigationItemSelectedListener(this);
}

@Override
public void onBackPressed() {
if(drawerLayout.isDrawerOpen(GravityCompat.START)) {
drawerLayout.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
}
}




네비게이션 뷰에서 아이템을 클릭 시 다음 동작을 하려면?

Activity class 에 NavigationView의 아이템선택리스너를 구현해줘야 합니다.

implements NavigationView.OnNavigationItemSelectedListener 


drawer.xml 에서 구현된 메뉴를 파라미터로 받습니다.

해당 메뉴 아이템이 선택되면 네비게이션 뷰를 닫아주고 종료해야합니다.

@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch(item.getItemId()) {
case R.id.item1:
Toast.makeText(this, "item1 clicked..", Toast.LENGTH_SHORT).show();
break;
case R.id.item2:
Toast.makeText(this, "item2 clicked..", Toast.LENGTH_SHORT).show();
break;
case R.id.item3:
Toast.makeText(this, "item3 clicked..", Toast.LENGTH_SHORT).show();
break;
}

drawerLayout.closeDrawer(GravityCompat.START);
return false;
}


그리고 네비게이션 뷰 객체에서 이벤트를 받으려면 리스너를 세팅해줘야 합니다.

navigationView = (NavigationView) findViewById(R.id.nv_main_navigation_root);
navigationView.setNavigationItemSelectedListener(this);





참고

Drawer Navigation Google 개발 가이드


Drawer Navigation 디자인 가이드


Comments