[Android] ViewPager와 Fragment를 활용하여 효율적인 UI 구성하기

어플리케이션을 만들때 가장 중요시 생각해야 할 문제가 효율적인 UI구성이 아닌가 싶습니다. 컴퓨터보다 불편한 디바이스인 핸드폰 어플리케이션의 UI는 그 중요성이 특히 강조됩니다. 카카오톡은 국민 채팅 어플리케이션입니다. 카카오톡이 국민 어플리케이션이 된 계기는 시장의 빠른선점과도 같은 이유도 있겠지만 편리한 UI도 한몫을 했다고 생각합니다. 바로 이 카카오톡의 UI가 TabLayout + ViewPager형식으로 이루어져 있습니다.

카카오톡UI

카카오톡의 UI입니다. 위와같은 구조를 이루고 있죠. 카카오톡을 사용하다보면 위의 메뉴를 눌러서 페이지를 전환할수도 있지만 손가락으로 쓸어서 페이지를 넘길수도 있죠. ViewPager를 사용하면 손가락으로 쓸어서 페이지를 넘기는 기능을 사용할 수 있게됩니다. 사용해보면 알겠지만 굉장히 편리한 기능입니다.


TabLayout과 ViewPager를 활용하여 효율적인 UI 구성하기ViewPager

원리는 이렇습니다. ViewPager를 제외한 나머지 Object는 고정이며 ViewPager의 내용(Fragment)만 가변적입니다. 1번메뉴, 2번메뉴, 3번메뉴 이동시 ViewPager에서 연동시켜놓은 Fragment페이지를 띄워 보여주는 방식입니다.


MainActivity

package com.example.user.tabbedactivity;

import android.content.Intent;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.TabLayout;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;

public class MainActivity extends AppCompatActivity {

    private ViewPager mViewPager;

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

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
      
        mViewPager = (ViewPager) findViewById(R.id.container);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                startActivityForResult(intent,1);
                
            }
        });
        TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
        tabLayout.addTab(tabLayout.newTab().setText("첫번째"));
        tabLayout.addTab(tabLayout.newTab().setText("두번째"));
        tabLayout.addTab(tabLayout.newTab().setText("세번째"));
        tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);

        final ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
        final PagerAdapter adapter = new PagerAdapter
                (getSupportFragmentManager(), tabLayout.getTabCount());
        viewPager.setAdapter(adapter);
        viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
        tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                viewPager.setCurrentItem(tab.getPosition());
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {

            }

            @Override
            public void onTabReselected(TabLayout.Tab tab) {

            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

TabLayout, ViewPager 속성 및 이벤트를 설정해줄 수 있는 MainActvity.Class입니다.


activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.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:id="@+id/main_content"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context="com.example.user.tabbedactivity.MainActivity">
    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:background="?attr/colorPrimary"
        android:elevation="6dp"
        android:minHeight="?attr/actionBarSize"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>

    <android.support.design.widget.TabLayout
        android:id="@+id/tab_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/toolbar"
        android:background="?attr/colorPrimary"
        android:elevation="6dp"
        android:minHeight="?attr/actionBarSize"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"/>

    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="fill_parent"
        android:layout_below="@id/tab_layout"/>

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingTop="@dimen/appbar_padding_top"
        android:theme="@style/AppTheme.AppBarOverlay">

    <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar1"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|enterAlways"
            app:popupTheme="@style/AppTheme.PopupOverlay">

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

    </android.support.design.widget.AppBarLayout>

    <android.support.v4.view.ViewPager
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="end|bottom"
        android:layout_margin="@dimen/fab_margin"
        app:srcCompat="@android:drawable/ic_menu_camera" />

</android.support.design.widget.CoordinatorLayout>

메인화면 Xml입니다.


PagerAdapter

package com.example.user.tabbedactivity;

/**
 * Created by user on 2016-12-26.
 */

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;

public class PagerAdapter extends FragmentStatePagerAdapter {
    int mNumOfTabs;

    public PagerAdapter(FragmentManager fm, int NumOfTabs) {
        super(fm);
        this.mNumOfTabs = NumOfTabs;
    }

    @Override
    public Fragment getItem(int position) {

        switch (position) {
            case 0:
                TabFragment1 tab1 = new TabFragment1();
                return tab1;
            case 1:
                TabFragment2 tab2 = new TabFragment2();
                return tab2;
            case 2:
                TabFragment3 tab3 = new TabFragment3();
                return tab3;
            default:
                return null;
        }
    }

    @Override
    public int getCount() {
        return mNumOfTabs;
    }
}

ViewPager의 경로 및 속성을 설정해주는 PagerAdapter.class입니다.


TabFragment1

package com.example.user.tabbedactivity;

/**
 * Created by user on 2016-12-26.
 */

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class TabFragment1 extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.tab_fragment_1, container, false);
    }
}

