[Android] Page Indicator with Fragment

오랜만에 또 포스팅 하는군요…
오늘은 GitHub의 라이브러리를 이용해 아래와 같은 것을 만들어볼까 합니다.

기반 라이브러리는 PageIndicator이고 원본 주소는 아래와 같습니다.
https://github.com/romandanylyk/PageIndicatorView

이 라이브러리를 사용하다가 샘플자체가 View로 되어있어서 이해하고 사용하기가 어려웠습니다.
마침 이전 강좌들에서 사용했던 Fragment를 이용해서 예제를 바꾸기로 했습니다.
라이브러리는 그대로 두고 샘플쪽 코드만 수정할겁니다.

Page Indicator

Page Indicator는 위에서 보셨듯 화면 하단에 현재 페이지가 몇번째 페이지인지 알려주는 것을 말합니다.
iOS에서는 PageViewController라고도 부릅니다.
이 UI를 사용하면 튜토리얼이나 단계가 있는 플로우를 작성하는데 꽤 그럴듯한 UX를 제공할 수 있습니다.

아주 잘 만들어진 라이브러리에 제가 사용하고 싶은 Fragment를 연동하려 합니다.

HomeActivity.java

단순하게 MainActivity역할을 하는 클래스입니다.
전문은 아래와 같습니다.

HomeActivity.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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
package com.rd.pageindicatorview.home;

import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.support.v4.view.ViewPager;
import android.view.Menu;
import android.view.MenuItem;

import com.rd.PageIndicatorView;
import com.rd.pageindicatorview.base.BaseActivity;
import com.rd.pageindicatorview.base.BaseFragment;
import com.rd.pageindicatorview.customize.CustomizeActivity;
import com.rd.pageindicatorview.data.Customization;
import com.rd.pageindicatorview.sample.R;

import java.util.ArrayList;
import java.util.List;


public class HomeActivity extends BaseActivity {

private PageIndicatorView pageIndicatorView;
private Customization customization;
private ViewPager viewPager;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.ac_home);
customization = new Customization();

initToolbar();
initViews();
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
boolean customization = requestCode == CustomizeActivity.EXTRAS_CUSTOMIZATION_REQUEST_CODE && resultCode == RESULT_OK;
if (customization && intent != null) {
this.customization = intent.getParcelableExtra(CustomizeActivity.EXTRAS_CUSTOMIZATION);
updateIndicator();
}
}

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

@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.actionCustomize:
CustomizeActivity.start(this, customization);
return true;

default:
return super.onOptionsItemSelected(item);
}
}

@SuppressWarnings("ConstantConditions")
private void initViews() {
viewPager = findViewById(R.id.viewPager);
HomeFragmentAdapter homeFragmentAdapter = new HomeFragmentAdapter(getSupportFragmentManager());
homeFragmentAdapter.setData(createPageList());
viewPager.setAdapter(homeFragmentAdapter);

pageIndicatorView = findViewById(R.id.pageIndicatorView);
}

@NonNull
private List<Fragment> createPageList() {
int[] colors = {R.color.google_red, R.color.google_yellow, R.color.google_green, R.color.google_blue};
List<Fragment> pageList = new ArrayList<>();
for(int i = 0; i < 4; i++) {
BaseFragment baseFragment = new BaseFragment();
Bundle bundle = new Bundle();
bundle.putInt(BaseFragment.BACKGROUND_COLOR, colors[i]);
baseFragment.setArguments(bundle);
pageList.add(baseFragment);
}

return pageList;
}

private void updateIndicator() {
if (customization == null) {
return;
}

pageIndicatorView.setAnimationType(customization.getAnimationType());
pageIndicatorView.setOrientation(customization.getOrientation());
pageIndicatorView.setRtlMode(customization.getRtlMode());
pageIndicatorView.setInteractiveAnimation(customization.isInteractiveAnimation());
pageIndicatorView.setAutoVisibility(customization.isAutoVisibility());
}
}

코드 전체적으로 라이브러리 사용방법과 설정을 통해 애니메이션, 세로모드 등을 바로바로 적용할 수 있도록 도와주는 코드입니다.
여기서 제가 수정한 곳에 포커스를 맞추려 합니다.

집중할 부분

1
2
3
4
5
6
7
8
private void initViews() {
viewPager = findViewById(R.id.viewPager);
HomeFragmentAdapter homeFragmentAdapter = new HomeFragmentAdapter(getSupportFragmentManager());
homeFragmentAdapter.setData(createPageList());
viewPager.setAdapter(homeFragmentAdapter);

pageIndicatorView = findViewById(R.id.pageIndicatorView);
}

