티스토리 뷰

Mobile/Android

[Android] Navigation Drawer

춘햄 2022. 3. 27. 12:27

NavgationDrawer은 화면의 좌측 상단의 아이콘을 클릭했을 때, 바로가기 메뉴 등이 나타나도록 할 수 있는 화면이다. 

프로젝트를 생성할 때, 해당 액티비티를 선택하여 생성하는 것 또한 가능하다.

프로젝트를 생성하고나서 activity_main.xml을 열어보면 아래와 같이 app_bar_main.xml 레이아웃 파일이 임포트되어 있고, 그 아래에 NavigationView 태그가 작성이 되어있다. 

 

◎activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        tools:openDrawer="start">

    <include
            layout="@layout/app_bar_main"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

    <com.google.android.material.navigation.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"/>
</androidx.drawerlayout.widget.DrawerLayout>

NavigationView객체를 보면, headerLayout과 menu 속성이 존재하는데 이는 각각 바로가기 메뉴 상단과 그 아래 표시될 메뉴를 나타낸다.

 

◎app_bar_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

    <com.google.android.material.appbar.AppBarLayout
            android:layout_height="wrap_content"
            android:layout_width="match_parent"
            android:theme="@style/Theme.Callenge.AppBarOverlay">

        <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary"
                app:popupTheme="@style/Theme.Callenge.PopupOverlay"/>

    </com.google.android.material.appbar.AppBarLayout>

    <FrameLayout
            android:id="@+id/container"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
    > </FrameLayout>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

app_bar_main.xml 파일이 실질적인 메인화면 역할을 하는 레이아웃 파일이므로 여기에 FrameLayout을 하나 추가한다.

 

이제, NavigationView 에 정의된 헤더 속성과 메뉴 속성에 있는 nav_header_main.xml과 activity_main_drawer.xml를 확인해보면 다음과 같다.

 

◎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="@dimen/nav_header_height"
        android:background="@drawable/side_nav_bar"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        android:theme="@style/ThemeOverlay.AppCompat.Dark"
        android:orientation="vertical"
        android:gravity="bottom">

    <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingTop="@dimen/nav_header_vertical_spacing"
            app:srcCompat="@mipmap/ic_launcher_round"
            android:contentDescription="@string/nav_header_desc"
            android:id="@+id/imageView"/>

    <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingTop="@dimen/nav_header_vertical_spacing"
            android:text="@string/nav_header_title"
            android:textAppearance="@style/TextAppearance.AppCompat.Body1"/>

    <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/nav_header_subtitle"
            android:id="@+id/textView"/>
</LinearLayout>

◎activity_main_drawer.xml

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

    <group android:checkableBehavior="single">
        <item
                android:id="@+id/nav_home"
                android:icon="@drawable/ic_menu_camera"
                android:title="@string/menu_home"/>
        <item
                android:id="@+id/nav_gallery"
                android:icon="@drawable/ic_menu_gallery"
                android:title="@string/menu_gallery"/>
        <item
                android:id="@+id/nav_slideshow"
                android:icon="@drawable/ic_menu_slideshow"
                android:title="@string/menu_slideshow"/>
    </group>
</menu>

또한 menu에서 사용한 각 Fragment는 액티비티를 생성할 때 자동으로 생성되는데, 그 코드들을 다음과 같다.

 

◎fragment_home.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".ui.home.HomeFragment">

    <TextView
            android:id="@+id/text_home"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp"
            android:layout_marginTop="8dp"
            android:layout_marginEnd="8dp"
            android:textAlignment="center"
            android:textSize="20sp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

 

◎HomeFragment.class

package com.example.callenge.ui.home;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import com.example.callenge.R;

public class HomeFragment extends Fragment {

    private HomeViewModel homeViewModel;

    public View onCreateView(@NonNull LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {
        homeViewModel =
                new ViewModelProvider(this).get(HomeViewModel.class);
        View root = inflater.inflate(R.layout.fragment_home, container, false);
        final TextView textView = root.findViewById(R.id.text_home);
        homeViewModel.getText().observe(getViewLifecycleOwner(), new Observer<String>() {
            @Override
            public void onChanged(@Nullable String s) {
                textView.setText(s);
            }
        });
        return root;
    }
}

◎HomeViewModel.class

package com.example.callenge.ui.home;

import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;

public class HomeViewModel extends ViewModel {

    private MutableLiveData<String> mText;

    public HomeViewModel() {
        mText = new MutableLiveData<>();
        mText.setValue("This is home fragment");
    }

    public LiveData<String> getText() {
        return mText;
    }
}

Fragment를 상속받고, inflate해주는 것 까지는 이해할 수 있지만 ViewModelProvider는 또 무엇이고 HomeViewModel은 또 왜 생성하는 지 의문이 많다. 

이는 액티비티나 프래그먼트 간 데이터 전달을 좀 더 원할하게 해주는 역할이라고만 알아두고, 넘어가자. 다음 포스팅에서 좀 더 자세히 다루도록 하겠다.

 

이제 MainActivity.java를 다음과 같이 구성해주자.

 

◎MainActivity.java

package com.example.callenge;

import android.os.Binder;
import android.os.Bundle;
import android.view.MenuItem;
import android.view.View;
import android.view.Menu;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.core.view.GravityCompat;
import androidx.fragment.app.Fragment;
import com.example.callenge.ui.gallery.GalleryFragment;
import com.example.callenge.ui.home.HomeFragment;
import com.example.callenge.ui.slideshow.SlideshowFragment;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.navigation.NavigationView;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import org.jetbrains.annotations.NotNull;

public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener{

    SlideshowFragment slideshowFragment;
    HomeFragment homeFragment;
    GalleryFragment galleryFragment;

    DrawerLayout drawerLayout;
    Toolbar toolbar;

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

        toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        drawerLayout = findViewById(R.id.drawer_layout);
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
                this, drawerLayout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close
        );
        drawerLayout.addDrawerListener(toggle);
        toggle.syncState();

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

        galleryFragment = new GalleryFragment();
        homeFragment = new HomeFragment();
        slideshowFragment = new SlideshowFragment();

        getSupportFragmentManager().beginTransaction().add(R.id.container, homeFragment).commit();

    }

    @Override
    public boolean onNavigationItemSelected(@NonNull @NotNull MenuItem item) {
        int id = item.getItemId();

        if(id == R.id.nav_home) {
            onFragmentSelected(0,null);
        } else if(id == R.id.nav_gallery) {
            onFragmentSelected(1,null);
        } else if(id == R.id.nav_slideshow) {
            onFragmentSelected(2,null);
        }

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

    public void onFragmentSelected(int position, Bundle bundle) {
        Fragment curFragment = null;

        if(position == 0) {
            curFragment = homeFragment;
            toolbar.setTitle("homeFragment");
        } else if(position == 1) {
            curFragment = galleryFragment;
            toolbar.setTitle("galleryFragment");
        } else if(position == 2) {
            curFragment = slideshowFragment;
            toolbar.setTitle("slideshowFragment");
        }

        getSupportFragmentManager().beginTransaction().replace(R.id.container, curFragment).commit();

    }
}

'Mobile > Android' 카테고리의 다른 글

[Android] Service 1: Service 이해하기  (0) 2022.03.29
[Android] ViewModel  (0) 2022.03.27
[Android] View Pager2  (0) 2022.03.25
[Android] Tab 2: 하단 Tab 구성  (0) 2022.03.24
[Android] Tab 1: Tab 이해하기, 상단 Tab 구성  (0) 2022.03.24
Comments