첫번째 메뉴 Fragment입니다. 첫번째 메뉴를 구현하시려면 이곳에서 구현하시면 됩니다.


TabFragment2

package com.example.user.tabbedactivity;

/**
 * Created by user on 2016-12-26.
 */

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class TabFragment2 extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.tab_fragment_2, container, false);
    }
}

두번째 메뉴 Fragment입니다. ViewPager 두번째 메뉴와 연동되어져있는 Fragment입니다.


TabFragment3

package com.example.user.tabbedactivity;

/**
 * Created by user on 2016-12-26.
 */

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class TabFragment3 extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.tab_fragment_3, container, false);
    }
}

세번째 메뉴 Fragment입니다. 동일하게 하나 만들어주시면 되겠습니다.


tab_fragment_1.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="첫번째 페이지"
        android:textAppearance="?android:attr/textAppearanceLarge"/>

</RelativeLayout>

첫번째 메뉴화면 입니다.


tab_fragment_2.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="두번째 페이지"
        android:textAppearance="?android:attr/textAppearanceLarge"/>

</RelativeLayout>

두번째 메뉴 Xml입니다. 똑같이 하나 만들어줍시다.


tab_fragment_3.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="세번째 페이지"
        android:textAppearance="?android:attr/textAppearanceLarge"/>

</RelativeLayout>

마찬가지로 만들어주시면 됩니다.


결과


[Android] 어플리케이션 UI 만들기(사용하기 편리한 인터페이스)

[Android] 네비게이션 드로어(Navigation Drawer) 사용법


댓글(11)

  • 트래져
    2019.01.25 13:48

    MainActivity에서 제가 따로 fragment1에서 선언한 메소드에 접근하려면 어떻게 해야 하나요?

    • 2019.01.29 13:33 신고

      Fragment1 fragment1 = new Fragment(); 이렇게 하신다음에 fragment1으로 접근하시면 안되나요?

  • 신하민
    2019.03.27 15:42

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

    if (id == R.id.action_settings)

    이부분이 빨갛게 표시가 되는데 뭘 추가 해줘야 하나요?

  • KJ
    2019.08.29 23:17

    정말 정리가 잘된 글인것 같습니다.
    코딩 공부하면서 이렇게 친절하게 정리한 블로그가 없는데
    정말 블로그에 대한 애정이 크시다는게 느껴지네요. 잘 배우고 갑니다.
    앞으로 지속적으로 이 블로그를 사용할것 같네요!

  • 123
    2020.01.07 19:56

    좋은글 감사합니다.
    위에분처럼
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu_main, menu);
    return true;
    }
    에서 menu 부분이 빨간색이뜹니다 구글링해보니 위에분처럼
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu_main, menu);
    return true;
    }
    에서 menu 부분이 빨간색이뜹니다 구글링해보니 menu가 기본 폴더에있는거라고하던데 버전이 달라서그런지몰라도
    저는 menu가 없네요

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();
    if (id == R.id.action_settings) {
    return true;
    }

    return super.onOptionsItemSelected(item);
    }

    이부분에 action_setting도 빨간색이뜹니다.

    final PagerAdapter adapter = new PagerAdapter
    (getSupportFragmentManager(), tabLayout.getTabCount());

    여기서도 빨간줄이뜹니다 어떻게 해야 될까ㅏ요?

    • 2020.01.08 02:48 신고

      프로젝트 생성 -> 뷰 페이저 프로젝트로 생성하면 바로 영상과 같은 프로젝트 만들어질겁니다

    • 123
      2020.01.08 14:55

      아 그러면 empty프로젝트로는 원래안되는건가요??

    • 2020.01.09 07:03 신고

      할수있는데 소스가 조금 바뀐거같네요 뷰 페이저 프로젝트의 소스 복붙하시면 됩니다

  • 뮌스터
    2021.01.30 21:56

    안녕하세요! 좋은 자료 올려주셔서 감사히 참고하고 있습니다.
    mainActivity.xml에 viewpager가 두 개 들어가 있는데, pager는 fragment 요소들이 들어가는 걸로 이해가 됩니다.
    그런데 container는 xml코드를 보니 화면에 나타나지 않는 걸로 보이는데, pager와 다르게 어떤 기능을 하는 지 알 수 있을까요?
    제가 앱 개발 입문 단계라 설명해주시면 큰 도움이 될 것 같습니다 :)

Designed by JB FACTORY