뷰를 초기화하는 부분입니다.
Fragment용 Adapter인 HomeFragmentAdapter를 만들었고 getSupportFragmentManager를 이용해 생성하였습니다.
그 Fragment Adapter에 데이터를 지정하고 뷰 페이저에 setAdpater로 연동하였습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@NonNull
private List<Fragment> createPageList() {
int[] colors = {R.color.google_red, R.color.google_yellow, R.color.google_green, R.color.google_blue};
List<Fragment> pageList = new ArrayList<>();
for(int i = 0; i < 4; i++) {
BaseFragment baseFragment = new BaseFragment();
Bundle bundle = new Bundle();
bundle.putInt(BaseFragment.BACKGROUND_COLOR, colors[i]);
baseFragment.setArguments(bundle);
pageList.add(baseFragment);
}

return pageList;
}

원래는 레이아웃 xml 이 없고 View를 새로 생성해서 넣는 코드였습니다.
하지만 실제 사용시에는 xml을 이용한 레이아웃 작업과 더 나아가 Fragment를 적용하는데 더 좋을 것 같아서 공부한 것을 토대로 수정하였습니다.

각 페이지는 알아보기 쉽게 기존 샘플에서와 같이 색상으로 페이지를 구분할 수 있도록 하였습니다.
Bundle을 이용해 Fragment에 색상 값들을 전달 할 수 있습니다.
밑에서 보겠지만 Fragment에서는 이 Bundle을 통해 들어온 색상값들을 자기 배경색으로 지정할 겁니다.

HomeFragmentAdpater.java

HomeFragmentAdapter.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
package com.rd.pageindicatorview.home;

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

import java.util.List;

public class HomeFragmentAdapter extends FragmentPagerAdapter {
private List<Fragment> fragmentList;

public HomeFragmentAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
}

public void setData(List<Fragment> fragmentList) {
this.fragmentList = fragmentList;
}

@Override
public Fragment getItem(int position) {
return fragmentList.get(position);
}

@Override
public int getCount() {
return fragmentList.size();
}
}

제가 Fragment를 ViewPager와 연동하기 위해 만든 Fragment Adapter입니다.
FragmentPagerAdpater를 상속받아서 생성하였고 위에서 설명한 setData를 정의하였습니다.
getItem, getCount너무 간단해서 설명하지 않도록 하겠습니다.

BaseFragment.java

BaseFragment.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
package com.rd.pageindicatorview.base;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;

import com.rd.pageindicatorview.sample.R;

public class BaseFragment extends Fragment {
public static final String BACKGROUND_COLOR = "param1";
private int backgroundColor;

public BaseFragment() {
// Required empty public constructor
}

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
backgroundColor = getArguments().getInt(BACKGROUND_COLOR);
}
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_base, container, false);
LinearLayout linearLayout = view.findViewById(R.id.linear_layout);
linearLayout.setBackgroundColor(ContextCompat.getColor(getContext(), backgroundColor));
return view;
}

}

BaseFragment라고 이름을 짓고 Fragment 를 상속받았습니다.
그리고 HomeActivity에서 인자로 사용할 static 변수인 BACKGROUND_COLOR도 지정했습니다.
onCreate에서 arguments가 있는지 확인하고 멤버 변수에 색상값을 넣어주었습니다.
onCreateView에서 배경이 되는 LinearLayout에 색상값을 이용해 배경색으로 지정하였습니다.

fragment_base.xml

fragment_base.xml
1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/linear_layout"
android:orientation="vertical">

</LinearLayout>

LinearLayout의 width와 height를 match_parent로 지정하였습니다.
이 영역에 색을 칠해서 배경으로 사용합니다.

마무리

라이브러리 사용자 관점에서는 굉장히 사용하고 싶은데 샘플이 너무 불친절했습니다.
xml없이 뷰를 하나 생성하고 색을 칠해서 Page Indicator에 연동해서 사용자에게 보여주는 형태였습니다.
사실 Page Indicator에는 Fragment가 더 좋을것 같다는 생각에 프로젝트를 Fork해서 수정했습니다.

제가 수정한 전체 코드는 아래에서 다운받을 수 있습니다.
https://github.com/dev-sawd/PageIndicatorViewWithFragment

이 강좌는 한번 더 확장할 에정입니다.
실제로 사용할때 ViewPager의 특성에 의해서 고려해야될 부분이 만만치 않기 때문입니다.
그럼 또 시간날때 포스팅 하겠습니다!