[Android] Fragment with ViewPager Example

오늘은 며칠동안 틈틈이 준비한 ViewPager와 Fragment를 이용한 화면전환 예제를 만들어보려 합니다.
며칠동안이라 했지만 거창하게 만들 시간은 없어서 정말 간단히 하려고 노력했습니다.

ViewPager

뷰페이저란 사용자가 Swipe나 Tab 등을 통해서 현재 보는 화면 다음 화면을 Smooth하게 시각적으로 보여주도록 도와줍니다.
주로 Application을 설치하고 튜토리얼을 진행하는 과정에서 좌우로 휙휙 넘기면서 설명하는 곳에서 많이 사용합니다.

화면구성

우리가 만들 화면은 아래와같이 3 페이지입니다.

각 페이지를 색깔로 구분하였고, 각각의 Fragment를 따로 작성하였습니다.

그리고 화면 전환시 아래와 같이 지금 화면이 작아지고 다음에 올 화면이 오른쪽 또는 왼쪽에 보이는 애니메이션을 넣을겁니다.

MainActivity

MainActivity.java

MainActivity에서는 FragmentPagerAdapter를 이용해 Fragment들간의 페이지 설정과 애니메이션을 지정할겁니다.

MainActivity.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package com.example.sawd.viewpagerwithfragment;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {
int MAX_PAGE = 3;

Fragment currentFragment = new Fragment();

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

ViewPager viewPager = findViewById(R.id.view_pager);
viewPager.setPageTransformer(true, new ZoomOutPageTransformer());
viewPager.setAdapter(new SawdFragmentAdatper(getSupportFragmentManager()));

}

class SawdFragmentAdatper extends FragmentPagerAdapter {

private SawdFragmentAdatper(FragmentManager fm) {
super(fm);
}

@Override
public Fragment getItem(int position) {
if (position < 0 || MAX_PAGE <= position)
return null;

switch (position) {
case 0:
currentFragment = new SawdFragment1();
break;
case 1:
currentFragment = new SawdFragment2();
break;
case 2:
currentFragment = new SawdFragment3();
break;
}

return currentFragment;
}

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

코드블록 단위로 쪼개서 분석해보겠습니다.

1
viewPager.setPageTransformer(true, new ZoomOutPageTransformer());

위의 코드를 이용해 페이지 이동시 애니메이션을 지정할 수 있습니다.
ZoomOutPageTransformer 클래스 코드는 Android Developer사이트에서 가져왔고 뒤에 코드를 서술하겠습니다.

1
viewPager.setAdapter(new SawdFragmentAdatper(getSupportFragmentManager()));

ViewPager에 Adapter를 설정하고 Fragment를 관리할 수 있도록 해당 액티비티의 SupportFragmentManager를 인자로 넘겨줍니다.
우리는 Adapter를 커스터마이징하기 위해 FragmentPagerAdapter를 상속받은 SawdFragmentAdapter를 만들겁니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.example.sawd.viewpagerwithfragment;

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

class SawdFragmentAdatper extends FragmentPagerAdapter {
private SawdFragmentAdatper(FragmentManager fm) {
super(fm);
}

@Override
public Fragment getItem(int position) {
if (position < 0 || MAX_PAGE <= position)
return null;

switch (position) {
case 0:
currentFragment = new SawdFragment1();
break;
case 1:
currentFragment = new SawdFragment2();
break;
case 2:
currentFragment = new SawdFragment3();
break;
}

return currentFragment;
}

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

필수로 getItem 메소드와 getCount 메소드를 오버라이딩 해야합니다.
코드로 간단히 알 수 있듯 getItem에서는 각 페이지별로 보여줄 Fragment를, getCount는 페이지수를 리턴합니다.
이부분에서 정말 다양하게 코드들을 작성 할 수 있습니다.

activity_main.xml

레이아웃은 ConstraintLayout안에 ViewPager만 꽉채워서 넣었습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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=".MainActivity">

<android.support.v4.view.ViewPager
android:id="@+id/view_pager"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

SawdFragment

SawdFragment1~3.java

Fragment를 상속받은 SawdFragment 클래스를 3개 만들었습니다.
내용은 완전히 같고 inflate에 들어가는 layout만 다르기 때문에 1을 기준으로 설명할게요.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.example.sawd.viewpagerwithfragment;

import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.constraint.ConstraintLayout;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class SawdFragment1 extends Fragment {
public SawdFragment1() {
}

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

fragment_sawd1.xml의 내용을 inflate하여 View로 만듭니다.
이 View가 MainActivity의 ViewPager에 적용된다고 생각하면 편합니다.

만약 Fragment안에 있는 뷰를 찾기 위해 findViewById() 메소드를 써야한다면 onCreate()에서 아래와 같이 접근할 수 있습니다.

1
2
3
View parentView = inflater.inflate(R.layout.fragment_sawd1, container, false);
View childView = parentView.findViewById(R.id.child_view);
// Something to do about childView

fragment_sawd1~3.xml

1
2
3
4
5
6
7
8
<?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"
android:id="@+id/constraint_layout"
android:background="#27ab27">

</android.support.constraint.ConstraintLayout>

background를 설정해 각 페이지를 색깔로 구분할 수 있게 간단히 구현하였습니다.

ZoomOutPageTransformer.java

ZoomOutPageTransformer.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package com.example.sawd.viewpagerwithfragment;

import android.support.annotation.NonNull;
import android.support.v4.view.ViewPager;
import android.view.View;

public class ZoomOutPageTransformer implements ViewPager.PageTransformer {
private static final float MIN_SCALE = 0.85f;
private static final float MIN_ALPHA = 0.5f;

public void transformPage(@NonNull View view, float position) {
int pageWidth = view.getWidth();
int pageHeight = view.getHeight();

if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.setAlpha(0);

} else if (position <= 1) { // [-1,1]
// Modify the default slide transition to shrink the page as well
float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position));
float vertMargin = pageHeight * (1 - scaleFactor) / 2;
float horzMargin = pageWidth * (1 - scaleFactor) / 2;
if (position < 0) {
view.setTranslationX(horzMargin - vertMargin / 2);
} else {
view.setTranslationX(-horzMargin + vertMargin / 2);
}

// Scale the page down (between MIN_SCALE and 1)
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);

// Fade the page relative to its size.
view.setAlpha(MIN_ALPHA +
(scaleFactor - MIN_SCALE) /
(1 - MIN_SCALE) * (1 - MIN_ALPHA));

} else { // (1,+Infinity]
// This page is way off-screen to the right.
view.setAlpha(0);
}
}
}

ZoomOutPageTransformer는 page수와 position에 따라서 화면 전환시에 애니메이션을 정의하고 있습니다.
Android Developers에서 DepthPageTransformer도 함께 예제로 제공하고있는데 시간날때 보시면 좋을 것 같습니다.

Depth page transformer

마무리

최근에 공부하고 배우는것을 토대로 포스팅을 하지만 주 업무에 너무 치이고 있어서 며칠에 걸쳐서 포스팅까지 완료하였네요.
빠르진 않더라도 앞으로도 이렇게 차근차근 포스팅 할 예정입니다.
전체 소스를 원하시는 분이 있을지 모르니 링크 드립니다.

ViewPager-With-Fragment