ViewPager
by 민갤
사용자가 화면을 swipe하여 페이지를 좌우로 넘겨 볼 수 있게 해주는 안드로이드 레이아웃 매니저 클래스.
ViewPager
ViewPager를 사용하기 위해 support.v4 지원 라이브러리를 등록한다.
compile 'com.android.support:support-v4:25.1.0'
한 화면을 ViewPager로만 구성한다면 layout을 생성할 때 Root element 에 ViewPager를 검색하여 선택 후 생성한다.
<!--activity_page_view.xml-->
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.view.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v4.view.ViewPager>
그렇지 않고 화면 안에 지정 구역을 만들어주고 싶다면 android.support.v4.view.ViewPager 를 직접 태그 한다.
<RelativeLayout ... >
<android.support.v4.view.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"/>
...
FragmentActivity를 상속받는 Activity를 생성한다.
(짧은 경험담이지만, FragmentActivity를 상속받을 경우 ImageButton과 지원 ActionBar 사용이 자유롭지 않은 것 같다.
때문에 글쓴이는 AppCompatActivity를 상속받아 사용했다.)
여기서의 import는 support.v4 로 통일한다.
import android.support.v4.app.FragmentActivity;
public class PagerActivity extends FragmentActivity {
ViewPager가 Fragment를 사용하려면 반드시 Adapter가 필요하다.
pagerAdapter의 서브 클래스인 FragmentStatePagerAdapter를 ViewPager에 설정한다.
이 클래스는 FragmentManager를 인스턴스로 얻는다.
public class PagerActivity extends FragmentActivity {
...
private final int PAGE_MAX_COUNT = 2;
private final int PAGE_ONE = 0, PAGE_TOW = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_page_view);
ViewPager viewPager = (ViewPager) findViewById(R.id.fragment_container);
FragmentManager fragmentManager = getSupportFragmentManager();
viewPager.setAdapter(new FragmentStatePagerAdapter(fragmentManager) {
@Override
public int getCount() {
return PAGE_MAX_COUNT;
}
@Override
public Fragment getItem(int position) {
switch (position) {
case PAGE_ONE:
return new OneFragment().newInstance();
case PAGE_TOW:
return new TowFragment().newInstance();
default: return null;
}
}
});
FragmentStatePagerAdapter는 getItem(int)으로 Fragment를 반환하여
FragmentManager로 순서(position)에 맞게 Activity에 추가해주며, ViewPager가 Fragment를 구별하는 것을 도와준다.
getItem(int)은 화면에 보여줄 Fragment를 반환한다.
getCount(int)는 생성 가능한 Fragment의 개수를 반환한다. 즉 총 몇 개의 화면을 구성해줄 지를 지정한다.
로딩 화면 수 정하기
ViewPager는 현재 화면을 만들 때
현재 페이지에서 다음 페이지에 대한 반응을 바로 할 수 있도록 좌우의 페이지들도 같이 만든다.
viewPager.setOffscreenPageLimit(int);
위 코드로 몇 개의 페이지를 미리 만들어둘 지 변경할 수 있다.
최소 1부터 입력해야 한다. ex) 1: 좌우 1개씩 생성, 2: 좌우 2개씩 생성
페이지 하나만 생성하기
현 페이지만 로딩되게 하고 싶다면 setUserVisibleHint(boolean) 메소드를 이용한다.
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
// 사용자 화면에 페이지가 보일 때
} else {
// 사용자 화면에 보이지 않을 때
}
}
ViewPager 내부가 아닌 각 Fragment 내부에서 Override 하여 사용한다.
if(true) 안에는 오류가 발생하지 않는 범위 내에서 코드를 짜야 한다.
주의점!
어떤 동작으로 페이지를 넘기느냐에 따라 호출 순서가 달라진다.
Swipe: (보이기 전)isVisibleToUser(false) - onCreate - onCreateView - onResume - (보이면)isVisibleToUser(true)
Button : (이동)isVisibleToUser(false) - isVisibleToUser(true) - onCreate - onCreateView - onResume
때문에 단순히 저 메소드만 사용할 경우 오류가 발생한다.
이를 방지하고자 필자는 flag 하나를 같이 사용했다.
public class TowFragment extends Fragment {
private boolean userHint = false;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.tow_fragment, container, false);
if (userHint) {
// up
} else {
userHint = true;
}
return v;
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
if (userHint) {
// up
} else {
userHint = true;
}
} else {
}
}
}
페이지 보기
ViewPager는 언제나 첫 번째 position에 있는 Fragment를 가장 먼저 보여주도록 되어있다.
만일 사용자의 선택 상태에 따라 다른 position의 Fragment를 보여주고 싶다면 다음 코드를 입력한다.
viewPager.setCurrentItem(yourSelectPosition);
페이지 바꾸기
ViewPager의 페이지가 바뀔 때마다 Activity에 변화를 주고 싶을 때는 addOnPageChangeListener 메소드를 호출한다.
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
switch (position) {
case PAGE_ONE:
break;
case PAGE_TOW:
break;
}
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
이 메소드는 사용자가 swipe를 하거나 버튼으로 페이지를 이동하거나 상관없이 페이지가 바뀌면 화면에 나타나는 페이지의 상태를 매개 변수로 받는 다.
SCROLL_STATE_DRAGGING: 1. 사용자에 의해 페이지가 swipe 되고 있는 상태.
SCROLL_STATE_IDLE: 0. 페이지가 완전히 보이고 애니메이션이 진행되지 않는 상태.
SCROLL_STATE_SETTLING: 2. 화면에 보일 페이지가 정해진 상태. 페이지가 변경됨.
onPageSelected는 페이지가 변경될 때 호출된다. 각 페이지(position)에 따라 Activity를 변경할 수 있다.
onPageScrolled은 사용자가 페이지를 넘길 때마다 호출된다.
페이지 갱신
어떤 동작을 한 후 페이지를 갱신해야 한다면 PagerAdapter의 getItemPosition(Object)을 사용한다.
flag를 하나 만들어서 변경 시점을 지정할 수 있도록 만든다.
private boolean flagChange = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
pagerAdapter = new FragmentStatePagerAdapter(fragmentManager) {
...
@Override
public int getItemPosition(Object object) {
if (flagChange)
return POSITION_NONE;
else return POSITION_UNCHANGED;
}
};
}
POSITION_NONE: ViewPager.destroyItem()을 호출하여 Item이 삭제된 것으로 판단해 Fragment.onCreateView를 호출한다.
변경될 시점에 다음 코드를 입력한다.
flagChange = true;
pagerAdapter.notifyDataSetChanged();